summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules4
-rw-r--r--.gitreview2
-rw-r--r--.mailmap3
-rw-r--r--.zuul.yaml3
-rw-r--r--BUILD6
-rw-r--r--Documentation/access-control.txt32
-rw-r--r--Documentation/backup.txt26
-rw-r--r--Documentation/cmd-ls-projects.txt6
-rw-r--r--Documentation/cmd-plugin-remove.txt1
-rw-r--r--Documentation/config-accounts.txt12
-rw-r--r--Documentation/config-gerrit.txt257
-rw-r--r--Documentation/config-groups.txt8
-rw-r--r--Documentation/config-labels.txt33
-rw-r--r--Documentation/config-robot-comments.txt1
-rw-r--r--Documentation/config-validation.txt9
-rw-r--r--Documentation/dev-bazel.txt104
-rw-r--r--Documentation/dev-build-plugins.txt6
-rw-r--r--Documentation/dev-cla.txt26
-rw-r--r--Documentation/dev-community.txt70
-rw-r--r--Documentation/dev-contributing.txt711
-rw-r--r--Documentation/dev-crafting-changes.txt297
-rw-r--r--Documentation/dev-design-doc-conclusion-template.md18
-rw-r--r--Documentation/dev-design-doc-index-template.md18
-rw-r--r--Documentation/dev-design-doc-solution-template.md72
-rw-r--r--Documentation/dev-design-doc-use-cases-template.md48
-rw-r--r--Documentation/dev-design-docs.txt153
-rw-r--r--Documentation/dev-design.txt38
-rw-r--r--Documentation/dev-eclipse.txt7
-rw-r--r--Documentation/dev-inspector.txt2
-rw-r--r--Documentation/dev-intellij.txt6
-rw-r--r--Documentation/dev-plugins-lifecycle.txt254
-rw-r--r--Documentation/dev-plugins.txt133
-rw-r--r--Documentation/dev-processes.txt355
-rw-r--r--Documentation/dev-readme.txt147
-rw-r--r--Documentation/dev-release-deploy-config.txt2
-rw-r--r--Documentation/dev-release-jgit.txt52
-rw-r--r--Documentation/dev-release.txt4
-rw-r--r--Documentation/dev-roles.txt378
-rw-r--r--Documentation/dev-starter-projects.txt14
-rw-r--r--Documentation/error-change-does-not-belong-to-project.txt18
-rw-r--r--Documentation/error-change-not-found.txt17
-rw-r--r--Documentation/error-messages.txt3
-rw-r--r--Documentation/error-push-refschanges-not-allowed.txt13
-rw-r--r--Documentation/i18n-readme.txt24
-rw-r--r--Documentation/index.txt30
-rw-r--r--Documentation/install.txt2
-rw-r--r--Documentation/intro-gerrit-walkthrough.txt2
-rw-r--r--Documentation/intro-user.txt30
-rw-r--r--Documentation/js-api.txt925
-rw-r--r--Documentation/js_licenses.txt43
-rw-r--r--Documentation/licenses.txt72
-rw-r--r--Documentation/linux-quickstart.txt11
-rw-r--r--Documentation/metrics.txt15
-rw-r--r--Documentation/pg-plugin-admin-api.txt8
-rw-r--r--Documentation/pg-plugin-dev.txt11
-rw-r--r--Documentation/pg-plugin-endpoints.txt5
-rw-r--r--Documentation/pg-plugin-style-object.txt33
-rw-r--r--Documentation/pg-plugin-styles-api.txt29
-rw-r--r--Documentation/pg-plugin-styling.txt14
-rw-r--r--Documentation/pgm-daemon.txt16
-rw-r--r--Documentation/prolog-cookbook.txt3
-rw-r--r--Documentation/rest-api-accounts.txt49
-rw-r--r--Documentation/rest-api-changes.txt97
-rw-r--r--Documentation/rest-api-config.txt12
-rw-r--r--Documentation/rest-api-groups.txt6
-rw-r--r--Documentation/rest-api-plugins.txt18
-rw-r--r--Documentation/rest-api-projects.txt67
-rw-r--r--Documentation/user-inline-edit.txt2
-rw-r--r--Documentation/user-request-tracing.txt92
-rw-r--r--Documentation/user-search.txt12
-rw-r--r--Documentation/user-upload.txt70
-rw-r--r--WORKSPACE156
-rwxr-xr-xcontrib/abandon_stale.py225
-rw-r--r--contrib/benchmark-createchange.go103
-rwxr-xr-xcontrib/hooks/post-receive-move-tmp-refs78
-rwxr-xr-xcontrib/refresh_plugin_in_testsite.sh63
-rwxr-xr-xcontrib/show_new_gerrit_doc_in_chrome.sh48
-rwxr-xr-xcontrib/start_testsite.sh63
-rw-r--r--java/com/google/gerrit/acceptance/AbstractDaemonTest.java380
-rw-r--r--java/com/google/gerrit/acceptance/AbstractNotificationTest.java28
-rw-r--r--java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java2
-rw-r--r--java/com/google/gerrit/acceptance/AccountCreator.java8
-rw-r--r--java/com/google/gerrit/acceptance/AccountIndexedCounter.java58
-rw-r--r--java/com/google/gerrit/acceptance/BUILD198
-rw-r--r--java/com/google/gerrit/acceptance/ChangeIndexedCounter.java5
-rw-r--r--java/com/google/gerrit/acceptance/DisabledAccountIndex.java2
-rw-r--r--java/com/google/gerrit/acceptance/DisabledChangeIndex.java2
-rw-r--r--java/com/google/gerrit/acceptance/DisabledProjectIndex.java2
-rw-r--r--java/com/google/gerrit/acceptance/ExtensionRegistry.java253
-rw-r--r--java/com/google/gerrit/acceptance/GcAssert.java2
-rw-r--r--java/com/google/gerrit/acceptance/GerritServer.java75
-rw-r--r--java/com/google/gerrit/acceptance/GitClientVersion.java66
-rw-r--r--java/com/google/gerrit/acceptance/GitUtil.java35
-rw-r--r--java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java2
-rw-r--r--java/com/google/gerrit/acceptance/InProcessProtocol.java28
-rw-r--r--java/com/google/gerrit/acceptance/ProjectResetter.java10
-rw-r--r--java/com/google/gerrit/acceptance/PushOneCommit.java13
-rw-r--r--java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java2
-rw-r--r--java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java5
-rw-r--r--java/com/google/gerrit/acceptance/RestResponse.java34
-rw-r--r--java/com/google/gerrit/acceptance/SshdModule.java2
-rw-r--r--java/com/google/gerrit/acceptance/StandaloneSiteTest.java42
-rw-r--r--java/com/google/gerrit/acceptance/TestAccount.java2
-rw-r--r--java/com/google/gerrit/acceptance/UseClockStep.java42
-rw-r--r--java/com/google/gerrit/acceptance/UseSystemTime.java35
-rw-r--r--java/com/google/gerrit/acceptance/UseTimezone.java35
-rw-r--r--java/com/google/gerrit/acceptance/rest/CreateTestPlugin.java3
-rw-r--r--java/com/google/gerrit/acceptance/rest/GetTestPlugin.java2
-rw-r--r--java/com/google/gerrit/acceptance/rest/ListTestPlugin.java5
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java2
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java20
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/TestAccount.java2
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java2
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java2
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java2
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java8
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java4
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java4
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java4
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/project/BUILD22
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java35
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java180
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java2
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java436
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.java2
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java2
-rw-r--r--java/com/google/gerrit/common/BUILD5
-rw-r--r--java/com/google/gerrit/common/FileUtil.java1
-rw-r--r--java/com/google/gerrit/common/PageLinks.java14
-rw-r--r--java/com/google/gerrit/common/UsedAt.java9
-rw-r--r--java/com/google/gerrit/common/data/AccessSection.java2
-rw-r--r--java/com/google/gerrit/common/data/CommentDetail.java8
-rw-r--r--java/com/google/gerrit/common/data/ContributorAgreement.java2
-rw-r--r--java/com/google/gerrit/common/data/FilenameComparator.java2
-rw-r--r--java/com/google/gerrit/common/data/GarbageCollectionResult.java2
-rw-r--r--java/com/google/gerrit/common/data/GroupDescription.java4
-rw-r--r--java/com/google/gerrit/common/data/GroupReference.java22
-rw-r--r--java/com/google/gerrit/common/data/LabelFunction.java8
-rw-r--r--java/com/google/gerrit/common/data/LabelType.java25
-rw-r--r--java/com/google/gerrit/common/data/LabelTypes.java2
-rw-r--r--java/com/google/gerrit/common/data/PatchScript.java13
-rw-r--r--java/com/google/gerrit/common/data/PermissionRange.java4
-rw-r--r--java/com/google/gerrit/common/data/SubmitRecord.java2
-rw-r--r--java/com/google/gerrit/common/data/SubscribeSection.java10
-rw-r--r--java/com/google/gerrit/common/data/testing/BUILD2
-rw-r--r--java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java15
-rw-r--r--java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java2
-rw-r--r--java/com/google/gerrit/elasticsearch/BUILD4
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java30
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java29
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java5
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java5
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java2
-rw-r--r--java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java6
-rw-r--r--java/com/google/gerrit/entities/Account.java (renamed from java/com/google/gerrit/reviewdb/client/Account.java)206
-rw-r--r--java/com/google/gerrit/entities/AccountGroup.java (renamed from java/com/google/gerrit/reviewdb/client/AccountGroup.java)99
-rw-r--r--java/com/google/gerrit/entities/AccountGroupByIdAudit.java66
-rw-r--r--java/com/google/gerrit/entities/AccountGroupMemberAudit.java74
-rw-r--r--java/com/google/gerrit/entities/BUILD (renamed from java/com/google/gerrit/reviewdb/BUILD)7
-rw-r--r--java/com/google/gerrit/entities/BooleanProjectConfig.java (renamed from java/com/google/gerrit/reviewdb/client/BooleanProjectConfig.java)2
-rw-r--r--java/com/google/gerrit/entities/BranchNameKey.java49
-rw-r--r--java/com/google/gerrit/entities/Change.java (renamed from java/com/google/gerrit/reviewdb/client/Change.java)187
-rw-r--r--java/com/google/gerrit/entities/ChangeMessage.java (renamed from java/com/google/gerrit/reviewdb/client/ChangeMessage.java)47
-rw-r--r--java/com/google/gerrit/entities/CodedEnum.java (renamed from java/com/google/gerrit/reviewdb/client/CodedEnum.java)2
-rw-r--r--java/com/google/gerrit/entities/Comment.java (renamed from java/com/google/gerrit/reviewdb/client/Comment.java)206
-rw-r--r--java/com/google/gerrit/entities/CommentRange.java (renamed from java/com/google/gerrit/reviewdb/client/CommentRange.java)2
-rw-r--r--java/com/google/gerrit/entities/CoreDownloadSchemes.java (renamed from java/com/google/gerrit/reviewdb/client/CoreDownloadSchemes.java)2
-rw-r--r--java/com/google/gerrit/entities/FixReplacement.java (renamed from java/com/google/gerrit/reviewdb/client/FixReplacement.java)2
-rw-r--r--java/com/google/gerrit/entities/FixSuggestion.java (renamed from java/com/google/gerrit/reviewdb/client/FixSuggestion.java)2
-rw-r--r--java/com/google/gerrit/entities/KeyUtil.java (renamed from java/com/google/gwtorm/client/StandardKeyEncoder.java)13
-rw-r--r--java/com/google/gerrit/entities/LabelId.java (renamed from java/com/google/gerrit/reviewdb/client/LabelId.java)29
-rw-r--r--java/com/google/gerrit/entities/Patch.java (renamed from java/com/google/gerrit/reviewdb/client/Patch.java)72
-rw-r--r--java/com/google/gerrit/entities/PatchSet.java233
-rw-r--r--java/com/google/gerrit/entities/PatchSetApproval.java139
-rw-r--r--java/com/google/gerrit/entities/PatchSetInfo.java (renamed from java/com/google/gerrit/reviewdb/client/PatchSetInfo.java)26
-rw-r--r--java/com/google/gerrit/entities/Project.java (renamed from java/com/google/gerrit/reviewdb/client/Project.java)60
-rw-r--r--java/com/google/gerrit/entities/RefNames.java (renamed from java/com/google/gerrit/reviewdb/client/RefNames.java)12
-rw-r--r--java/com/google/gerrit/entities/RobotComment.java53
-rw-r--r--java/com/google/gerrit/entities/SubmoduleSubscription.java80
-rw-r--r--java/com/google/gerrit/entities/UserIdentity.java (renamed from java/com/google/gerrit/reviewdb/client/UserIdentity.java)2
-rw-r--r--java/com/google/gerrit/entities/converter/AccountIdProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/AccountIdProtoConverter.java)8
-rw-r--r--java/com/google/gerrit/entities/converter/BranchNameKeyProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverter.java)22
-rw-r--r--java/com/google/gerrit/entities/converter/ChangeIdProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverter.java)8
-rw-r--r--java/com/google/gerrit/entities/converter/ChangeKeyProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverter.java)8
-rw-r--r--java/com/google/gerrit/entities/converter/ChangeMessageKeyProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverter.java)14
-rw-r--r--java/com/google/gerrit/entities/converter/ChangeMessageProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverter.java)10
-rw-r--r--java/com/google/gerrit/entities/converter/ChangeProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/ChangeProtoConverter.java)18
-rw-r--r--java/com/google/gerrit/entities/converter/LabelIdProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/LabelIdProtoConverter.java)8
-rw-r--r--java/com/google/gerrit/entities/converter/ObjectIdProtoConverter.java52
-rw-r--r--java/com/google/gerrit/entities/converter/PatchSetApprovalKeyProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverter.java)22
-rw-r--r--java/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverter.java)45
-rw-r--r--java/com/google/gerrit/entities/converter/PatchSetIdProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverter.java)14
-rw-r--r--java/com/google/gerrit/entities/converter/PatchSetProtoConverter.java96
-rw-r--r--java/com/google/gerrit/entities/converter/ProjectNameKeyProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverter.java)8
-rw-r--r--java/com/google/gerrit/entities/converter/ProtoConverter.java (renamed from java/com/google/gerrit/reviewdb/converter/ProtoConverter.java)4
-rw-r--r--java/com/google/gerrit/exceptions/BUILD2
-rw-r--r--java/com/google/gerrit/exceptions/InvalidMergeStrategyException.java23
-rw-r--r--java/com/google/gerrit/exceptions/NoSuchGroupException.java2
-rw-r--r--java/com/google/gerrit/extensions/BUILD6
-rw-r--r--java/com/google/gerrit/extensions/api/changes/ChangeApi.java31
-rw-r--r--java/com/google/gerrit/extensions/api/changes/NotifyInfo.java25
-rw-r--r--java/com/google/gerrit/extensions/api/changes/RevertInput.java2
-rw-r--r--java/com/google/gerrit/extensions/api/changes/RevisionApi.java4
-rw-r--r--java/com/google/gerrit/extensions/api/groups/GroupInput.java1
-rw-r--r--java/com/google/gerrit/extensions/api/projects/ProjectApi.java8
-rw-r--r--java/com/google/gerrit/extensions/client/Comment.java4
-rw-r--r--java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java12
-rw-r--r--java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java35
-rw-r--r--java/com/google/gerrit/extensions/client/ListChangesOption.java8
-rw-r--r--java/com/google/gerrit/extensions/common/AccountDetailInfo.java1
-rw-r--r--java/com/google/gerrit/extensions/common/AccountInfo.java12
-rw-r--r--java/com/google/gerrit/extensions/common/AvatarInfo.java2
-rw-r--r--java/com/google/gerrit/extensions/common/ChangeConfigInfo.java1
-rw-r--r--java/com/google/gerrit/extensions/common/GroupAuditEventInfo.java21
-rw-r--r--java/com/google/gerrit/extensions/common/testing/BUILD2
-rw-r--r--java/com/google/gerrit/extensions/common/testing/CommitInfoSubject.java18
-rw-r--r--java/com/google/gerrit/extensions/common/testing/ContentEntrySubject.java13
-rw-r--r--java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java19
-rw-r--r--java/com/google/gerrit/extensions/common/testing/EditInfoSubject.java11
-rw-r--r--java/com/google/gerrit/extensions/common/testing/FileInfoSubject.java16
-rw-r--r--java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java6
-rw-r--r--java/com/google/gerrit/extensions/common/testing/FixReplacementInfoSubject.java12
-rw-r--r--java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java14
-rw-r--r--java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java25
-rw-r--r--java/com/google/gerrit/extensions/common/testing/RangeSubject.java17
-rw-r--r--java/com/google/gerrit/extensions/common/testing/RobotCommentInfoSubject.java11
-rw-r--r--java/com/google/gerrit/extensions/events/AccountActivationListener.java42
-rw-r--r--java/com/google/gerrit/extensions/events/ChangeEvent.java5
-rw-r--r--java/com/google/gerrit/extensions/events/ChangeIndexedListener.java15
-rw-r--r--java/com/google/gerrit/extensions/events/RevisionEvent.java6
-rw-r--r--java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java5
-rw-r--r--java/com/google/gerrit/extensions/restapi/Response.java139
-rw-r--r--java/com/google/gerrit/extensions/restapi/RestApiException.java8
-rw-r--r--java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java17
-rw-r--r--java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java20
-rw-r--r--java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java22
-rw-r--r--java/com/google/gerrit/extensions/restapi/RestModifyView.java22
-rw-r--r--java/com/google/gerrit/extensions/restapi/RestReadView.java21
-rw-r--r--java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java7
-rw-r--r--java/com/google/gerrit/extensions/validators/CommentForValidation.java48
-rw-r--r--java/com/google/gerrit/extensions/validators/CommentValidationFailure.java32
-rw-r--r--java/com/google/gerrit/extensions/validators/CommentValidator.java34
-rw-r--r--java/com/google/gerrit/git/BUILD5
-rw-r--r--java/com/google/gerrit/git/GitUpdateFailureException.java95
-rw-r--r--java/com/google/gerrit/git/LockFailureException.java24
-rw-r--r--java/com/google/gerrit/git/ObjectIds.java131
-rw-r--r--java/com/google/gerrit/git/RefUpdateUtil.java8
-rw-r--r--java/com/google/gerrit/git/testing/BUILD2
-rw-r--r--java/com/google/gerrit/git/testing/CommitSubject.java14
-rw-r--r--java/com/google/gerrit/git/testing/ObjectIdSubject.java12
-rw-r--r--java/com/google/gerrit/git/testing/PushResultSubject.java92
-rw-r--r--java/com/google/gerrit/gpg/BUILD8
-rw-r--r--java/com/google/gerrit/gpg/GerritPublicKeyChecker.java2
-rw-r--r--java/com/google/gerrit/gpg/PublicKeyStore.java10
-rw-r--r--java/com/google/gerrit/gpg/SignedPushModule.java4
-rw-r--r--java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java10
-rw-r--r--java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java10
-rw-r--r--java/com/google/gerrit/gpg/server/DeleteGpgKey.java2
-rw-r--r--java/com/google/gerrit/gpg/server/GpgKeys.java16
-rw-r--r--java/com/google/gerrit/gpg/server/PostGpgKeys.java62
-rw-r--r--java/com/google/gerrit/gpg/testing/BUILD2
-rw-r--r--java/com/google/gerrit/httpd/AdvertisedObjectsCacheKey.java4
-rw-r--r--java/com/google/gerrit/httpd/BUILD7
-rw-r--r--java/com/google/gerrit/httpd/CacheBasedWebSession.java4
-rw-r--r--java/com/google/gerrit/httpd/ContainerAuthFilter.java4
-rw-r--r--java/com/google/gerrit/httpd/DirectChangeByCommit.java6
-rw-r--r--java/com/google/gerrit/httpd/GitOverHttpModule.java2
-rw-r--r--java/com/google/gerrit/httpd/GitOverHttpServlet.java57
-rw-r--r--java/com/google/gerrit/httpd/HttpServletResponseRecorder.java2
-rw-r--r--java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java4
-rw-r--r--java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java11
-rw-r--r--java/com/google/gerrit/httpd/ProjectOAuthFilter.java10
-rw-r--r--java/com/google/gerrit/httpd/RequestMetrics.java10
-rw-r--r--java/com/google/gerrit/httpd/RunAsFilter.java4
-rw-r--r--java/com/google/gerrit/httpd/UrlModule.java10
-rw-r--r--java/com/google/gerrit/httpd/WebSession.java2
-rw-r--r--java/com/google/gerrit/httpd/WebSessionManager.java4
-rw-r--r--java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java22
-rw-r--r--java/com/google/gerrit/httpd/auth/oauth/BUILD5
-rw-r--r--java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java4
-rw-r--r--java/com/google/gerrit/httpd/auth/openid/BUILD6
-rw-r--r--java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java4
-rw-r--r--java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java4
-rw-r--r--java/com/google/gerrit/httpd/gitweb/GitwebServlet.java10
-rw-r--r--java/com/google/gerrit/httpd/init/BUILD3
-rw-r--r--java/com/google/gerrit/httpd/init/WebAppInitializer.java24
-rw-r--r--java/com/google/gerrit/httpd/raw/AuthorizationCheckServlet.java52
-rw-r--r--java/com/google/gerrit/httpd/raw/CatServlet.java16
-rw-r--r--java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java148
-rw-r--r--java/com/google/gerrit/httpd/raw/IndexServlet.java98
-rw-r--r--java/com/google/gerrit/httpd/raw/StaticModule.java14
-rw-r--r--java/com/google/gerrit/httpd/restapi/RestApiMetrics.java18
-rw-r--r--java/com/google/gerrit/httpd/restapi/RestApiServlet.java546
-rw-r--r--java/com/google/gerrit/index/BUILD5
-rw-r--r--java/com/google/gerrit/index/IndexConfig.java15
-rw-r--r--java/com/google/gerrit/index/IndexType.java59
-rw-r--r--java/com/google/gerrit/index/RefState.java8
-rw-r--r--java/com/google/gerrit/index/Schema.java20
-rw-r--r--java/com/google/gerrit/index/SchemaUtil.java11
-rw-r--r--java/com/google/gerrit/index/project/BUILD2
-rw-r--r--java/com/google/gerrit/index/project/IndexedProjectQuery.java2
-rw-r--r--java/com/google/gerrit/index/project/ProjectData.java2
-rw-r--r--java/com/google/gerrit/index/project/ProjectField.java4
-rw-r--r--java/com/google/gerrit/index/project/ProjectIndex.java2
-rw-r--r--java/com/google/gerrit/index/project/ProjectIndexCollection.java2
-rw-r--r--java/com/google/gerrit/index/project/ProjectIndexer.java2
-rw-r--r--java/com/google/gerrit/index/query/AndSource.java85
-rw-r--r--java/com/google/gerrit/index/query/LazyResultSet.java56
-rw-r--r--java/com/google/gerrit/index/query/ListResultSet.java22
-rw-r--r--java/com/google/gerrit/index/query/QueryProcessor.java8
-rw-r--r--java/com/google/gerrit/index/query/TooManyTermsInQueryException.java29
-rw-r--r--java/com/google/gerrit/index/query/testing/TreeSubject.java15
-rw-r--r--java/com/google/gerrit/jgit/BUILD2
-rw-r--r--java/com/google/gerrit/json/BUILD1
-rw-r--r--java/com/google/gerrit/json/EnumTypeAdapterFactory.java78
-rw-r--r--java/com/google/gerrit/json/OutputFormat.java3
-rw-r--r--java/com/google/gerrit/json/SqlTimestampDeserializer.java10
-rw-r--r--java/com/google/gerrit/lucene/AbstractLuceneIndex.java27
-rw-r--r--java/com/google/gerrit/lucene/BUILD7
-rw-r--r--java/com/google/gerrit/lucene/ChangeSubIndex.java6
-rw-r--r--java/com/google/gerrit/lucene/LuceneAccountIndex.java38
-rw-r--r--java/com/google/gerrit/lucene/LuceneChangeIndex.java86
-rw-r--r--java/com/google/gerrit/lucene/LuceneGroupIndex.java4
-rw-r--r--java/com/google/gerrit/lucene/LuceneProjectIndex.java4
-rw-r--r--java/com/google/gerrit/lucene/QueryBuilder.java65
-rw-r--r--java/com/google/gerrit/lucene/WrappableSearcherManager.java2
-rw-r--r--java/com/google/gerrit/mail/BUILD2
-rw-r--r--java/com/google/gerrit/mail/HtmlParser.java2
-rw-r--r--java/com/google/gerrit/mail/MailComment.java2
-rw-r--r--java/com/google/gerrit/mail/ParserUtil.java2
-rw-r--r--java/com/google/gerrit/mail/TextParser.java2
-rw-r--r--java/com/google/gerrit/metrics/BUILD5
-rw-r--r--java/com/google/gerrit/metrics/DisabledMetricMaker.java6
-rw-r--r--java/com/google/gerrit/metrics/Field.java172
-rw-r--r--java/com/google/gerrit/metrics/Timer0.java7
-rw-r--r--java/com/google/gerrit/metrics/Timer1.java49
-rw-r--r--java/com/google/gerrit/metrics/Timer2.java64
-rw-r--r--java/com/google/gerrit/metrics/Timer3.java85
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl1.java2
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/CounterImpl1.java2
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/CounterImplN.java2
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/GetMetric.java7
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/HistogramImpl1.java2
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/HistogramImplN.java2
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/ListMetrics.java5
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/MetricJson.java8
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java5
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/TimerImplN.java9
-rw-r--r--java/com/google/gerrit/metrics/proc/JGitMetricModule.java3
-rw-r--r--java/com/google/gerrit/metrics/proc/ProcMetricModule.java10
-rw-r--r--java/com/google/gerrit/pgm/BUILD16
-rw-r--r--java/com/google/gerrit/pgm/Daemon.java54
-rw-r--r--java/com/google/gerrit/pgm/Init.java3
-rw-r--r--java/com/google/gerrit/pgm/Reindex.java25
-rw-r--r--java/com/google/gerrit/pgm/Rulec.java4
-rw-r--r--java/com/google/gerrit/pgm/http/jetty/BUILD2
-rw-r--r--java/com/google/gerrit/pgm/init/AccountsOnInit.java107
-rw-r--r--java/com/google/gerrit/pgm/init/BUILD4
-rw-r--r--java/com/google/gerrit/pgm/init/BaseInit.java17
-rw-r--r--java/com/google/gerrit/pgm/init/GroupsOnInit.java6
-rw-r--r--java/com/google/gerrit/pgm/init/InitAdminUser.java21
-rw-r--r--java/com/google/gerrit/pgm/init/InitIndex.java18
-rw-r--r--java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java4
-rw-r--r--java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java8
-rw-r--r--java/com/google/gerrit/pgm/init/api/BUILD5
-rw-r--r--java/com/google/gerrit/pgm/init/api/GitRepositoryManagerOnInit.java2
-rw-r--r--java/com/google/gerrit/pgm/init/api/Section.java4
-rw-r--r--java/com/google/gerrit/pgm/init/api/SequencesOnInit.java4
-rw-r--r--java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java4
-rw-r--r--java/com/google/gerrit/pgm/rules/PrologCompiler.java2
-rw-r--r--java/com/google/gerrit/pgm/util/BUILD4
-rw-r--r--java/com/google/gerrit/pgm/util/BatchProgramModule.java2
-rw-r--r--java/com/google/gerrit/pgm/util/SiteProgram.java9
-rw-r--r--java/com/google/gerrit/prettify/BUILD3
-rw-r--r--java/com/google/gerrit/proto/testing/BUILD1
-rw-r--r--java/com/google/gerrit/proto/testing/SerializedClassSubject.java25
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupById.java104
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java181
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupMember.java100
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java186
-rw-r--r--java/com/google/gerrit/reviewdb/client/Branch.java115
-rw-r--r--java/com/google/gerrit/reviewdb/client/PatchLineComment.java361
-rw-r--r--java/com/google/gerrit/reviewdb/client/PatchSet.java304
-rw-r--r--java/com/google/gerrit/reviewdb/client/PatchSetApproval.java238
-rw-r--r--java/com/google/gerrit/reviewdb/client/RevId.java73
-rw-r--r--java/com/google/gerrit/reviewdb/client/RobotComment.java99
-rw-r--r--java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java114
-rw-r--r--java/com/google/gerrit/reviewdb/converter/PatchSetProtoConverter.java93
-rw-r--r--java/com/google/gerrit/reviewdb/converter/RevIdProtoConverter.java38
-rw-r--r--java/com/google/gerrit/server/ApprovalInference.java (renamed from java/com/google/gerrit/server/ApprovalCopier.java)293
-rw-r--r--java/com/google/gerrit/server/ApprovalsUtil.java85
-rw-r--r--java/com/google/gerrit/server/AssigneeStatusUpdate.java35
-rw-r--r--java/com/google/gerrit/server/BUILD17
-rw-r--r--java/com/google/gerrit/server/ChangeMessagesUtil.java13
-rw-r--r--java/com/google/gerrit/server/ChangeUtil.java12
-rw-r--r--java/com/google/gerrit/server/CmdLineParserModule.java8
-rw-r--r--java/com/google/gerrit/server/CommentsUtil.java51
-rw-r--r--java/com/google/gerrit/server/CreateGroupPermissionSyncer.java2
-rw-r--r--java/com/google/gerrit/server/CurrentUser.java2
-rw-r--r--java/com/google/gerrit/server/ExceptionHook.java42
-rw-r--r--java/com/google/gerrit/server/IdentifiedUser.java25
-rw-r--r--java/com/google/gerrit/server/PatchSetUtil.java49
-rw-r--r--java/com/google/gerrit/server/ProjectUtil.java13
-rw-r--r--java/com/google/gerrit/server/PublishCommentUtil.java55
-rw-r--r--java/com/google/gerrit/server/RequestInfo.java96
-rw-r--r--java/com/google/gerrit/server/RequestListener.java22
-rw-r--r--java/com/google/gerrit/server/ReviewerSet.java20
-rw-r--r--java/com/google/gerrit/server/ReviewerStatusUpdate.java2
-rw-r--r--java/com/google/gerrit/server/StarredChangesUtil.java46
-rw-r--r--java/com/google/gerrit/server/TraceRequestListener.java228
-rw-r--r--java/com/google/gerrit/server/WebLinks.java73
-rw-r--r--java/com/google/gerrit/server/account/AbstractGroupBackend.java2
-rw-r--r--java/com/google/gerrit/server/account/AbstractRealm.java4
-rw-r--r--java/com/google/gerrit/server/account/AccountCache.java2
-rw-r--r--java/com/google/gerrit/server/account/AccountCacheImpl.java56
-rw-r--r--java/com/google/gerrit/server/account/AccountConfig.java33
-rw-r--r--java/com/google/gerrit/server/account/AccountControl.java6
-rw-r--r--java/com/google/gerrit/server/account/AccountDeactivator.java10
-rw-r--r--java/com/google/gerrit/server/account/AccountDirectory.java5
-rw-r--r--java/com/google/gerrit/server/account/AccountExternalIdCreator.java2
-rw-r--r--java/com/google/gerrit/server/account/AccountLoader.java10
-rw-r--r--java/com/google/gerrit/server/account/AccountManager.java37
-rw-r--r--java/com/google/gerrit/server/account/AccountProperties.java15
-rw-r--r--java/com/google/gerrit/server/account/AccountResolver.java16
-rw-r--r--java/com/google/gerrit/server/account/AccountResource.java2
-rw-r--r--java/com/google/gerrit/server/account/AccountSshKey.java2
-rw-r--r--java/com/google/gerrit/server/account/AccountState.java228
-rw-r--r--java/com/google/gerrit/server/account/Accounts.java8
-rw-r--r--java/com/google/gerrit/server/account/AccountsConsistencyChecker.java12
-rw-r--r--java/com/google/gerrit/server/account/AccountsUpdate.java22
-rw-r--r--java/com/google/gerrit/server/account/AuthResult.java2
-rw-r--r--java/com/google/gerrit/server/account/AuthorizedKeys.java2
-rw-r--r--java/com/google/gerrit/server/account/CreateGroupArgs.java7
-rw-r--r--java/com/google/gerrit/server/account/DefaultRealm.java2
-rw-r--r--java/com/google/gerrit/server/account/DestinationList.java20
-rw-r--r--java/com/google/gerrit/server/account/Emails.java61
-rw-r--r--java/com/google/gerrit/server/account/FakeRealm.java2
-rw-r--r--java/com/google/gerrit/server/account/GroupBackend.java2
-rw-r--r--java/com/google/gerrit/server/account/GroupCache.java2
-rw-r--r--java/com/google/gerrit/server/account/GroupCacheImpl.java19
-rw-r--r--java/com/google/gerrit/server/account/GroupControl.java4
-rw-r--r--java/com/google/gerrit/server/account/GroupIncludeCache.java4
-rw-r--r--java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java13
-rw-r--r--java/com/google/gerrit/server/account/GroupMembers.java8
-rw-r--r--java/com/google/gerrit/server/account/GroupMembership.java2
-rw-r--r--java/com/google/gerrit/server/account/GroupUUID.java4
-rw-r--r--java/com/google/gerrit/server/account/HashedPassword.java9
-rw-r--r--java/com/google/gerrit/server/account/IncludingGroupMembership.java2
-rw-r--r--java/com/google/gerrit/server/account/InternalAccountDirectory.java54
-rw-r--r--java/com/google/gerrit/server/account/InternalAccountUpdate.java2
-rw-r--r--java/com/google/gerrit/server/account/InternalGroupBackend.java2
-rw-r--r--java/com/google/gerrit/server/account/ListGroupMembership.java2
-rw-r--r--java/com/google/gerrit/server/account/Preferences.java843
-rw-r--r--java/com/google/gerrit/server/account/ProjectWatches.java6
-rw-r--r--java/com/google/gerrit/server/account/Realm.java2
-rw-r--r--java/com/google/gerrit/server/account/SetInactiveFlag.java30
-rw-r--r--java/com/google/gerrit/server/account/StoredPreferences.java574
-rw-r--r--java/com/google/gerrit/server/account/UniversalGroupBackend.java4
-rw-r--r--java/com/google/gerrit/server/account/VersionedAccountDestinations.java4
-rw-r--r--java/com/google/gerrit/server/account/VersionedAccountQueries.java26
-rw-r--r--java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java4
-rw-r--r--java/com/google/gerrit/server/account/externalids/AllExternalIds.java4
-rw-r--r--java/com/google/gerrit/server/account/externalids/DisabledExternalIdCache.java2
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalId.java12
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIdCache.java2
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java25
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java277
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIdModule.java6
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java27
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIdReader.java2
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIds.java2
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java3
-rw-r--r--java/com/google/gerrit/server/account/externalids/PasswordVerifier.java53
-rw-r--r--java/com/google/gerrit/server/account/externalids/testing/BUILD13
-rw-r--r--java/com/google/gerrit/server/account/externalids/testing/ExternalIdInserter.java25
-rw-r--r--java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java163
-rw-r--r--java/com/google/gerrit/server/api/BUILD4
-rw-r--r--java/com/google/gerrit/server/api/accounts/AccountApiImpl.java42
-rw-r--r--java/com/google/gerrit/server/api/accounts/AccountsImpl.java4
-rw-r--r--java/com/google/gerrit/server/api/accounts/EmailApiImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/changes/ChangeApiImpl.java30
-rw-r--r--java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/changes/ChangesImpl.java6
-rw-r--r--java/com/google/gerrit/server/api/changes/CommentApiImpl.java4
-rw-r--r--java/com/google/gerrit/server/api/changes/DraftApiImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/changes/FileApiImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/changes/RevisionApiImpl.java54
-rw-r--r--java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/config/ServerImpl.java24
-rw-r--r--java/com/google/gerrit/server/api/groups/GroupApiImpl.java30
-rw-r--r--java/com/google/gerrit/server/api/groups/GroupsImpl.java8
-rw-r--r--java/com/google/gerrit/server/api/plugins/PluginApiImpl.java8
-rw-r--r--java/com/google/gerrit/server/api/plugins/PluginsImpl.java13
-rw-r--r--java/com/google/gerrit/server/api/projects/BranchApiImpl.java10
-rw-r--r--java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java10
-rw-r--r--java/com/google/gerrit/server/api/projects/CommitApiImpl.java6
-rw-r--r--java/com/google/gerrit/server/api/projects/DashboardApiImpl.java4
-rw-r--r--java/com/google/gerrit/server/api/projects/ProjectApiImpl.java54
-rw-r--r--java/com/google/gerrit/server/args4j/AccountGroupIdHandler.java4
-rw-r--r--java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java4
-rw-r--r--java/com/google/gerrit/server/args4j/AccountIdHandler.java4
-rw-r--r--java/com/google/gerrit/server/args4j/ChangeIdHandler.java10
-rw-r--r--java/com/google/gerrit/server/args4j/PatchSetIdHandler.java2
-rw-r--r--java/com/google/gerrit/server/args4j/ProjectHandler.java4
-rw-r--r--java/com/google/gerrit/server/audit/AuditService.java4
-rw-r--r--java/com/google/gerrit/server/audit/BUILD12
-rw-r--r--java/com/google/gerrit/server/audit/group/GroupAuditEvent.java10
-rw-r--r--java/com/google/gerrit/server/audit/group/GroupMemberAuditEvent.java4
-rw-r--r--java/com/google/gerrit/server/audit/group/GroupSubgroupAuditEvent.java4
-rw-r--r--java/com/google/gerrit/server/auth/InternalAuthBackend.java5
-rw-r--r--java/com/google/gerrit/server/auth/ldap/Helper.java4
-rw-r--r--java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java8
-rw-r--r--java/com/google/gerrit/server/auth/ldap/LdapGroupMembership.java2
-rw-r--r--java/com/google/gerrit/server/auth/ldap/LdapModule.java4
-rw-r--r--java/com/google/gerrit/server/auth/ldap/LdapRealm.java19
-rw-r--r--java/com/google/gerrit/server/auth/oauth/OAuthRealm.java2
-rw-r--r--java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java9
-rw-r--r--java/com/google/gerrit/server/cache/CacheMetrics.java4
-rw-r--r--java/com/google/gerrit/server/cache/h2/BUILD2
-rw-r--r--java/com/google/gerrit/server/cache/h2/H2CacheImpl.java5
-rw-r--r--java/com/google/gerrit/server/cache/mem/BUILD2
-rw-r--r--java/com/google/gerrit/server/cache/serialize/BUILD4
-rw-r--r--java/com/google/gerrit/server/cache/serialize/CacheSerializer.java23
-rw-r--r--java/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializer.java38
-rw-r--r--java/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializer.java6
-rw-r--r--java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java9
-rw-r--r--java/com/google/gerrit/server/change/AbandonOp.java8
-rw-r--r--java/com/google/gerrit/server/change/AbandonUtil.java19
-rw-r--r--java/com/google/gerrit/server/change/AccountPatchReviewStore.java9
-rw-r--r--java/com/google/gerrit/server/change/AddReviewersEmail.java4
-rw-r--r--java/com/google/gerrit/server/change/AddReviewersOp.java28
-rw-r--r--java/com/google/gerrit/server/change/ArchiveFormat.java2
-rw-r--r--java/com/google/gerrit/server/change/BatchAbandon.java2
-rw-r--r--java/com/google/gerrit/server/change/ChangeETagComputation.java63
-rw-r--r--java/com/google/gerrit/server/change/ChangeFinder.java16
-rw-r--r--java/com/google/gerrit/server/change/ChangeInserter.java34
-rw-r--r--java/com/google/gerrit/server/change/ChangeJson.java41
-rw-r--r--java/com/google/gerrit/server/change/ChangeKeyAdapter.java51
-rw-r--r--java/com/google/gerrit/server/change/ChangeKindCache.java6
-rw-r--r--java/com/google/gerrit/server/change/ChangeKindCacheImpl.java30
-rw-r--r--java/com/google/gerrit/server/change/ChangeMessageResource.java2
-rw-r--r--java/com/google/gerrit/server/change/ChangeResource.java52
-rw-r--r--java/com/google/gerrit/server/change/ChangeTriplet.java16
-rw-r--r--java/com/google/gerrit/server/change/CommentResource.java6
-rw-r--r--java/com/google/gerrit/server/change/ConsistencyChecker.java73
-rw-r--r--java/com/google/gerrit/server/change/DeleteChangeOp.java61
-rw-r--r--java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java8
-rw-r--r--java/com/google/gerrit/server/change/DeleteReviewerOp.java39
-rw-r--r--java/com/google/gerrit/server/change/DraftCommentResource.java6
-rw-r--r--java/com/google/gerrit/server/change/EmailReviewComments.java8
-rw-r--r--java/com/google/gerrit/server/change/FileContentUtil.java2
-rw-r--r--java/com/google/gerrit/server/change/FileInfoJson.java24
-rw-r--r--java/com/google/gerrit/server/change/FileResource.java6
-rw-r--r--java/com/google/gerrit/server/change/FixResource.java2
-rw-r--r--java/com/google/gerrit/server/change/IncludedIn.java2
-rw-r--r--java/com/google/gerrit/server/change/LabelNormalizer.java31
-rw-r--r--java/com/google/gerrit/server/change/LabelsJson.java40
-rw-r--r--java/com/google/gerrit/server/change/MergeabilityCache.java6
-rw-r--r--java/com/google/gerrit/server/change/MergeabilityCacheImpl.java4
-rw-r--r--java/com/google/gerrit/server/change/NotifyResolver.java4
-rw-r--r--java/com/google/gerrit/server/change/PatchSetInserter.java14
-rw-r--r--java/com/google/gerrit/server/change/PureRevert.java6
-rw-r--r--java/com/google/gerrit/server/change/RebaseChangeOp.java17
-rw-r--r--java/com/google/gerrit/server/change/RebaseUtil.java50
-rw-r--r--java/com/google/gerrit/server/change/ReviewerAdder.java44
-rw-r--r--java/com/google/gerrit/server/change/ReviewerJson.java10
-rw-r--r--java/com/google/gerrit/server/change/ReviewerResource.java4
-rw-r--r--java/com/google/gerrit/server/change/ReviewerSuggestion.java11
-rw-r--r--java/com/google/gerrit/server/change/RevisionJson.java42
-rw-r--r--java/com/google/gerrit/server/change/RevisionResource.java30
-rw-r--r--java/com/google/gerrit/server/change/RobotCommentResource.java6
-rw-r--r--java/com/google/gerrit/server/change/SetAssigneeOp.java4
-rw-r--r--java/com/google/gerrit/server/change/SetHashtagsOp.java4
-rw-r--r--java/com/google/gerrit/server/change/SetPrivateOp.java6
-rw-r--r--java/com/google/gerrit/server/change/SuggestedReviewer.java2
-rw-r--r--java/com/google/gerrit/server/change/WalkSorter.java14
-rw-r--r--java/com/google/gerrit/server/change/WorkInProgressOp.java6
-rw-r--r--java/com/google/gerrit/server/change/testing/TestChangeETagComputation.java30
-rw-r--r--java/com/google/gerrit/server/config/AllProjectsName.java2
-rw-r--r--java/com/google/gerrit/server/config/AllUsersName.java2
-rw-r--r--java/com/google/gerrit/server/config/DownloadConfig.java2
-rw-r--r--java/com/google/gerrit/server/config/GerritGlobalModule.java22
-rw-r--r--java/com/google/gerrit/server/config/GerritIsReplica.java25
-rw-r--r--java/com/google/gerrit/server/config/GerritIsReplicaProvider.java46
-rw-r--r--java/com/google/gerrit/server/config/GerritServerConfigModule.java3
-rw-r--r--java/com/google/gerrit/server/config/GroupSetProvider.java2
-rw-r--r--java/com/google/gerrit/server/config/PluginConfigFactory.java2
-rw-r--r--java/com/google/gerrit/server/config/ProjectConfigEntry.java6
-rw-r--r--java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java2
-rw-r--r--java/com/google/gerrit/server/config/RepositoryConfig.java2
-rw-r--r--java/com/google/gerrit/server/config/SitePaths.java4
-rw-r--r--java/com/google/gerrit/server/config/UrlFormatter.java4
-rw-r--r--java/com/google/gerrit/server/data/ChangeAttribute.java2
-rw-r--r--java/com/google/gerrit/server/data/PatchAttribute.java2
-rw-r--r--java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java3
-rw-r--r--java/com/google/gerrit/server/edit/ChangeEdit.java4
-rw-r--r--java/com/google/gerrit/server/edit/ChangeEditJson.java4
-rw-r--r--java/com/google/gerrit/server/edit/ChangeEditModifier.java30
-rw-r--r--java/com/google/gerrit/server/edit/ChangeEditUtil.java21
-rw-r--r--java/com/google/gerrit/server/events/AssigneeChangedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/ChangeAbandonedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/ChangeDeletedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/ChangeEvent.java8
-rw-r--r--java/com/google/gerrit/server/events/ChangeMergedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/ChangeRestoredEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/CommentAddedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/CommitReceivedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/EventBroker.java23
-rw-r--r--java/com/google/gerrit/server/events/EventDispatcher.java8
-rw-r--r--java/com/google/gerrit/server/events/EventFactory.java101
-rw-r--r--java/com/google/gerrit/server/events/EventGson.java28
-rw-r--r--java/com/google/gerrit/server/events/EventGsonProvider.java36
-rw-r--r--java/com/google/gerrit/server/events/EventsMetrics.java3
-rw-r--r--java/com/google/gerrit/server/events/HashtagsChangedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/PatchSetCreatedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/PatchSetEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/PrivateStateChangedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/ProjectCreatedEvent.java4
-rw-r--r--java/com/google/gerrit/server/events/ProjectEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/ProjectNameKeyAdapter.java (renamed from java/com/google/gerrit/server/events/ProjectNameKeySerializer.java)18
-rw-r--r--java/com/google/gerrit/server/events/RefEvent.java6
-rw-r--r--java/com/google/gerrit/server/events/RefReceivedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/RefUpdatedEvent.java4
-rw-r--r--java/com/google/gerrit/server/events/ReviewerAddedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/ReviewerDeletedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/StreamEventsApiListener.java14
-rw-r--r--java/com/google/gerrit/server/events/TopicChangedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/VoteDeletedEvent.java2
-rw-r--r--java/com/google/gerrit/server/events/WorkInProgressStateChangedEvent.java2
-rw-r--r--java/com/google/gerrit/server/extensions/events/AssigneeChanged.java2
-rw-r--r--java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/ChangeDeleted.java2
-rw-r--r--java/com/google/gerrit/server/extensions/events/ChangeMerged.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/ChangeRestored.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/ChangeReverted.java2
-rw-r--r--java/com/google/gerrit/server/extensions/events/CommentAdded.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/EventUtil.java69
-rw-r--r--java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java2
-rw-r--r--java/com/google/gerrit/server/extensions/events/HashtagsEdited.java2
-rw-r--r--java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/ReviewerAdded.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/RevisionCreated.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/TopicEdited.java2
-rw-r--r--java/com/google/gerrit/server/extensions/events/VoteDeleted.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java4
-rw-r--r--java/com/google/gerrit/server/extensions/webui/UiActions.java7
-rw-r--r--java/com/google/gerrit/server/fixes/FixReplacementInterpreter.java4
-rw-r--r--java/com/google/gerrit/server/git/BanCommit.java6
-rw-r--r--java/com/google/gerrit/server/git/BranchOrderSection.java2
-rw-r--r--java/com/google/gerrit/server/git/ChangeMessageModifier.java4
-rw-r--r--java/com/google/gerrit/server/git/ChangeReportFormatter.java2
-rw-r--r--java/com/google/gerrit/server/git/CodeReviewCommit.java6
-rw-r--r--java/com/google/gerrit/server/git/CommitUtil.java114
-rw-r--r--java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java58
-rw-r--r--java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java2
-rw-r--r--java/com/google/gerrit/server/git/DelegateRefDatabase.java86
-rw-r--r--java/com/google/gerrit/server/git/DelegateRepository.java89
-rw-r--r--java/com/google/gerrit/server/git/GarbageCollection.java2
-rw-r--r--java/com/google/gerrit/server/git/GarbageCollectionQueue.java2
-rw-r--r--java/com/google/gerrit/server/git/GitRepositoryManager.java2
-rw-r--r--java/com/google/gerrit/server/git/GroupCollector.java12
-rw-r--r--java/com/google/gerrit/server/git/HookUtil.java33
-rw-r--r--java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java6
-rw-r--r--java/com/google/gerrit/server/git/MergeUtil.java121
-rw-r--r--java/com/google/gerrit/server/git/MergedByPushOp.java29
-rw-r--r--java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java2
-rw-r--r--java/com/google/gerrit/server/git/MultiProgressMonitor.java2
-rw-r--r--java/com/google/gerrit/server/git/NotesBranchUtil.java2
-rw-r--r--java/com/google/gerrit/server/git/NotifyConfig.java10
-rw-r--r--java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java185
-rw-r--r--java/com/google/gerrit/server/git/PermissionAwareRepository.java39
-rw-r--r--java/com/google/gerrit/server/git/PermissionAwareRepositoryManager.java32
-rw-r--r--java/com/google/gerrit/server/git/ProjectRunnable.java2
-rw-r--r--java/com/google/gerrit/server/git/PureRevertCache.java22
-rw-r--r--java/com/google/gerrit/server/git/ReceivePackInitializer.java2
-rw-r--r--java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java2
-rw-r--r--java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java13
-rw-r--r--java/com/google/gerrit/server/git/SystemReaderInstaller.java58
-rw-r--r--java/com/google/gerrit/server/git/TagCache.java2
-rw-r--r--java/com/google/gerrit/server/git/TagSet.java8
-rw-r--r--java/com/google/gerrit/server/git/TagSetHolder.java4
-rw-r--r--java/com/google/gerrit/server/git/TracingHook.java96
-rw-r--r--java/com/google/gerrit/server/git/UploadPackInitializer.java2
-rw-r--r--java/com/google/gerrit/server/git/UploadPackMetricsHook.java14
-rw-r--r--java/com/google/gerrit/server/git/UsersSelfAdvertiseRefsHook.java93
-rw-r--r--java/com/google/gerrit/server/git/WorkQueue.java2
-rw-r--r--java/com/google/gerrit/server/git/meta/MetaDataUpdate.java2
-rw-r--r--java/com/google/gerrit/server/git/meta/VersionedMetaData.java37
-rw-r--r--java/com/google/gerrit/server/git/receive/AllRefsWatcher.java4
-rw-r--r--java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java54
-rw-r--r--java/com/google/gerrit/server/git/receive/BUILD5
-rw-r--r--java/com/google/gerrit/server/git/receive/BranchCommitValidator.java123
-rw-r--r--java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java36
-rw-r--r--java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java22
-rw-r--r--java/com/google/gerrit/server/git/receive/ReceiveCommits.java3179
-rw-r--r--java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java86
-rw-r--r--java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHookChain.java91
-rw-r--r--java/com/google/gerrit/server/git/receive/ReceiveConfig.java2
-rw-r--r--java/com/google/gerrit/server/git/receive/ReceiveRefFilter.java4
-rw-r--r--java/com/google/gerrit/server/git/receive/ReplaceOp.java40
-rw-r--r--java/com/google/gerrit/server/git/receive/ResultChangeIds.java2
-rw-r--r--java/com/google/gerrit/server/git/receive/testing/BUILD14
-rw-r--r--java/com/google/gerrit/server/git/receive/testing/TestRefAdvertiser.java87
-rw-r--r--java/com/google/gerrit/server/git/validators/AccountValidator.java6
-rw-r--r--java/com/google/gerrit/server/git/validators/CommitValidators.java42
-rw-r--r--java/com/google/gerrit/server/git/validators/MergeValidationListener.java6
-rw-r--r--java/com/google/gerrit/server/git/validators/MergeValidators.java29
-rw-r--r--java/com/google/gerrit/server/git/validators/OnSubmitValidationListener.java2
-rw-r--r--java/com/google/gerrit/server/git/validators/OnSubmitValidators.java2
-rw-r--r--java/com/google/gerrit/server/git/validators/RefOperationValidationException.java17
-rw-r--r--java/com/google/gerrit/server/git/validators/RefOperationValidators.java25
-rw-r--r--java/com/google/gerrit/server/git/validators/UploadValidationListener.java2
-rw-r--r--java/com/google/gerrit/server/git/validators/UploadValidators.java2
-rw-r--r--java/com/google/gerrit/server/group/GroupAuditService.java4
-rw-r--r--java/com/google/gerrit/server/group/GroupResolver.java4
-rw-r--r--java/com/google/gerrit/server/group/InternalGroup.java4
-rw-r--r--java/com/google/gerrit/server/group/InternalGroupDescription.java4
-rw-r--r--java/com/google/gerrit/server/group/PeriodicGroupIndexer.java2
-rw-r--r--java/com/google/gerrit/server/group/SubgroupResource.java2
-rw-r--r--java/com/google/gerrit/server/group/SystemGroupBackend.java10
-rw-r--r--java/com/google/gerrit/server/group/db/AuditLogFormatter.java10
-rw-r--r--java/com/google/gerrit/server/group/db/AuditLogReader.java76
-rw-r--r--java/com/google/gerrit/server/group/db/GroupConfig.java12
-rw-r--r--java/com/google/gerrit/server/group/db/GroupConfigEntry.java8
-rw-r--r--java/com/google/gerrit/server/group/db/GroupNameNotes.java13
-rw-r--r--java/com/google/gerrit/server/group/db/Groups.java8
-rw-r--r--java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java4
-rw-r--r--java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java6
-rw-r--r--java/com/google/gerrit/server/group/db/GroupsUpdate.java16
-rw-r--r--java/com/google/gerrit/server/group/db/InternalGroupCreation.java2
-rw-r--r--java/com/google/gerrit/server/group/db/InternalGroupUpdate.java4
-rw-r--r--java/com/google/gerrit/server/group/db/RenameGroupOp.java4
-rw-r--r--java/com/google/gerrit/server/group/db/testing/BUILD4
-rw-r--r--java/com/google/gerrit/server/group/testing/BUILD4
-rw-r--r--java/com/google/gerrit/server/group/testing/InternalGroupSubject.java57
-rw-r--r--java/com/google/gerrit/server/group/testing/TestGroupBackend.java4
-rw-r--r--java/com/google/gerrit/server/index/AbstractIndexModule.java24
-rw-r--r--java/com/google/gerrit/server/index/IndexModule.java22
-rw-r--r--java/com/google/gerrit/server/index/IndexUtils.java22
-rw-r--r--java/com/google/gerrit/server/index/account/AccountField.java53
-rw-r--r--java/com/google/gerrit/server/index/account/AccountIndex.java4
-rw-r--r--java/com/google/gerrit/server/index/account/AccountIndexCollection.java2
-rw-r--r--java/com/google/gerrit/server/index/account/AccountIndexDefinition.java2
-rw-r--r--java/com/google/gerrit/server/index/account/AccountIndexRewriter.java35
-rw-r--r--java/com/google/gerrit/server/index/account/AccountIndexer.java2
-rw-r--r--java/com/google/gerrit/server/index/account/AccountIndexerImpl.java15
-rw-r--r--java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java13
-rw-r--r--java/com/google/gerrit/server/index/account/AllAccountsIndexer.java2
-rw-r--r--java/com/google/gerrit/server/index/account/IndexedAccountQuery.java2
-rw-r--r--java/com/google/gerrit/server/index/account/StalenessChecker.java26
-rw-r--r--java/com/google/gerrit/server/index/change/AllChangesIndexer.java6
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeField.java54
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeIndex.java7
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeIndexCollection.java2
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeIndexDefinition.java2
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java16
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeIndexer.java54
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java72
-rw-r--r--java/com/google/gerrit/server/index/change/DummyChangeIndex.java2
-rw-r--r--java/com/google/gerrit/server/index/change/IndexedChangeQuery.java2
-rw-r--r--java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java82
-rw-r--r--java/com/google/gerrit/server/index/change/StalenessChecker.java7
-rw-r--r--java/com/google/gerrit/server/index/group/AllGroupsIndexer.java2
-rw-r--r--java/com/google/gerrit/server/index/group/GroupField.java8
-rw-r--r--java/com/google/gerrit/server/index/group/GroupIndex.java2
-rw-r--r--java/com/google/gerrit/server/index/group/GroupIndexCollection.java2
-rw-r--r--java/com/google/gerrit/server/index/group/GroupIndexDefinition.java2
-rw-r--r--java/com/google/gerrit/server/index/group/GroupIndexer.java2
-rw-r--r--java/com/google/gerrit/server/index/group/GroupIndexerImpl.java15
-rw-r--r--java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java6
-rw-r--r--java/com/google/gerrit/server/index/group/IndexedGroupQuery.java2
-rw-r--r--java/com/google/gerrit/server/index/group/StalenessChecker.java4
-rw-r--r--java/com/google/gerrit/server/index/project/AllProjectsIndexer.java2
-rw-r--r--java/com/google/gerrit/server/index/project/ProjectIndexDefinition.java2
-rw-r--r--java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java17
-rw-r--r--java/com/google/gerrit/server/index/project/StalenessChecker.java4
-rw-r--r--java/com/google/gerrit/server/ioutil/BUILD6
-rw-r--r--java/com/google/gerrit/server/ioutil/BasicSerialization.java2
-rw-r--r--java/com/google/gerrit/server/logging/BUILD3
-rw-r--r--java/com/google/gerrit/server/logging/CallerFinder.java41
-rw-r--r--java/com/google/gerrit/server/logging/LoggingContext.java150
-rw-r--r--java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java39
-rw-r--r--java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java39
-rw-r--r--java/com/google/gerrit/server/logging/Metadata.java339
-rw-r--r--java/com/google/gerrit/server/logging/MutablePerformanceLogRecords.java55
-rw-r--r--java/com/google/gerrit/server/logging/MutableTags.java7
-rw-r--r--java/com/google/gerrit/server/logging/PerformanceLogContext.java117
-rw-r--r--java/com/google/gerrit/server/logging/PerformanceLogRecord.java64
-rw-r--r--java/com/google/gerrit/server/logging/PerformanceLogger.java50
-rw-r--r--java/com/google/gerrit/server/logging/PluginMetadata.java39
-rw-r--r--java/com/google/gerrit/server/logging/TraceContext.java132
-rw-r--r--java/com/google/gerrit/server/mail/EmailTokenVerifier.java2
-rw-r--r--java/com/google/gerrit/server/mail/MailUtil.java4
-rw-r--r--java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java2
-rw-r--r--java/com/google/gerrit/server/mail/receive/MailProcessor.java64
-rw-r--r--java/com/google/gerrit/server/mail/send/AbandonedSender.java10
-rw-r--r--java/com/google/gerrit/server/mail/send/AddKeySender.java10
-rw-r--r--java/com/google/gerrit/server/mail/send/AddReviewerSender.java10
-rw-r--r--java/com/google/gerrit/server/mail/send/ChangeEmail.java49
-rw-r--r--java/com/google/gerrit/server/mail/send/CommentSender.java32
-rw-r--r--java/com/google/gerrit/server/mail/send/CreateChangeSender.java14
-rw-r--r--java/com/google/gerrit/server/mail/send/DeleteKeySender.java12
-rw-r--r--java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java12
-rw-r--r--java/com/google/gerrit/server/mail/send/DeleteVoteSender.java10
-rw-r--r--java/com/google/gerrit/server/mail/send/EmailArguments.java8
-rw-r--r--java/com/google/gerrit/server/mail/send/FromAddressGenerator.java2
-rw-r--r--java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java13
-rw-r--r--java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java10
-rw-r--r--java/com/google/gerrit/server/mail/send/MailSoySauceProvider.java (renamed from java/com/google/gerrit/server/mail/send/MailSoyTofuProvider.java)31
-rw-r--r--java/com/google/gerrit/server/mail/send/MailSoyTemplateProvider.java43
-rw-r--r--java/com/google/gerrit/server/mail/send/MergedSender.java28
-rw-r--r--java/com/google/gerrit/server/mail/send/NewChangeSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/NotificationEmail.java20
-rw-r--r--java/com/google/gerrit/server/mail/send/OutgoingEmail.java90
-rw-r--r--java/com/google/gerrit/server/mail/send/ProjectWatch.java50
-rw-r--r--java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java12
-rw-r--r--java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java12
-rw-r--r--java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java8
-rw-r--r--java/com/google/gerrit/server/mail/send/RestoredSender.java10
-rw-r--r--java/com/google/gerrit/server/mail/send/RevertedSender.java10
-rw-r--r--java/com/google/gerrit/server/mail/send/SetAssigneeSender.java14
-rw-r--r--java/com/google/gerrit/server/notedb/AbstractChangeNotes.java16
-rw-r--r--java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java22
-rw-r--r--java/com/google/gerrit/server/notedb/AllUsersAsyncUpdate.java116
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java110
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNoteUtil.java17
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNotes.java101
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNotesCache.java36
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNotesParser.java320
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNotesState.java152
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeRevisionNote.java61
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeUpdate.java64
-rw-r--r--java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java8
-rw-r--r--java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java47
-rw-r--r--java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java2
-rw-r--r--java/com/google/gerrit/server/notedb/DraftCommentNotes.java27
-rw-r--r--java/com/google/gerrit/server/notedb/IntBlob.java2
-rw-r--r--java/com/google/gerrit/server/notedb/InvalidServerIdException.java25
-rw-r--r--java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java401
-rw-r--r--java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java189
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbMetrics.java12
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java233
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbUtil.java84
-rw-r--r--java/com/google/gerrit/server/notedb/OpenRepo.java176
-rw-r--r--java/com/google/gerrit/server/notedb/RepoSequence.java148
-rw-r--r--java/com/google/gerrit/server/notedb/ReviewerStateInternal.java16
-rw-r--r--java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java17
-rw-r--r--java/com/google/gerrit/server/notedb/RevisionNoteData.java2
-rw-r--r--java/com/google/gerrit/server/notedb/RevisionNoteMap.java29
-rw-r--r--java/com/google/gerrit/server/notedb/RobotCommentNotes.java17
-rw-r--r--java/com/google/gerrit/server/notedb/RobotCommentUpdate.java23
-rw-r--r--java/com/google/gerrit/server/notedb/RobotCommentsRevisionNote.java2
-rw-r--r--java/com/google/gerrit/server/notedb/RobotCommentsRevisionNoteData.java2
-rw-r--r--java/com/google/gerrit/server/notedb/Sequences.java18
-rw-r--r--java/com/google/gerrit/server/notedb/TooManyUpdatesException.java41
-rw-r--r--java/com/google/gerrit/server/patch/AutoMerger.java2
-rw-r--r--java/com/google/gerrit/server/patch/DiffSummaryLoader.java4
-rw-r--r--java/com/google/gerrit/server/patch/IntraLineDiff.java2
-rw-r--r--java/com/google/gerrit/server/patch/IntraLineDiffArgs.java2
-rw-r--r--java/com/google/gerrit/server/patch/PatchFile.java11
-rw-r--r--java/com/google/gerrit/server/patch/PatchList.java7
-rw-r--r--java/com/google/gerrit/server/patch/PatchListCache.java6
-rw-r--r--java/com/google/gerrit/server/patch/PatchListCacheImpl.java11
-rw-r--r--java/com/google/gerrit/server/patch/PatchListEntry.java10
-rw-r--r--java/com/google/gerrit/server/patch/PatchListKey.java3
-rw-r--r--java/com/google/gerrit/server/patch/PatchListLoader.java10
-rw-r--r--java/com/google/gerrit/server/patch/PatchScriptBuilder.java21
-rw-r--r--java/com/google/gerrit/server/patch/PatchScriptFactory.java87
-rw-r--r--java/com/google/gerrit/server/patch/PatchSetInfoFactory.java45
-rw-r--r--java/com/google/gerrit/server/patch/Text.java11
-rw-r--r--java/com/google/gerrit/server/permissions/ChangeControl.java6
-rw-r--r--java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java6
-rw-r--r--java/com/google/gerrit/server/permissions/DefaultRefFilter.java209
-rw-r--r--java/com/google/gerrit/server/permissions/FailedPermissionBackend.java2
-rw-r--r--java/com/google/gerrit/server/permissions/PermissionBackend.java16
-rw-r--r--java/com/google/gerrit/server/permissions/PermissionCollection.java4
-rw-r--r--java/com/google/gerrit/server/permissions/ProjectControl.java16
-rw-r--r--java/com/google/gerrit/server/permissions/ProjectPermission.java2
-rw-r--r--java/com/google/gerrit/server/permissions/RefControl.java12
-rw-r--r--java/com/google/gerrit/server/plugincontext/PluginContext.java40
-rw-r--r--java/com/google/gerrit/server/plugins/CopyConfigModule.java10
-rw-r--r--java/com/google/gerrit/server/plugins/DisablePlugin.java16
-rw-r--r--java/com/google/gerrit/server/plugins/EnablePlugin.java5
-rw-r--r--java/com/google/gerrit/server/plugins/GetStatus.java5
-rw-r--r--java/com/google/gerrit/server/plugins/ListPlugins.java6
-rw-r--r--java/com/google/gerrit/server/plugins/MandatoryPluginsCollection.java50
-rw-r--r--java/com/google/gerrit/server/plugins/MissingMandatoryPluginsException.java30
-rw-r--r--java/com/google/gerrit/server/plugins/PluginLoader.java94
-rw-r--r--java/com/google/gerrit/server/plugins/PluginModule.java1
-rw-r--r--java/com/google/gerrit/server/plugins/ReloadPlugin.java6
-rw-r--r--java/com/google/gerrit/server/project/AccessControlModule.java2
-rw-r--r--java/com/google/gerrit/server/project/BooleanProjectConfigTransformations.java2
-rw-r--r--java/com/google/gerrit/server/project/BranchResource.java6
-rw-r--r--java/com/google/gerrit/server/project/ChildProjects.java2
-rw-r--r--java/com/google/gerrit/server/project/ContributorAgreementsChecker.java10
-rw-r--r--java/com/google/gerrit/server/project/CreateProjectArgs.java6
-rw-r--r--java/com/google/gerrit/server/project/CreateRefControl.java16
-rw-r--r--java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java2
-rw-r--r--java/com/google/gerrit/server/project/GroupList.java6
-rw-r--r--java/com/google/gerrit/server/project/NoSuchChangeException.java2
-rw-r--r--java/com/google/gerrit/server/project/NoSuchProjectException.java2
-rw-r--r--java/com/google/gerrit/server/project/ProjectCache.java8
-rw-r--r--java/com/google/gerrit/server/project/ProjectCacheImpl.java15
-rw-r--r--java/com/google/gerrit/server/project/ProjectCacheWarmer.java2
-rw-r--r--java/com/google/gerrit/server/project/ProjectConfig.java55
-rw-r--r--java/com/google/gerrit/server/project/ProjectCreator.java8
-rw-r--r--java/com/google/gerrit/server/project/ProjectHierarchyIterator.java2
-rw-r--r--java/com/google/gerrit/server/project/ProjectJson.java6
-rw-r--r--java/com/google/gerrit/server/project/ProjectLevelConfig.java2
-rw-r--r--java/com/google/gerrit/server/project/ProjectNameLockManager.java2
-rw-r--r--java/com/google/gerrit/server/project/ProjectResource.java2
-rw-r--r--java/com/google/gerrit/server/project/ProjectState.java28
-rw-r--r--java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java11
-rw-r--r--java/com/google/gerrit/server/project/Reachable.java2
-rw-r--r--java/com/google/gerrit/server/project/RefFilter.java78
-rw-r--r--java/com/google/gerrit/server/project/RefPatternMatcher.java4
-rw-r--r--java/com/google/gerrit/server/project/RefUtil.java4
-rw-r--r--java/com/google/gerrit/server/project/RefValidationHelper.java4
-rw-r--r--java/com/google/gerrit/server/project/RemoveReviewerControl.java10
-rw-r--r--java/com/google/gerrit/server/project/SectionMatcher.java2
-rw-r--r--java/com/google/gerrit/server/project/SubmitRuleEvaluator.java118
-rw-r--r--java/com/google/gerrit/server/project/SubmitRuleOptions.java21
-rw-r--r--java/com/google/gerrit/server/project/SuggestParentCandidates.java2
-rw-r--r--java/com/google/gerrit/server/project/testing/BUILD6
-rw-r--r--java/com/google/gerrit/server/project/testing/TestLabels.java53
-rw-r--r--java/com/google/gerrit/server/project/testing/Util.java239
-rw-r--r--java/com/google/gerrit/server/query/account/AccountPredicates.java12
-rw-r--r--java/com/google/gerrit/server/query/account/AccountQueryBuilder.java4
-rw-r--r--java/com/google/gerrit/server/query/account/AccountQueryProcessor.java2
-rw-r--r--java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/account/InternalAccountQuery.java25
-rw-r--r--java/com/google/gerrit/server/query/change/AgePredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/AssigneePredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeData.java56
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeIdPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java43
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/CommentByPredicate.java6
-rw-r--r--java/com/google/gerrit/server/query/change/CommentPredicate.java11
-rw-r--r--java/com/google/gerrit/server/query/change/CommitPredicate.java14
-rw-r--r--java/com/google/gerrit/server/query/change/ConflictsPredicate.java22
-rw-r--r--java/com/google/gerrit/server/query/change/DestinationPredicate.java8
-rw-r--r--java/com/google/gerrit/server/query/change/EditByPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java12
-rw-r--r--java/com/google/gerrit/server/query/change/ExactTopicPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java7
-rw-r--r--java/com/google/gerrit/server/query/change/GroupPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/HasDraftByPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/HasStarsPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/InternalChangeQuery.java63
-rw-r--r--java/com/google/gerrit/server/query/change/IsReviewedPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/LabelPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/LegacyChangeIdStrPredicate.java (renamed from java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java)18
-rw-r--r--java/com/google/gerrit/server/query/change/MessagePredicate.java7
-rw-r--r--java/com/google/gerrit/server/query/change/OrSource.java45
-rw-r--r--java/com/google/gerrit/server/query/change/OutputStreamQuery.java4
-rw-r--r--java/com/google/gerrit/server/query/change/OwnerPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/OwnerinPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/ParentProjectPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/ProjectPredicate.java8
-rw-r--r--java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/RefPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/RegexProjectPredicate.java6
-rw-r--r--java/com/google/gerrit/server/query/change/RegexRefPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/RegexTopicPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/ReviewerPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/ReviewerinPredicate.java4
-rw-r--r--java/com/google/gerrit/server/query/change/SingleGroupUser.java2
-rw-r--r--java/com/google/gerrit/server/query/change/StarPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java2
-rw-r--r--java/com/google/gerrit/server/query/group/GroupPredicates.java4
-rw-r--r--java/com/google/gerrit/server/query/group/GroupQueryBuilder.java8
-rw-r--r--java/com/google/gerrit/server/query/group/InternalGroupQuery.java4
-rw-r--r--java/com/google/gerrit/server/query/project/ProjectPredicates.java2
-rw-r--r--java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java6
-rw-r--r--java/com/google/gerrit/server/quota/DefaultQuotaBackend.java6
-rw-r--r--java/com/google/gerrit/server/quota/QuotaBackend.java6
-rw-r--r--java/com/google/gerrit/server/quota/QuotaRequestContext.java6
-rw-r--r--java/com/google/gerrit/server/restapi/BUILD5
-rw-r--r--java/com/google/gerrit/server/restapi/access/ListAccess.java16
-rw-r--r--java/com/google/gerrit/server/restapi/account/AddSshKey.java2
-rw-r--r--java/com/google/gerrit/server/restapi/account/CreateAccount.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/CreateEmail.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java20
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteEmail.java2
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteSshKey.java2
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/EmailsCollection.java2
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetAccount.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetAgreements.java7
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetCapabilities.java13
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetDetail.java11
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java14
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetEditPreferences.java14
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetEmail.java7
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetEmails.java16
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetExternalIds.java7
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetGroups.java10
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetName.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetOAuthToken.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetPreferences.java9
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetSshKey.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetSshKeys.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetStatus.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetUsername.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java18
-rw-r--r--java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutAgreement.java4
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutHttpPassword.java4
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutName.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutPreferred.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutStatus.java4
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutUsername.java24
-rw-r--r--java/com/google/gerrit/server/restapi/account/QueryAccounts.java13
-rw-r--r--java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java16
-rw-r--r--java/com/google/gerrit/server/restapi/account/SetEditPreferences.java16
-rw-r--r--java/com/google/gerrit/server/restapi/account/SetPreferences.java20
-rw-r--r--java/com/google/gerrit/server/restapi/account/Stars.java36
-rw-r--r--java/com/google/gerrit/server/restapi/change/Abandon.java7
-rw-r--r--java/com/google/gerrit/server/restapi/change/ApplyFix.java8
-rw-r--r--java/com/google/gerrit/server/restapi/change/ChangeEdits.java41
-rw-r--r--java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java7
-rw-r--r--java/com/google/gerrit/server/restapi/change/ChangeMessages.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/ChangesCollection.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/CherryPick.java15
-rw-r--r--java/com/google/gerrit/server/restapi/change/CherryPickChange.java166
-rw-r--r--java/com/google/gerrit/server/restapi/change/CherryPickCommit.java19
-rw-r--r--java/com/google/gerrit/server/restapi/change/CommentJson.java8
-rw-r--r--java/com/google/gerrit/server/restapi/change/Comments.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/CreateChange.java63
-rw-r--r--java/com/google/gerrit/server/restapi/change/CreateDraftComment.java18
-rw-r--r--java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java27
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteAssignee.java11
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteChange.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java10
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteComment.java9
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java12
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeletePrivate.java2
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteReviewer.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteVote.java26
-rw-r--r--java/com/google/gerrit/server/restapi/change/DownloadContent.java12
-rw-r--r--java/com/google/gerrit/server/restapi/change/DraftComments.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/Files.java24
-rw-r--r--java/com/google/gerrit/server/restapi/change/Fixes.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetArchive.java12
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetAssignee.java2
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetBlame.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetChangeMessage.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetComment.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetCommit.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetContent.java43
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetDescription.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetDiff.java26
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetDraftComment.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetMergeList.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetPastAssignees.java2
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetPatch.java20
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetPureRevert.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetRelated.java23
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetReviewer.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetRobotComment.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetTopic.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/Index.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListChangeComments.java2
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java7
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListChangeMessages.java8
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java16
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListReviewers.java7
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListRevisionComments.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java18
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java7
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListRobotComments.java18
-rw-r--r--java/com/google/gerrit/server/restapi/change/Mergeable.java29
-rw-r--r--java/com/google/gerrit/server/restapi/change/Move.java53
-rw-r--r--java/com/google/gerrit/server/restapi/change/PostHashtags.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/PostPrivate.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/PostReview.java298
-rw-r--r--java/com/google/gerrit/server/restapi/change/PostReviewers.java9
-rw-r--r--java/com/google/gerrit/server/restapi/change/PreviewSubmit.java11
-rw-r--r--java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutAssignee.java21
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutDescription.java10
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutDraftComment.java15
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutMessage.java39
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutTopic.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/QueryChanges.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/Rebase.java38
-rw-r--r--java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java7
-rw-r--r--java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java33
-rw-r--r--java/com/google/gerrit/server/restapi/change/Restore.java13
-rw-r--r--java/com/google/gerrit/server/restapi/change/Revert.java74
-rw-r--r--java/com/google/gerrit/server/restapi/change/Reviewed.java8
-rw-r--r--java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java125
-rw-r--r--java/com/google/gerrit/server/restapi/change/Reviewers.java2
-rw-r--r--java/com/google/gerrit/server/restapi/change/ReviewersUtil.java106
-rw-r--r--java/com/google/gerrit/server/restapi/change/RevisionReviewers.java2
-rw-r--r--java/com/google/gerrit/server/restapi/change/Revisions.java33
-rw-r--r--java/com/google/gerrit/server/restapi/change/RobotComments.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/SetReadyForReview.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/Submit.java98
-rw-r--r--java/com/google/gerrit/server/restapi/change/SubmittedTogether.java12
-rw-r--r--java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java46
-rw-r--r--java/com/google/gerrit/server/restapi/change/TestSubmitRule.java46
-rw-r--r--java/com/google/gerrit/server/restapi/change/TestSubmitType.java61
-rw-r--r--java/com/google/gerrit/server/restapi/change/Votes.java10
-rw-r--r--java/com/google/gerrit/server/restapi/config/CheckConsistency.java5
-rw-r--r--java/com/google/gerrit/server/restapi/config/ConfirmEmail.java2
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetCache.java5
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java7
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetEditPreferences.java7
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetPreferences.java7
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetServerInfo.java9
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetSummary.java5
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetTask.java5
-rw-r--r--java/com/google/gerrit/server/restapi/config/IndexChanges.java2
-rw-r--r--java/com/google/gerrit/server/restapi/config/ListCaches.java16
-rw-r--r--java/com/google/gerrit/server/restapi/config/ListCapabilities.java12
-rw-r--r--java/com/google/gerrit/server/restapi/config/ListTasks.java11
-rw-r--r--java/com/google/gerrit/server/restapi/config/ReloadConfig.java16
-rw-r--r--java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java10
-rw-r--r--java/com/google/gerrit/server/restapi/config/SetEditPreferences.java10
-rw-r--r--java/com/google/gerrit/server/restapi/config/SetPreferences.java12
-rw-r--r--java/com/google/gerrit/server/restapi/config/TasksCollection.java2
-rw-r--r--java/com/google/gerrit/server/restapi/group/AddMembers.java27
-rw-r--r--java/com/google/gerrit/server/restapi/group/AddSubgroups.java19
-rw-r--r--java/com/google/gerrit/server/restapi/group/CreateGroup.java31
-rw-r--r--java/com/google/gerrit/server/restapi/group/DeleteMembers.java6
-rw-r--r--java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java2
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetAuditLog.java31
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetDescription.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetDetail.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetGroup.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetMember.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetName.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetOptions.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetOwner.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetSubgroup.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GroupJson.java2
-rw-r--r--java/com/google/gerrit/server/restapi/group/Index.java2
-rw-r--r--java/com/google/gerrit/server/restapi/group/ListGroups.java15
-rw-r--r--java/com/google/gerrit/server/restapi/group/ListMembers.java11
-rw-r--r--java/com/google/gerrit/server/restapi/group/ListSubgroups.java7
-rw-r--r--java/com/google/gerrit/server/restapi/group/PutDescription.java2
-rw-r--r--java/com/google/gerrit/server/restapi/group/PutName.java11
-rw-r--r--java/com/google/gerrit/server/restapi/group/PutOptions.java7
-rw-r--r--java/com/google/gerrit/server/restapi/group/PutOwner.java7
-rw-r--r--java/com/google/gerrit/server/restapi/group/QueryGroups.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/BanCommit.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/BranchesCollection.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/Check.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/CheckAccess.java19
-rw-r--r--java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java3
-rw-r--r--java/com/google/gerrit/server/restapi/project/CheckMergeability.java10
-rw-r--r--java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java7
-rw-r--r--java/com/google/gerrit/server/restapi/project/CommitsCollection.java30
-rw-r--r--java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateAccessChange.java15
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateBranch.java17
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateDashboard.java11
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateProject.java2
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateTag.java6
-rw-r--r--java/com/google/gerrit/server/restapi/project/DashboardsCollection.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteBranch.java10
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteBranches.java2
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteDashboard.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteRef.java8
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteTag.java2
-rw-r--r--java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java10
-rw-r--r--java/com/google/gerrit/server/restapi/project/GarbageCollect.java6
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetAccess.java17
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetBranch.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetChildProject.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetCommit.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetConfig.java22
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetContent.java6
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetDashboard.java20
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetDescription.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetHead.java7
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetParent.java7
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetProject.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetReflog.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetStatistics.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetTag.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/Index.java13
-rw-r--r--java/com/google/gerrit/server/restapi/project/IndexChanges.java2
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListBranches.java23
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListChildProjects.java14
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListDashboards.java11
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListProjects.java20
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListTags.java22
-rw-r--r--java/com/google/gerrit/server/restapi/project/ProjectNode.java2
-rw-r--r--java/com/google/gerrit/server/restapi/project/ProjectsCollection.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/PutBranch.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/PutConfig.java9
-rw-r--r--java/com/google/gerrit/server/restapi/project/PutDescription.java2
-rw-r--r--java/com/google/gerrit/server/restapi/project/PutTag.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/QueryProjects.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetAccess.java17
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetAccessUtil.java2
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetDashboard.java5
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java13
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetHead.java9
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetParent.java9
-rw-r--r--java/com/google/gerrit/server/rules/DefaultSubmitRule.java17
-rw-r--r--java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java29
-rw-r--r--java/com/google/gerrit/server/rules/PrologOptions.java60
-rw-r--r--java/com/google/gerrit/server/rules/PrologRule.java24
-rw-r--r--java/com/google/gerrit/server/rules/PrologRuleEvaluator.java96
-rw-r--r--java/com/google/gerrit/server/rules/RulesCache.java4
-rw-r--r--java/com/google/gerrit/server/rules/StoredValues.java12
-rw-r--r--java/com/google/gerrit/server/rules/SubmitRule.java14
-rw-r--r--java/com/google/gerrit/server/schema/AllProjectsCreator.java6
-rw-r--r--java/com/google/gerrit/server/schema/AllProjectsInput.java2
-rw-r--r--java/com/google/gerrit/server/schema/AllUsersCreator.java4
-rw-r--r--java/com/google/gerrit/server/schema/BUILD8
-rw-r--r--java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java69
-rw-r--r--java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java2
-rw-r--r--java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java25
-rw-r--r--java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java2
-rw-r--r--java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java2
-rw-r--r--java/com/google/gerrit/server/schema/SchemaCreatorImpl.java6
-rw-r--r--java/com/google/gerrit/server/schema/VersionedAccountPreferences.java4
-rw-r--r--java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java13
-rw-r--r--java/com/google/gerrit/server/schema/testing/BUILD4
-rw-r--r--java/com/google/gerrit/server/securestore/testing/BUILD2
-rw-r--r--java/com/google/gerrit/server/ssh/NoSshKeyCache.java2
-rw-r--r--java/com/google/gerrit/server/ssh/SshKeyCreator.java2
-rw-r--r--java/com/google/gerrit/server/submit/ChangeSet.java10
-rw-r--r--java/com/google/gerrit/server/submit/CherryPick.java8
-rw-r--r--java/com/google/gerrit/server/submit/CommitMergeStatus.java15
-rw-r--r--java/com/google/gerrit/server/submit/EmailMerge.java6
-rw-r--r--java/com/google/gerrit/server/submit/FastForwardOp.java2
-rw-r--r--java/com/google/gerrit/server/submit/GitModules.java20
-rw-r--r--java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java34
-rw-r--r--java/com/google/gerrit/server/submit/MergeOneOp.java2
-rw-r--r--java/com/google/gerrit/server/submit/MergeOp.java120
-rw-r--r--java/com/google/gerrit/server/submit/MergeOpRepoManager.java18
-rw-r--r--java/com/google/gerrit/server/submit/MergeSuperSet.java2
-rw-r--r--java/com/google/gerrit/server/submit/RebaseSorter.java4
-rw-r--r--java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java9
-rw-r--r--java/com/google/gerrit/server/submit/SubmitDryRun.java10
-rw-r--r--java/com/google/gerrit/server/submit/SubmitStrategy.java39
-rw-r--r--java/com/google/gerrit/server/submit/SubmitStrategyFactory.java4
-rw-r--r--java/com/google/gerrit/server/submit/SubmitStrategyListener.java4
-rw-r--r--java/com/google/gerrit/server/submit/SubmitStrategyOp.java81
-rw-r--r--java/com/google/gerrit/server/submit/SubmoduleOp.java114
-rw-r--r--java/com/google/gerrit/server/submit/TestHelperOp.java2
-rw-r--r--java/com/google/gerrit/server/update/BatchUpdate.java39
-rw-r--r--java/com/google/gerrit/server/update/BatchUpdateOp.java2
-rw-r--r--java/com/google/gerrit/server/update/ChangeContext.java4
-rw-r--r--java/com/google/gerrit/server/update/CommentsRejectedException.java47
-rw-r--r--java/com/google/gerrit/server/update/Context.java8
-rw-r--r--java/com/google/gerrit/server/update/InsertChangeOp.java2
-rw-r--r--java/com/google/gerrit/server/update/RepoView.java2
-rw-r--r--java/com/google/gerrit/server/update/RetryHelper.java124
-rw-r--r--java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java26
-rw-r--r--java/com/google/gerrit/server/update/RetryingRestModifyView.java24
-rw-r--r--java/com/google/gerrit/server/util/CommitMessageUtil.java6
-rw-r--r--java/com/google/gerrit/server/util/IdGenerator.java8
-rw-r--r--java/com/google/gerrit/server/util/MagicBranch.java25
-rw-r--r--java/com/google/gerrit/server/util/OneOffRequestContext.java2
-rw-r--r--java/com/google/gerrit/server/util/ReplicaUtil.java25
-rw-r--r--java/com/google/gerrit/server/util/RequestScopePropagator.java2
-rw-r--r--java/com/google/gerrit/server/util/git/BUILD4
-rw-r--r--java/com/google/gerrit/server/util/git/DelegateSystemReader.java68
-rw-r--r--java/com/google/gerrit/server/util/git/SubmoduleSectionParser.java18
-rw-r--r--java/com/google/gerrit/server/util/time/BUILD4
-rw-r--r--java/com/google/gerrit/server/util/time/TimeUtil.java55
-rw-r--r--java/com/google/gerrit/server/validators/AccountActivationValidationListener.java6
-rw-r--r--java/com/google/gerrit/server/validators/AssigneeValidationListener.java4
-rw-r--r--java/com/google/gerrit/server/validators/HashtagValidationListener.java2
-rw-r--r--java/com/google/gerrit/sshd/AbstractGitCommand.java13
-rw-r--r--java/com/google/gerrit/sshd/AliasCommand.java13
-rw-r--r--java/com/google/gerrit/sshd/BUILD8
-rw-r--r--java/com/google/gerrit/sshd/BaseCommand.java8
-rw-r--r--java/com/google/gerrit/sshd/ChangeArgumentParser.java6
-rw-r--r--java/com/google/gerrit/sshd/ChannelIdTrackingUnknownChannelReferenceHandler.java90
-rw-r--r--java/com/google/gerrit/sshd/CommandFactoryProvider.java25
-rw-r--r--java/com/google/gerrit/sshd/DatabasePubKeyAuth.java26
-rw-r--r--java/com/google/gerrit/sshd/DispatchCommand.java9
-rw-r--r--java/com/google/gerrit/sshd/GerritGSSAuthenticator.java6
-rw-r--r--java/com/google/gerrit/sshd/HostKeyProvider.java17
-rw-r--r--java/com/google/gerrit/sshd/InactiveAccountDisconnector.java60
-rw-r--r--java/com/google/gerrit/sshd/NoShell.java15
-rw-r--r--java/com/google/gerrit/sshd/SshCommand.java23
-rw-r--r--java/com/google/gerrit/sshd/SshDaemon.java66
-rw-r--r--java/com/google/gerrit/sshd/SshKeyCacheEntry.java2
-rw-r--r--java/com/google/gerrit/sshd/SshKeyCacheImpl.java5
-rw-r--r--java/com/google/gerrit/sshd/SshKeyCreatorImpl.java2
-rw-r--r--java/com/google/gerrit/sshd/SshModule.java4
-rw-r--r--java/com/google/gerrit/sshd/SshSession.java2
-rw-r--r--java/com/google/gerrit/sshd/SshUtil.java32
-rw-r--r--java/com/google/gerrit/sshd/SuExec.java11
-rw-r--r--java/com/google/gerrit/sshd/commands/BanCommitCommand.java2
-rw-r--r--java/com/google/gerrit/sshd/commands/CloseConnection.java51
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateAccountCommand.java2
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateGroupCommand.java13
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateProjectCommand.java4
-rw-r--r--java/com/google/gerrit/sshd/commands/DefaultCommandModule.java2
-rw-r--r--java/com/google/gerrit/sshd/commands/FlushCaches.java8
-rw-r--r--java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java2
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexChangesCommand.java2
-rw-r--r--java/com/google/gerrit/sshd/commands/ListGroupsCommand.java4
-rw-r--r--java/com/google/gerrit/sshd/commands/ListMembersCommand.java4
-rw-r--r--java/com/google/gerrit/sshd/commands/LsUserRefs.java8
-rw-r--r--java/com/google/gerrit/sshd/commands/PatchSetParser.java16
-rw-r--r--java/com/google/gerrit/sshd/commands/PluginLsCommand.java2
-rw-r--r--java/com/google/gerrit/sshd/commands/Receive.java42
-rw-r--r--java/com/google/gerrit/sshd/commands/ReviewCommand.java23
-rw-r--r--java/com/google/gerrit/sshd/commands/ScpCommand.java3
-rw-r--r--java/com/google/gerrit/sshd/commands/SetAccountCommand.java23
-rw-r--r--java/com/google/gerrit/sshd/commands/SetMembersCommand.java6
-rw-r--r--java/com/google/gerrit/sshd/commands/SetParentCommand.java13
-rw-r--r--java/com/google/gerrit/sshd/commands/SetReviewersCommand.java6
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowCaches.java59
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowConnections.java7
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowQueue.java9
-rw-r--r--java/com/google/gerrit/sshd/commands/StreamEvents.java91
-rw-r--r--java/com/google/gerrit/sshd/commands/Upload.java37
-rw-r--r--java/com/google/gerrit/sshd/commands/UploadArchive.java2
-rw-r--r--java/com/google/gerrit/testing/AssertableExecutorService.java65
-rw-r--r--java/com/google/gerrit/testing/BUILD34
-rw-r--r--java/com/google/gerrit/testing/ConfigSuite.java2
-rw-r--r--java/com/google/gerrit/testing/FakeAccountCache.java13
-rw-r--r--java/com/google/gerrit/testing/GerritServerTests.java2
-rw-r--r--java/com/google/gerrit/testing/GerritTestName.java (renamed from java/com/google/gerrit/testing/GerritBaseTests.java)23
-rw-r--r--java/com/google/gerrit/testing/InMemoryModule.java37
-rw-r--r--java/com/google/gerrit/testing/InMemoryRepositoryManager.java4
-rw-r--r--java/com/google/gerrit/testing/InMemoryTestEnvironment.java2
-rw-r--r--java/com/google/gerrit/testing/IndexConfig.java1
-rw-r--r--java/com/google/gerrit/testing/TestChanges.java36
-rw-r--r--java/com/google/gerrit/testing/TestCommentHelper.java107
-rw-r--r--java/com/google/gerrit/truth/BUILD1
-rw-r--r--java/com/google/gerrit/truth/CacheStatsSubject.java8
-rw-r--r--java/com/google/gerrit/truth/ConfigSubject.java129
-rw-r--r--java/com/google/gerrit/truth/ListSubject.java37
-rw-r--r--java/com/google/gerrit/truth/OptionalSubject.java41
-rw-r--r--java/com/google/gerrit/util/cli/CmdLineParser.java36
-rw-r--r--java/com/google/gerrit/util/cli/OptionUtil.java7
-rw-r--r--java/com/google/gwtorm/BUILD7
-rw-r--r--java/com/google/gwtorm/client/CompoundKey.java108
-rw-r--r--java/com/google/gwtorm/client/IntKey.java80
-rw-r--r--java/com/google/gwtorm/client/Key.java45
-rw-r--r--java/com/google/gwtorm/client/KeyUtil.java91
-rw-r--r--java/com/google/gwtorm/client/StringKey.java86
-rw-r--r--java/gerrit/AbstractCommitUserIdentityPredicate.java2
-rw-r--r--java/gerrit/BUILD5
-rw-r--r--java/gerrit/PRED__load_commit_labels_1.java9
-rw-r--r--java/gerrit/PRED_change_branch_1.java6
-rw-r--r--java/gerrit/PRED_change_owner_1.java2
-rw-r--r--java/gerrit/PRED_change_project_1.java2
-rw-r--r--java/gerrit/PRED_change_topic_1.java2
-rw-r--r--java/gerrit/PRED_commit_delta_4.java2
-rw-r--r--java/gerrit/PRED_commit_edits_2.java2
-rw-r--r--java/gerrit/PRED_commit_stats_3.java2
-rw-r--r--java/gerrit/PRED_uploader_1.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/BUILD3
-rw-r--r--javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/ProjectResetterTest.java180
-rw-r--r--javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java17
-rw-r--r--javatests/com/google/gerrit/acceptance/annotation/BUILD1
-rw-r--r--javatests/com/google/gerrit/acceptance/annotation/UseClockStepTest.java57
-rw-r--r--javatests/com/google/gerrit/acceptance/annotation/UseSystemTimeTest.java34
-rw-r--r--javatests/com/google/gerrit/acceptance/annotation/UseTimezoneTest.java35
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java2491
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java14
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/AccountListenersIT.java212
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java152
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java92
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java49
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java61
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java1684
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java58
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java9
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java52
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java26
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java290
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java49
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/RevertIT.java454
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java151
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java44
-rw-r--r--javatests/com/google/gerrit/acceptance/api/config/DiffPreferencesIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/api/config/EditPreferencesIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/api/config/GeneralPreferencesIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java49
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java341
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java28
-rw-r--r--javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java36
-rw-r--r--javatests/com/google/gerrit/acceptance/api/plugin/PluginLoaderIT.java42
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java107
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java41
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/CommitIT.java41
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java27
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java320
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java26
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java76
-rw-r--r--javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java7
-rw-r--r--javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java371
-rw-r--r--javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java142
-rw-r--r--javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java123
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java28
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java460
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java244
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java17
-rw-r--r--javatests/com/google/gerrit/acceptance/git/BUILD3
-rw-r--r--javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java16
-rw-r--r--javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java8
-rw-r--r--javatests/com/google/gerrit/acceptance/git/PushAccountIT.java778
-rw-r--r--javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java92
-rw-r--r--javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java833
-rw-r--r--javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java69
-rw-r--r--javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java210
-rw-r--r--javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java76
-rw-r--r--javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java28
-rw-r--r--javatests/com/google/gerrit/acceptance/pgm/InitIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/TraceIT.java827
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java26
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/BUILD7
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java27
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java31
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java338
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java21
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java225
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java15
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/auth/AuthenticationCheckIT.java35
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/auth/BUILD7
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java22
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java16
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java13
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java627
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java34
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java106
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java143
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java92
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/BUILD8
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeIncludedInIT.java15
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java83
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java29
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java132
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java89
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java63
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java171
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java39
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java31
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java229
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java26
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java104
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java51
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java13
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java215
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java98
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java19
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java257
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java148
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java19
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java19
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java110
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java16
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java52
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java120
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/BUILD10
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/CheckMergeabilityIT.java25
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java122
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java70
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java117
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java76
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java37
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java40
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java13
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java12
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java25
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java50
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java14
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java25
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java8
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/RefAssert.java9
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java130
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java76
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java103
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java223
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java90
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/server/config/BUILD7
-rw-r--r--javatests/com/google/gerrit/acceptance/server/config/GerritIsReplicaIT.java46
-rw-r--r--javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java334
-rw-r--r--javatests/com/google/gerrit/acceptance/server/event/EventPayloadIT.java64
-rw-r--r--javatests/com/google/gerrit/acceptance/server/git/receive/BUILD8
-rw-r--r--javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java136
-rw-r--r--javatests/com/google/gerrit/acceptance/server/httpd/BUILD7
-rw-r--r--javatests/com/google/gerrit/acceptance/server/httpd/HttpLogoutServletIT.java112
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/BUILD14
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java62
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java22
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java122
-rw-r--r--javatests/com/google/gerrit/acceptance/server/notedb/BUILD3
-rw-r--r--javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java26
-rw-r--r--javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java16
-rw-r--r--javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java148
-rw-r--r--javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java54
-rw-r--r--javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java28
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java81
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java99
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java84
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java79
-rw-r--r--javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java21
-rw-r--r--javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java35
-rw-r--r--javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java103
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/CustomIndexIT.java33
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/IndexIT.java3
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/PluginChangeFieldsIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/QueryIT.java5
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java48
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java123
-rw-r--r--javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java128
-rw-r--r--javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java537
-rw-r--r--javatests/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdateTest.java202
-rw-r--r--javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java38
-rw-r--r--javatests/com/google/gerrit/common/AutoValueTest.java3
-rw-r--r--javatests/com/google/gerrit/common/BUILD1
-rw-r--r--javatests/com/google/gerrit/common/data/AccessSectionTest.java47
-rw-r--r--javatests/com/google/gerrit/common/data/BUILD2
-rw-r--r--javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java3
-rw-r--r--javatests/com/google/gerrit/common/data/FilenameComparatorTest.java3
-rw-r--r--javatests/com/google/gerrit/common/data/GroupReferenceTest.java38
-rw-r--r--javatests/com/google/gerrit/common/data/LabelFunctionTest.java40
-rw-r--r--javatests/com/google/gerrit/common/data/LabelTypeTest.java3
-rw-r--r--javatests/com/google/gerrit/common/data/ParameterizedStringTest.java3
-rw-r--r--javatests/com/google/gerrit/common/data/PermissionRuleTest.java39
-rw-r--r--javatests/com/google/gerrit/common/data/PermissionTest.java55
-rw-r--r--javatests/com/google/gerrit/common/data/SubmitRecordTest.java3
-rw-r--r--javatests/com/google/gerrit/elasticsearch/BUILD7
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java12
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java3
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java2
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java30
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java2
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java2
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java2
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java30
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java2
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java2
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java16
-rw-r--r--javatests/com/google/gerrit/entities/AccountGroupTest.java (renamed from javatests/com/google/gerrit/reviewdb/client/AccountGroupTest.java)33
-rw-r--r--javatests/com/google/gerrit/entities/AccountTest.java (renamed from javatests/com/google/gerrit/reviewdb/client/AccountTest.java)13
-rw-r--r--javatests/com/google/gerrit/entities/BUILD (renamed from javatests/com/google/gerrit/reviewdb/client/BUILD)6
-rw-r--r--javatests/com/google/gerrit/entities/BranchTest.java39
-rw-r--r--javatests/com/google/gerrit/entities/ChangeTest.java (renamed from javatests/com/google/gerrit/reviewdb/client/ChangeTest.java)25
-rw-r--r--javatests/com/google/gerrit/entities/PatchSetApprovalTest.java (renamed from javatests/com/google/gerrit/reviewdb/client/PatchSetApprovalTest.java)17
-rw-r--r--javatests/com/google/gerrit/entities/PatchSetTest.java (renamed from javatests/com/google/gerrit/reviewdb/client/PatchSetTest.java)47
-rw-r--r--javatests/com/google/gerrit/entities/PatchTest.java54
-rw-r--r--javatests/com/google/gerrit/entities/ProjectTest.java39
-rw-r--r--javatests/com/google/gerrit/entities/RefNamesTest.java (renamed from javatests/com/google/gerrit/reviewdb/client/RefNamesTest.java)41
-rw-r--r--javatests/com/google/gerrit/entities/converter/AccountIdProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/AccountIdProtoConverterTest.java)13
-rw-r--r--javatests/com/google/gerrit/entities/converter/BUILD (renamed from javatests/com/google/gerrit/reviewdb/converter/BUILD)4
-rw-r--r--javatests/com/google/gerrit/entities/converter/BranchNameKeyProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverterTest.java)30
-rw-r--r--javatests/com/google/gerrit/entities/converter/ChangeIdProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverterTest.java)13
-rw-r--r--javatests/com/google/gerrit/entities/converter/ChangeKeyProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverterTest.java)13
-rw-r--r--javatests/com/google/gerrit/entities/converter/ChangeMessageKeyProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverterTest.java)14
-rw-r--r--javatests/com/google/gerrit/entities/converter/ChangeMessageProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverterTest.java)53
-rw-r--r--javatests/com/google/gerrit/entities/converter/ChangeProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/ChangeProtoConverterTest.java)102
-rw-r--r--javatests/com/google/gerrit/entities/converter/LabelIdProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/LabelIdProtoConverterTest.java)13
-rw-r--r--javatests/com/google/gerrit/entities/converter/ObjectIdProtoConverterTest.java75
-rw-r--r--javatests/com/google/gerrit/entities/converter/PatchSetApprovalKeyProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverterTest.java)34
-rw-r--r--javatests/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverterTest.java)115
-rw-r--r--javatests/com/google/gerrit/entities/converter/PatchSetIdProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverterTest.java)20
-rw-r--r--javatests/com/google/gerrit/entities/converter/PatchSetProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/PatchSetProtoConverterTest.java)106
-rw-r--r--javatests/com/google/gerrit/entities/converter/ProjectNameKeyProtoConverterTest.java (renamed from javatests/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverterTest.java)8
-rw-r--r--javatests/com/google/gerrit/extensions/BUILD1
-rw-r--r--javatests/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java3
-rw-r--r--javatests/com/google/gerrit/extensions/client/ListOptionTest.java3
-rw-r--r--javatests/com/google/gerrit/extensions/client/RangeTest.java3
-rw-r--r--javatests/com/google/gerrit/extensions/conditions/BUILD1
-rw-r--r--javatests/com/google/gerrit/extensions/conditions/BooleanConditionTest.java3
-rw-r--r--javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java3
-rw-r--r--javatests/com/google/gerrit/git/BUILD8
-rw-r--r--javatests/com/google/gerrit/git/ObjectIdsTest.java156
-rw-r--r--javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java3
-rw-r--r--javatests/com/google/gerrit/git/RefUpdateUtilTest.java24
-rw-r--r--javatests/com/google/gerrit/git/testing/BUILD1
-rw-r--r--javatests/com/google/gerrit/git/testing/PushResultSubjectTest.java3
-rw-r--r--javatests/com/google/gerrit/gpg/BUILD7
-rw-r--r--javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java7
-rw-r--r--javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java3
-rw-r--r--javatests/com/google/gerrit/gpg/PublicKeyStoreTest.java3
-rw-r--r--javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java3
-rw-r--r--javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java245
-rw-r--r--javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java75
-rw-r--r--javatests/com/google/gerrit/httpd/BUILD8
-rw-r--r--javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java3
-rw-r--r--javatests/com/google/gerrit/httpd/plugins/ContextMapperTest.java3
-rw-r--r--javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java77
-rw-r--r--javatests/com/google/gerrit/httpd/raw/IndexServletTest.java89
-rw-r--r--javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java8
-rw-r--r--javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java3
-rw-r--r--javatests/com/google/gerrit/httpd/restapi/ParameterParserTest.java32
-rw-r--r--javatests/com/google/gerrit/index/BUILD3
-rw-r--r--javatests/com/google/gerrit/index/SchemaUtilTest.java10
-rw-r--r--javatests/com/google/gerrit/index/query/AndPredicateTest.java23
-rw-r--r--javatests/com/google/gerrit/index/query/FieldPredicateTest.java9
-rw-r--r--javatests/com/google/gerrit/index/query/LazyDataSourceTest.java106
-rw-r--r--javatests/com/google/gerrit/index/query/NotPredicateTest.java49
-rw-r--r--javatests/com/google/gerrit/index/query/OrPredicateTest.java23
-rw-r--r--javatests/com/google/gerrit/index/query/PredicateTest.java3
-rw-r--r--javatests/com/google/gerrit/index/query/QueryBuilderTest.java3
-rw-r--r--javatests/com/google/gerrit/index/query/QueryParserTest.java12
-rw-r--r--javatests/com/google/gerrit/integration/git/BUILD6
-rw-r--r--javatests/com/google/gerrit/integration/git/GitProtocolV2IT.java382
-rw-r--r--javatests/com/google/gerrit/integration/git/UploadArchiveIT.java4
-rw-r--r--javatests/com/google/gerrit/json/BUILD3
-rw-r--r--javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java9
-rw-r--r--javatests/com/google/gerrit/json/JsonEnumMappingTest.java80
-rw-r--r--javatests/com/google/gerrit/json/SqlTimestampDeserializerTest.java33
-rw-r--r--javatests/com/google/gerrit/mail/AbstractParserTest.java11
-rw-r--r--javatests/com/google/gerrit/mail/AddressTest.java14
-rw-r--r--javatests/com/google/gerrit/mail/BUILD6
-rw-r--r--javatests/com/google/gerrit/mail/HtmlParserTest.java2
-rw-r--r--javatests/com/google/gerrit/mail/MailHeaderParserTest.java3
-rw-r--r--javatests/com/google/gerrit/mail/ParserUtilTest.java3
-rw-r--r--javatests/com/google/gerrit/mail/RawMailParserTest.java3
-rw-r--r--javatests/com/google/gerrit/mail/TextParserTest.java2
-rw-r--r--javatests/com/google/gerrit/mail/data/SimpleTextMessage.java2
-rw-r--r--javatests/com/google/gerrit/metrics/dropwizard/BUILD1
-rw-r--r--javatests/com/google/gerrit/metrics/dropwizard/DropWizardMetricMakerTest.java3
-rw-r--r--javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java25
-rw-r--r--javatests/com/google/gerrit/pgm/BUILD8
-rw-r--r--javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java10
-rw-r--r--javatests/com/google/gerrit/proto/ProtosTest.java59
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/RevIdProtoConverterTest.java66
-rw-r--r--javatests/com/google/gerrit/server/BUILD15
-rw-r--r--javatests/com/google/gerrit/server/ChangeUtilTest.java3
-rw-r--r--javatests/com/google/gerrit/server/IdentifiedUserTest.java12
-rw-r--r--javatests/com/google/gerrit/server/account/AccountResolverTest.java70
-rw-r--r--javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java13
-rw-r--r--javatests/com/google/gerrit/server/account/DestinationListTest.java35
-rw-r--r--javatests/com/google/gerrit/server/account/GroupUUIDTest.java5
-rw-r--r--javatests/com/google/gerrit/server/account/HashedPasswordTest.java19
-rw-r--r--javatests/com/google/gerrit/server/account/PreferencesTest.java50
-rw-r--r--javatests/com/google/gerrit/server/account/QueryListTest.java3
-rw-r--r--javatests/com/google/gerrit/server/account/StoredPreferencesTest.java66
-rw-r--r--javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java66
-rw-r--r--javatests/com/google/gerrit/server/account/WatchConfigTest.java19
-rw-r--r--javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java15
-rw-r--r--javatests/com/google/gerrit/server/account/externalids/ExternalIDCacheLoaderTest.java307
-rw-r--r--javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java6
-rw-r--r--javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java4
-rw-r--r--javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java10
-rw-r--r--javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java13
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/BUILD4
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/BooleanCacheSerializerTest.java13
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/CacheSerializerTest.java50
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java12
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java67
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java18
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java9
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java13
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java14
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.java3
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java27
-rw-r--r--javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java3
-rw-r--r--javatests/com/google/gerrit/server/change/HashtagsTest.java3
-rw-r--r--javatests/com/google/gerrit/server/change/IncludedInResolverTest.java5
-rw-r--r--javatests/com/google/gerrit/server/change/LabelNormalizerTest.java61
-rw-r--r--javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java3
-rw-r--r--javatests/com/google/gerrit/server/change/WalkSorterTest.java29
-rw-r--r--javatests/com/google/gerrit/server/config/AllProjectsNameTest.java40
-rw-r--r--javatests/com/google/gerrit/server/config/AllUsersNameTest.java40
-rw-r--r--javatests/com/google/gerrit/server/config/ConfigUtilTest.java26
-rw-r--r--javatests/com/google/gerrit/server/config/GitwebConfigTest.java3
-rw-r--r--javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java7
-rw-r--r--javatests/com/google/gerrit/server/config/RepositoryConfigTest.java59
-rw-r--r--javatests/com/google/gerrit/server/config/ScheduleConfigTest.java3
-rw-r--r--javatests/com/google/gerrit/server/config/SitePathsTest.java8
-rw-r--r--javatests/com/google/gerrit/server/edit/ChangeEditTest.java17
-rw-r--r--javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java21
-rw-r--r--javatests/com/google/gerrit/server/edit/tree/TreeModificationSubject.java14
-rw-r--r--javatests/com/google/gerrit/server/events/BUILD15
-rw-r--r--javatests/com/google/gerrit/server/events/EventDeserializerTest.java294
-rw-r--r--javatests/com/google/gerrit/server/events/EventJsonTest.java28
-rw-r--r--javatests/com/google/gerrit/server/events/EventTypesTest.java3
-rw-r--r--javatests/com/google/gerrit/server/extensions/webui/UiActionsTest.java17
-rw-r--r--javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java105
-rw-r--r--javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java18
-rw-r--r--javatests/com/google/gerrit/server/fixes/StringModifierTest.java28
-rw-r--r--javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java14
-rw-r--r--javatests/com/google/gerrit/server/git/GroupCollectorTest.java9
-rw-r--r--javatests/com/google/gerrit/server/git/JGitConfigTest.java84
-rw-r--r--javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java172
-rw-r--r--javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java56
-rw-r--r--javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.java5
-rw-r--r--javatests/com/google/gerrit/server/git/TagSetHolderTest.java9
-rw-r--r--javatests/com/google/gerrit/server/git/TagSetTest.java21
-rw-r--r--javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java7
-rw-r--r--javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java17
-rw-r--r--javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java67
-rw-r--r--javatests/com/google/gerrit/server/group/db/BUILD6
-rw-r--r--javatests/com/google/gerrit/server/group/db/GroupConfigTest.java342
-rw-r--r--javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java171
-rw-r--r--javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java20
-rw-r--r--javatests/com/google/gerrit/server/index/account/AccountFieldTest.java22
-rw-r--r--javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java22
-rw-r--r--javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java21
-rw-r--r--javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java7
-rw-r--r--javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java30
-rw-r--r--javatests/com/google/gerrit/server/ioutil/BUILD1
-rw-r--r--javatests/com/google/gerrit/server/ioutil/BasicSerializationTest.java3
-rw-r--r--javatests/com/google/gerrit/server/ioutil/ColumnFormatterTest.java3
-rw-r--r--javatests/com/google/gerrit/server/ioutil/HexFormatTest.java3
-rw-r--r--javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java5
-rw-r--r--javatests/com/google/gerrit/server/ioutil/StringUtilTest.java3
-rw-r--r--javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java68
-rw-r--r--javatests/com/google/gerrit/server/logging/MetadataTest.java37
-rw-r--r--javatests/com/google/gerrit/server/logging/MutableTagsTest.java13
-rw-r--r--javatests/com/google/gerrit/server/logging/PerformanceLogContextTest.java382
-rw-r--r--javatests/com/google/gerrit/server/logging/TraceContextTest.java20
-rw-r--r--javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java3
-rw-r--r--javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java3
-rw-r--r--javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java3
-rw-r--r--javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java87
-rw-r--r--javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java44
-rw-r--r--javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java8
-rw-r--r--javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java35
-rw-r--r--javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java269
-rw-r--r--javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java819
-rw-r--r--javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java12
-rw-r--r--javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java21
-rw-r--r--javatests/com/google/gerrit/server/notedb/DraftCommentNotesTest.java98
-rw-r--r--javatests/com/google/gerrit/server/notedb/IntBlobTest.java48
-rw-r--r--javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java132
-rw-r--r--javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java32
-rw-r--r--javatests/com/google/gerrit/server/patch/PatchListEntryTest.java5
-rw-r--r--javatests/com/google/gerrit/server/patch/PatchListTest.java5
-rw-r--r--javatests/com/google/gerrit/server/permissions/DefaultPermissionsMappingTest.java3
-rw-r--r--javatests/com/google/gerrit/server/permissions/PluginPermissionsUtilTest.java10
-rw-r--r--javatests/com/google/gerrit/server/permissions/RefControlTest.java1122
-rw-r--r--javatests/com/google/gerrit/server/plugins/AutoRegisterModulesTest.java29
-rw-r--r--javatests/com/google/gerrit/server/project/CommitsCollectionTest.java110
-rw-r--r--javatests/com/google/gerrit/server/project/GroupListTest.java41
-rw-r--r--javatests/com/google/gerrit/server/project/ProjectConfigTest.java165
-rw-r--r--javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java55
-rw-r--r--javatests/com/google/gerrit/server/query/account/BUILD7
-rw-r--r--javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java284
-rw-r--r--javatests/com/google/gerrit/server/query/change/BUILD38
-rw-r--r--javatests/com/google/gerrit/server/query/change/ChangeDataTest.java34
-rw-r--r--javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java3
-rw-r--r--javatests/com/google/gerrit/server/query/change/LuceneQueryChangesLatestIndexVersionTest.java26
-rw-r--r--javatests/com/google/gerrit/server/query/change/LuceneQueryChangesPreviousIndexVersionTest.java36
-rw-r--r--javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java34
-rw-r--r--javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java11
-rw-r--r--javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java30
-rw-r--r--javatests/com/google/gerrit/server/query/group/BUILD7
-rw-r--r--javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java38
-rw-r--r--javatests/com/google/gerrit/server/query/project/BUILD7
-rw-r--r--javatests/com/google/gerrit/server/rules/BUILD5
-rw-r--r--javatests/com/google/gerrit/server/rules/GerritCommonTest.java31
-rw-r--r--javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java31
-rw-r--r--javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java3
-rw-r--r--javatests/com/google/gerrit/server/rules/PrologTestCase.java3
-rw-r--r--javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java12
-rw-r--r--javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java45
-rw-r--r--javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionCheckTest.java79
-rw-r--r--javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java31
-rw-r--r--javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java3
-rw-r--r--javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java4
-rw-r--r--javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java5
-rw-r--r--javatests/com/google/gerrit/server/schema/TestGroup.java11
-rw-r--r--javatests/com/google/gerrit/server/update/BUILD14
-rw-r--r--javatests/com/google/gerrit/server/update/BatchUpdateTest.java259
-rw-r--r--javatests/com/google/gerrit/server/update/RepoViewTest.java7
-rw-r--r--javatests/com/google/gerrit/server/util/IdGeneratorTest.java3
-rw-r--r--javatests/com/google/gerrit/server/util/LabelVoteTest.java12
-rw-r--r--javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java3
-rw-r--r--javatests/com/google/gerrit/server/util/SocketUtilTest.java17
-rw-r--r--javatests/com/google/gerrit/server/util/git/BUILD8
-rw-r--r--javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java110
-rw-r--r--javatests/com/google/gerrit/sshd/BUILD1
-rw-r--r--javatests/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java3
-rw-r--r--javatests/com/google/gerrit/testing/GerritJUnitTest.java6
-rw-r--r--javatests/com/google/gerrit/testing/IndexVersionsTest.java9
-rw-r--r--javatests/com/google/gerrit/util/http/BUILD2
-rw-r--r--javatests/com/google/gerrit/util/http/RequestUtilTest.java3
-rw-r--r--javatests/com/google/gerrit/util/http/testutil/BUILD2
-rw-r--r--lib/BUILD64
-rw-r--r--lib/LICENSE-shadycss20
-rw-r--r--lib/easymock/BUILD26
-rw-r--r--lib/errorprone/BUILD8
-rw-r--r--lib/greenmail/BUILD2
-rw-r--r--lib/guava.bzl4
-rw-r--r--lib/jackson/BUILD1
-rw-r--r--lib/jgit/BUILD0
-rw-r--r--lib/jgit/jgit.bzl75
-rw-r--r--lib/jgit/org.eclipse.jgit.archive/BUILD10
-rw-r--r--lib/jgit/org.eclipse.jgit.http.server/BUILD10
-rw-r--r--lib/jgit/org.eclipse.jgit.junit/BUILD11
-rw-r--r--lib/jgit/org.eclipse.jgit/BUILD20
-rw-r--r--lib/js/bower_archives.bzl80
-rw-r--r--lib/js/bower_components.bzl18
-rw-r--r--lib/js/npm.bzl4
-rw-r--r--lib/log/BUILD2
-rw-r--r--lib/mina/BUILD1
-rw-r--r--lib/mockito/BUILD7
-rwxr-xr-xlib/nongoogle_test.sh8
-rw-r--r--lib/powermock/BUILD69
m---------modules/jgit0
-rw-r--r--package.json2
-rw-r--r--plugins/BUILD21
m---------plugins/delete-project0
m---------plugins/download-commands0
m---------plugins/gitiles0
m---------plugins/hooks0
m---------plugins/plugin-manager0
m---------plugins/replication0
m---------plugins/reviewnotes0
m---------plugins/singleusergroup0
m---------plugins/webhooks0
-rw-r--r--polygerrit-ui/BUILD2
-rw-r--r--polygerrit-ui/Polymer2.md15
-rw-r--r--polygerrit-ui/README.md91
-rw-r--r--polygerrit-ui/app/BUILD2
-rw-r--r--polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html6
-rw-r--r--polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/fire-behavior/fire-behavior.html55
-rw-r--r--polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html1
-rw-r--r--polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior.html (renamed from polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html)27
-rw-r--r--polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html (renamed from polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html)30
-rw-r--r--polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html11
-rw-r--r--polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html2
-rw-r--r--polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html6
-rw-r--r--polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html6
-rw-r--r--polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html2
-rw-r--r--polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html2
-rw-r--r--polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js4
-rw-r--r--polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html14
-rw-r--r--polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html12
-rw-r--r--polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html7
-rw-r--r--polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html9
-rw-r--r--polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html37
-rw-r--r--polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js40
-rw-r--r--polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html65
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js9
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html32
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html3
-rw-r--r--polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js5
-rw-r--r--polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html14
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html31
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.html12
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html19
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.html38
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html62
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.html20
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html4
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js2
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html15
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js3
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group/gr-group.html19
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group/gr-group.js11
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html20
-rw-r--r--polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html21
-rw-r--r--polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js22
-rw-r--r--polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html8
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.html34
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js2
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html3
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js2
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html8
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html19
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js15
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html8
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.html7
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js4
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js12
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html8
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html5
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js5
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html6
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html25
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js2
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html8
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.html29
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html8
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html24
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js9
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html9
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html22
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js8
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html21
-rw-r--r--polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html13
-rw-r--r--polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js27
-rw-r--r--polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html50
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html20
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js15
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html14
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html7
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js11
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html34
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html21
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js33
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html146
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.html12
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js4
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html6
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.html6
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.html2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js8
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html11
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js33
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html29
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.html2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.html2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html37
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js185
-rw-r--r--polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html274
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html22
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js106
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html82
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html11
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html54
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js57
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html8
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/test/plugin.html32
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.html27
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html110
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js183
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html149
-rw-r--r--polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html8
-rw-r--r--polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.html2
-rw-r--r--polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js11
-rw-r--r--polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html7
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html7
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js4
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html17
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html3
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js7
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html14
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html23
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js16
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html7
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js7
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html15
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js10
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html28
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js7
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js3
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html8
-rw-r--r--polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html15
-rw-r--r--polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js30
-rw-r--r--polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html9
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html20
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js14
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html8
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html52
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js86
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html52
-rw-r--r--polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html44
-rw-r--r--polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js16
-rw-r--r--polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html21
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html13
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js34
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html81
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js23
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html25
-rw-r--r--polygerrit-ui/app/elements/change/gr-message/gr-message.html56
-rw-r--r--polygerrit-ui/app/elements/change/gr-message/gr-message.js20
-rw-r--r--polygerrit-ui/app/elements/change/gr-message/gr-message_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html18
-rw-r--r--polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js44
-rw-r--r--polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html7
-rw-r--r--polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js27
-rw-r--r--polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html15
-rw-r--r--polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html54
-rw-r--r--polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js185
-rw-r--r--polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html380
-rw-r--r--polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html64
-rw-r--r--polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js21
-rw-r--r--polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html14
-rw-r--r--polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.html12
-rw-r--r--polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js24
-rw-r--r--polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html115
-rw-r--r--polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html7
-rw-r--r--polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js15
-rw-r--r--polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html27
-rw-r--r--polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html7
-rw-r--r--polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js8
-rw-r--r--polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html14
-rw-r--r--polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html2
-rw-r--r--polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html3
-rw-r--r--polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js2
-rw-r--r--polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html6
-rw-r--r--polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.html6
-rw-r--r--polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js1
-rw-r--r--polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html6
-rw-r--r--polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html17
-rw-r--r--polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js3
-rw-r--r--polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html44
-rw-r--r--polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js22
-rw-r--r--polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html100
-rw-r--r--polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html5
-rw-r--r--polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html6
-rw-r--r--polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js61
-rw-r--r--polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html78
-rw-r--r--polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html3
-rw-r--r--polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js124
-rw-r--r--polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html43
-rw-r--r--polygerrit-ui/app/elements/core/gr-router/gr-router.html5
-rw-r--r--polygerrit-ui/app/elements/core/gr-router/gr-router.js24
-rw-r--r--polygerrit-ui/app/elements/core/gr-router/gr-router_test.html16
-rw-r--r--polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html7
-rw-r--r--polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js3
-rw-r--r--polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html8
-rw-r--r--polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html4
-rw-r--r--polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js3
-rw-r--r--polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html6
-rw-r--r--polygerrit-ui/app/elements/custom-dark-theme_test.html101
-rw-r--r--polygerrit-ui/app/elements/custom-light-theme_test.html (renamed from polygerrit-ui/app/elements/gr-app-it_test.html)41
-rw-r--r--polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js1
-rw-r--r--polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.html2
-rw-r--r--polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js57
-rw-r--r--polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.html3
-rw-r--r--polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js30
-rw-r--r--polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js2
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.js3
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.js11
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.html205
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html101
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js79
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html103
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.html2
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js30
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html34
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html13
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js5
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html22
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-range-normalizer.js8
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html12
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js265
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html320
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.html7
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js1
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html8
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html13
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js9
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.html2
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js541
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html642
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.html27
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js35
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html44
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js149
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html145
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js171
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html118
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js46
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html134
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js102
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html149
-rw-r--r--polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html4
-rw-r--r--polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js22
-rw-r--r--polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html8
-rw-r--r--polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html2
-rw-r--r--polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js22
-rw-r--r--polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html42
-rw-r--r--polygerrit-ui/app/elements/diff/gr-ranged-comment-themes/gr-ranged-comment-theme.html30
-rw-r--r--polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html3
-rw-r--r--polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js2
-rw-r--r--polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.html2
-rw-r--r--polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js13
-rw-r--r--polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-syntax-themes/gr-syntax-theme.html1
-rw-r--r--polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.html3
-rw-r--r--polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js1
-rw-r--r--polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html8
-rw-r--r--polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.html4
-rw-r--r--polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js6
-rw-r--r--polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html8
-rw-r--r--polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html46
-rw-r--r--polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js16
-rw-r--r--polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html15
-rw-r--r--polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.html5
-rw-r--r--polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js6
-rw-r--r--polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html6
-rw-r--r--polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html19
-rw-r--r--polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js35
-rw-r--r--polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html8
-rw-r--r--polygerrit-ui/app/elements/gr-app-element.html238
-rw-r--r--polygerrit-ui/app/elements/gr-app-element.js468
-rw-r--r--polygerrit-ui/app/elements/gr-app.html249
-rw-r--r--polygerrit-ui/app/elements/gr-app.js454
-rw-r--r--polygerrit-ui/app/elements/gr-app_test.html46
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html8
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html7
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.html3
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js1
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.html9
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.html4
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js7
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html26
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js18
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html3
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js24
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html49
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js16
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html6
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js84
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html182
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js5
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html6
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html6
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html3
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html8
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html8
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.html18
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js83
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.html182
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html5
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.html2
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html8
-rw-r--r--polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html43
-rw-r--r--polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js13
-rw-r--r--polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html2
-rw-r--r--polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html14
-rw-r--r--polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.html11
-rw-r--r--polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js14
-rw-r--r--polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html34
-rw-r--r--polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html37
-rw-r--r--polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js18
-rw-r--r--polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.html39
-rw-r--r--polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html22
-rw-r--r--polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js3
-rw-r--r--polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html8
-rw-r--r--polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.html27
-rw-r--r--polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.html2
-rw-r--r--polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js5
-rw-r--r--polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html24
-rw-r--r--polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.html14
-rw-r--r--polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html10
-rw-r--r--polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html12
-rw-r--r--polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.html37
-rw-r--r--polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js7
-rw-r--r--polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html9
-rw-r--r--polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html40
-rw-r--r--polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js13
-rw-r--r--polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html4
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js2
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html2
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js2
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html50
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js5
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.html20
-rw-r--r--polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html23
-rw-r--r--polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js3
-rw-r--r--polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html7
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html31
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js5
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.html (renamed from polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html)11
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js101
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html113
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html12
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js27
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html62
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.html (renamed from polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html)13
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js (renamed from polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js)108
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html (renamed from polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html)199
-rw-r--r--polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html32
-rw-r--r--polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html12
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js25
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html10
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html29
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js28
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html40
-rw-r--r--polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html95
-rw-r--r--polygerrit-ui/app/elements/shared/gr-button/gr-button.html59
-rw-r--r--polygerrit-ui/app/elements/shared/gr-button/gr-button.js4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html41
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html10
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html23
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html35
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html9
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html91
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js56
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html7
-rw-r--r--polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js7
-rw-r--r--polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.html49
-rw-r--r--polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js3
-rw-r--r--polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html11
-rw-r--r--polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js39
-rw-r--r--polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js102
-rw-r--r--polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html306
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html11
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js7
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html46
-rw-r--r--polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html13
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html29
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js10
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html152
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html12
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js20
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html22
-rw-r--r--polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js21
-rw-r--r--polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html9
-rw-r--r--polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html26
-rw-r--r--polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js28
-rw-r--r--polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html18
-rw-r--r--polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html3
-rw-r--r--polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html10
-rw-r--r--polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js5
-rw-r--r--polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html11
-rw-r--r--polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.js13
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html31
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.js4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html10
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js112
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html78
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html16
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js196
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html100
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html13
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js60
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html158
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html11
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js397
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html502
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js13
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html7
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js359
-rw-r--r--polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.html29
-rw-r--r--polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js9
-rw-r--r--polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html7
-rw-r--r--polygerrit-ui/app/elements/shared/gr-label/gr-label.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-label/gr-label.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js3
-rw-r--r--polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js15
-rw-r--r--polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js17
-rw-r--r--polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html15
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html31
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js5
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html70
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js61
-rw-r--r--polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html20
-rw-r--r--polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js17
-rw-r--r--polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html13
-rw-r--r--polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html5
-rw-r--r--polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js682
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html405
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.js431
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html177
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-select/gr-select.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-select/gr-select.js14
-rw-r--r--polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html31
-rw-r--r--polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.html25
-rw-r--r--polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-storage/gr-storage.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js9
-rw-r--r--polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html23
-rw-r--r--polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js146
-rw-r--r--polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html165
-rw-r--r--polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html8
-rw-r--r--polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html6
-rw-r--r--polygerrit-ui/app/elements/shared/revision-info/revision-info.html10
-rw-r--r--polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html6
-rw-r--r--polygerrit-ui/app/elements/test/plugin.html48
-rw-r--r--polygerrit-ui/app/embed/embed.html7
-rw-r--r--polygerrit-ui/app/embed/embed_test.html8
-rw-r--r--polygerrit-ui/app/embed/gr-diff.html6
-rw-r--r--polygerrit-ui/app/embed/test.html6
-rwxr-xr-xpolygerrit-ui/app/embed_test.sh17
-rw-r--r--polygerrit-ui/app/gr-diff/gr-diff-root.html5
-rw-r--r--polygerrit-ui/app/rules.bzl9
-rw-r--r--polygerrit-ui/app/samples/bind-parameters.html2
-rw-r--r--polygerrit-ui/app/samples/coverage-plugin.html15
-rw-r--r--polygerrit-ui/app/samples/repo-command.html2
-rw-r--r--polygerrit-ui/app/samples/some-screen.html2
-rw-r--r--polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js71
-rw-r--r--polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html140
-rw-r--r--polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider.js46
-rw-r--r--polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html99
-rw-r--r--polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js47
-rw-r--r--polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html106
-rw-r--r--polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js114
-rw-r--r--polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html260
-rw-r--r--polygerrit-ui/app/scripts/util.js89
-rw-r--r--polygerrit-ui/app/styles/dashboard-header-styles.html2
-rw-r--r--polygerrit-ui/app/styles/fonts.css2
-rw-r--r--polygerrit-ui/app/styles/gr-change-list-styles.html20
-rw-r--r--polygerrit-ui/app/styles/gr-change-metadata-shared-styles.html48
-rw-r--r--polygerrit-ui/app/styles/gr-change-view-integration-shared-styles.html54
-rw-r--r--polygerrit-ui/app/styles/gr-form-styles.html30
-rw-r--r--polygerrit-ui/app/styles/gr-menu-page-styles.html14
-rw-r--r--polygerrit-ui/app/styles/gr-page-nav-styles.html14
-rw-r--r--polygerrit-ui/app/styles/gr-subpage-styles.html2
-rw-r--r--polygerrit-ui/app/styles/gr-table-styles.html12
-rw-r--r--polygerrit-ui/app/styles/gr-voting-styles.html1
-rw-r--r--polygerrit-ui/app/styles/main.css10
-rw-r--r--polygerrit-ui/app/styles/shared-styles.html70
-rw-r--r--polygerrit-ui/app/styles/themes/app-theme.html250
-rw-r--r--polygerrit-ui/app/styles/themes/dark-theme.html175
-rw-r--r--polygerrit-ui/app/template_test_srcs/template_test.js92
-rw-r--r--polygerrit-ui/app/test/common-test-setup.html10
-rw-r--r--polygerrit-ui/app/test/common-test-setup.js26
-rw-r--r--polygerrit-ui/app/test/index.html48
-rw-r--r--polygerrit-ui/app/types/custom-externs.js63
-rw-r--r--polygerrit-ui/app/types/types.js279
-rwxr-xr-xpolygerrit-ui/app/wct_test.sh3
-rw-r--r--polygerrit-ui/server.go54
-rw-r--r--prologtests/examples/BUILD15
-rw-r--r--prologtests/examples/README.md54
-rw-r--r--prologtests/examples/aosp_rules.pl148
-rwxr-xr-xprologtests/examples/dummy.sh6
-rw-r--r--prologtests/examples/load.pl26
-rw-r--r--prologtests/examples/rules.pl29
-rwxr-xr-xprologtests/examples/run.sh62
-rw-r--r--prologtests/examples/t1.pl20
-rw-r--r--prologtests/examples/t2.pl25
-rw-r--r--prologtests/examples/t3.pl69
-rw-r--r--prologtests/examples/utils.pl78
-rw-r--r--proto/cache.proto24
-rw-r--r--proto/entities.proto43
-rw-r--r--resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy24
-rwxr-xr-xresources/com/google/gerrit/pgm/init/gerrit.sh5
-rw-r--r--resources/com/google/gerrit/server/mail/Abandoned.soy2
-rw-r--r--resources/com/google/gerrit/server/mail/InboundEmailRejection.soy5
-rw-r--r--resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy7
-rw-r--r--tools/BUILD107
-rw-r--r--tools/bzl/javadoc.bzl17
-rw-r--r--tools/bzl/js.bzl16
-rw-r--r--tools/bzl/junit.bzl3
-rw-r--r--tools/bzl/maven_jar.bzl22
-rw-r--r--tools/bzl/plugin.bzl2
-rwxr-xr-xtools/eclipse/project.py28
-rw-r--r--tools/maven/gerrit-acceptance-framework_pom.xml2
-rw-r--r--tools/maven/gerrit-extension-api_pom.xml2
-rw-r--r--tools/maven/gerrit-plugin-api_pom.xml2
-rw-r--r--tools/maven/gerrit-war_pom.xml2
-rw-r--r--tools/nongoogle.bzl76
-rw-r--r--tools/workspace_status_release.py195
-rw-r--r--version.bzl2
2498 files changed, 59680 insertions, 35913 deletions
diff --git a/.gitmodules b/.gitmodules
index 6844f6a54a..9f67e77676 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,7 @@
+[submodule "modules/jgit"]
+ path = modules/jgit
+ url = ../jgit
+
[submodule "plugins/codemirror-editor"]
path = plugins/codemirror-editor
url = ../plugins/codemirror-editor
diff --git a/.gitreview b/.gitreview
index b3c37ad125..9df7aae035 100644
--- a/.gitreview
+++ b/.gitreview
@@ -2,4 +2,4 @@
host=gerrit-review.googlesource.com
scheme=https
project=gerrit.git
-defaultbranch=stable-3.0
+defaultbranch=stable-3.1
diff --git a/.mailmap b/.mailmap
index 38c2a5f2cc..721f3c0cb1 100644
--- a/.mailmap
+++ b/.mailmap
@@ -4,6 +4,7 @@ Alex Blewitt <alex.blewitt@gmail.com>
Alex Blewitt <alex.blewitt@gmail.com> <alex.blewitt@credit-suisse.com>
Alex Ryazantsev <alex.ryazantsev@gmail.com> alex <alex.ryazantsev@gmail.com>
Alex Ryazantsev <alex.ryazantsev@gmail.com> alex.ryazantsev <alex.ryazantsev@gmail.com>
+Alice Kober-Sotzek <aliceks@google.com> <aliceks@google.com>
Alexandre Philbert <alexandre.philbert@ericsson.com> <alexandre.philbert@hotmail.com>
Andrew Bonventre <andybons@chromium.org> <andybons@google.com>
Becky Siegel <beckysiegel@google.com> beckysiegel <beckysiegel@google.com>
@@ -14,6 +15,7 @@ Bruce Zu <bruce.zu.run10@gmail.com>
Carlos Eduardo Baldacin <carloseduardo.baldacin@sonyericsson.com> carloseduardo.baldacin <carloseduardo.baldacin@sonyericsson.com>
Chad Horohoe <chorohoe@wikimedia.org> <chadh@wikimedia.org>
Changcheng Xiao <xchangcheng@google.com> xchangcheng
+Cheng Ke <chengke.info@gmail.com> <chengke.info@gmail.com>
Dariusz Luksza <dluksza@collab.net> <dariusz@luksza.org>
Darrien Glasser <darrien@arista.com> darrien <darrien@arista.com>
Dave Borowitz <dborowitz@google.com> <dborowitz@google.com>
@@ -73,6 +75,7 @@ Rafael Rabelo Silva <rafael.rabelosilva@sonyericsson.com>
Réda Housni Alaoui <reda.housnialaoui@gmail.com> <alaoui.rda@gmail.com>
Richard Möhn <richard.moehn@posteo.de> <richard.moehn@fu-berlin.de>
Sam Saccone <samccone@google.com> <samccone@gmail.com>
+Sam Saccone <samccone@google.com> <samccone@google.com>
Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com>
Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com>
Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <zivkov@gmail.com>
diff --git a/.zuul.yaml b/.zuul.yaml
index 463bc51026..fe9dc800d8 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -6,7 +6,8 @@
This adds required projects needed for all Gerrit-related builds
(i.e., builds of Gerrit itself or plugins) on this branch.
- # No additional required projects required for this branch.
+ required-projects:
+ - jgit
- job:
name: gerrit-build
diff --git a/BUILD b/BUILD
index 3989a75bdd..c48b3b951e 100644
--- a/BUILD
+++ b/BUILD
@@ -4,16 +4,16 @@ load("//tools/bzl:pkg_war.bzl", "pkg_war")
package(default_visibility = ["//visibility:public"])
config_setting(
- name = "java9",
+ name = "java11",
values = {
- "java_toolchain": "@bazel_tools//tools/jdk:toolchain_java9",
+ "java_toolchain": "@bazel_tools//tools/jdk:toolchain_java11",
},
)
config_setting(
name = "java_next",
values = {
- "java_toolchain": "@bazel_tools//tools/jdk:toolchain_vanilla",
+ "java_toolchain": "//tools:toolchain_vanilla",
},
)
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 9f7c45782f..7961b7e955 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -727,13 +727,29 @@ changes, comments, code diffs, and Git access over SSH or HTTP.
A user must have this access granted in order to see a project, its
changes, or any of its data.
-This category has a special behavior, where the per-project ACL is
-evaluated before the global all projects ACL. If the per-project
-ACL has granted `Read` with 'DENY', and does not otherwise grant
-`Read` with 'ALLOW', then a `Read` in the all projects ACL
-is ignored. This behavior is useful to hide a handful of projects
+[[read_special_behaviors]]
+==== Special behaviors
+
+This category has multiple special behaviors:
+
+The per-project ACL is evaluated before the global all projects ACL.
+If the per-project ACL has granted `Read` with 'DENY', and does not
+otherwise grant `Read` with 'ALLOW', then a `Read` in the all projects
+ACL is ignored. This behavior is useful to hide a handful of projects
on an otherwise public server.
+You cannot grant `Read` on the `refs/tags/` namespace. Visibility to
+`refs/tags/` is derived from `Read` grants on refs namespaces other than
+`refs/tags/`, `refs/changes/`, and `refs/cache-automerge/` by finding
+tags reachable from those refs. For example, if a tag `refs/tags/test`
+points to a commit on the branch `refs/heads/master`, then allowing
+`Read` access to `refs/heads/master` would also allow access to
+`refs/tags/test`. If a tag is reachable from multiple refs, allowing
+access to any of those refs allows access to the tag.
+
+[[read_typical_usage]]
+==== Typical usage
+
For an open source, public Gerrit installation it is common to grant
`Read` to `Anonymous Users` in the `All-Projects` ACL, enabling
casual browsing of any project's changes, as well as fetching any
@@ -919,7 +935,7 @@ any changes.
Suggested access rights to grant:
-* xref:category_read[`Read`] on 'refs/heads/\*' and 'refs/tags/*'
+* xref:category_read[`Read`] on 'refs/heads/\*'
* xref:category_push[`Push`] to 'refs/for/refs/heads/*'
* link:config-labels.html#label_Code-Review[`Code-Review`] with range '-1' to '+1' for 'refs/heads/*'
@@ -947,7 +963,7 @@ exclusively.
Suggested access rights to grant:
-* xref:category_read[`Read`] on 'refs/heads/\*' and 'refs/tags/*'
+* xref:category_read[`Read`] on 'refs/heads/\*'
* xref:category_push[`Push`] to 'refs/for/refs/heads/*'
* xref:category_push_merge[`Push merge commit`] to 'refs/for/refs/heads/*'
* xref:category_forge_author[`Forge Author Identity`] to 'refs/heads/*'
@@ -1002,7 +1018,7 @@ and so the push right can be found under the optional section.
Suggested access rights to grant, that won't block changes:
-* xref:category_read[`Read`] on 'refs/heads/\*' and 'refs/tags/*'
+* xref:category_read[`Read`] on 'refs/heads/\*'
* link:config-labels.html#label_Code-Review[`Label: Code-Review`] with range '-1' to '0' for 'refs/heads/*'
* link:config-labels.html#label_Verified[`Label: Verified`] with range '0' to '+1' for 'refs/heads/*'
diff --git a/Documentation/backup.txt b/Documentation/backup.txt
index ed044ba665..7220c740f0 100644
--- a/Documentation/backup.txt
+++ b/Documentation/backup.txt
@@ -139,11 +139,12 @@ Use a file system supporting snapshots to keep the period where the gerrit
server is read-only or down as short as possible.
[#cons-backup-read-only]
-=== Turn master read-only for backup
+=== Turn primary server read-only for backup
-Make the server read-only before taking the backup. This means read-access
-is still available during backup, because only write operations have to be
-stopped to ensure consistency. This can be implemented using the
+Make the primary server handling write operations read-only before taking the
+backup. This means read-access is still available from replica servers during
+backup, because only write operations have to be stopped to ensure consistency.
+This can be implemented using the
link:https://gerrit.googlesource.com/plugins/readonly/[_readonly_] plugin.
[#cons-backup-replicate]
@@ -162,9 +163,9 @@ link:https://gerrit.googlesource.com/plugins/pull-replication[pull-replication p
Best you use a filesystem supporting snapshots to create a backup archive
of such a replica.
-For 2.x Gerrit versions also set up a database slave for the data stored in the
+For 2.x Gerrit versions also set up a database replica for the data stored in the
SQL database. If you are using 2.16 and migrated to _NoteDb_ you may consider to
-skip setting up a database slave, instead take a backup of the database which only
+skip setting up a database replica, instead take a backup of the database which only
contains the current schema version in this case.
In addition you need to ensure that no write operations are in flight before you
take the replica offline. Otherwise the database backup might be inconsistent
@@ -176,15 +177,16 @@ Replication of repository deletions can be switched off using the
link:https://gerrit.googlesource.com/plugins/replication/+/refs/heads/master/src/main/resources/Documentation/config.md[server option]
`remote.NAME.replicateProjectDeletions`.
-If you are using Gerrit slaves to offload read traffic you can use one of these
-slaves for creating backups.
+If you are using Gerrit replica to offload read traffic you can use one of these
+replica for creating backups.
[#cons-backup-offline]
-=== Take master offline for backup
+=== Take primary server offline for backup
-Shutdown the server before taking a backup. This is simple but means downtime
-for the users. Also crons and currently running cron jobs (e.g. repacking
-repositories) which affect the repositories may need to be shut down.
+Shut down the primary server handling write operations before taking a backup.
+This is simple but means downtime for the users. Also crons and currently
+running cron jobs (e.g. repacking repositories) which affect the repositories
+may need to be shut down.
[#backup-methods]
== Backup methods
diff --git a/Documentation/cmd-ls-projects.txt b/Documentation/cmd-ls-projects.txt
index fb35dc2227..1dd6720148 100644
--- a/Documentation/cmd-ls-projects.txt
+++ b/Documentation/cmd-ls-projects.txt
@@ -122,16 +122,16 @@ List visible projects:
$ ssh -p 29418 review.example.com gerrit ls-projects
platform/manifest
tools/gerrit
-tools/gwtorm
+tools/gitiles
$ curl http://review.example.com/projects/
platform/manifest
tools/gerrit
-tools/gwtorm
+tools/gitiles
$ curl http://review.example.com/projects/tools/
tools/gerrit
-tools/gwtorm
+tools/gitiles
----
Clone any project visible to the user:
diff --git a/Documentation/cmd-plugin-remove.txt b/Documentation/cmd-plugin-remove.txt
index f5fe56b2d6..012bf7b238 100644
--- a/Documentation/cmd-plugin-remove.txt
+++ b/Documentation/cmd-plugin-remove.txt
@@ -20,6 +20,7 @@ jars in the site path's `plugins` directory to `<plugin-jar-name>.disabled`.
* Caller must be a member of the privileged 'Administrators' group.
* link:config-gerrit.html#plugins.allowRemoteAdmin[plugins.allowRemoteAdmin]
must be enabled in `$site_path/etc/gerrit.config`.
+* Mandatory plugin cannot be disabled
== SCRIPTING
This command is intended to be used in scripts.
diff --git a/Documentation/config-accounts.txt b/Documentation/config-accounts.txt
index addada11fc..90150b1346 100644
--- a/Documentation/config-accounts.txt
+++ b/Documentation/config-accounts.txt
@@ -185,8 +185,6 @@ link:intro-user.html#preferences[general],
link:user-review-ui.html#diff-preferences[diff] and edit preferences:
----
-[general]
- showSiteHeader = false
[diff]
hideTopMenu = true
[edit]
@@ -412,10 +410,10 @@ stored in a local H2 database, but there is an extension point that
allows to plug in alternate implementations for storing the reviewed
flags. To replace the storage for reviewed flags a plugin needs to
implement the link:dev-plugins.html#account-patch-review-store[
-AccountPatchReviewStore] interface. E.g. to support a multi-master
-setup where reviewed flags should be replicated between the master
-nodes one could implement a store for the reviewed flags that is
-based on MySQL with replication.
+AccountPatchReviewStore] interface. E.g. to support a cluster setup with
+multiple primary servers handling write operations where reviewed flags should
+be replicated between the primary nodes one could implement a store for the
+reviewed flags that is based on MySQL with replication.
[[account-sequence]]
== Account Sequence
@@ -443,7 +441,7 @@ repository must be replicated:
* `refs/meta/external-ids` (external IDs)
* `refs/starred-changes/*` (star labels)
* `refs/sequences/accounts` (account sequence numbers, not needed for Gerrit
- slaves)
+ replicas)
GERRIT
------
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 3594626906..eb751ac919 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -833,7 +833,8 @@ changes for up to 1024 projects can be held in the cache.
+
Default value is 0 (disabled). It is disabled by default due to the fact
that change updates are not communicated between Gerrit servers. Hence
-this cache should be disabled in an multi-master/multi-slave setup.
+this cache should be disabled in a cluster setup using multiple primary
+or multiple replica nodes.
+
The cache should be flushed whenever the database changes table is modified
outside of Gerrit.
@@ -881,6 +882,12 @@ It is not recommended to change the in-memory attributes of this cache
away from the defaults. The cache may be persisted by setting
`diskLimit`, which is only recommended if cold start performance is
problematic.
++
+`external_ids_map` supports computing the new cache value based on a
+previously cached state. This applies modifications based on the Git
+diff and is almost always faster.
+`cache.external_ids_map.enablePartialReloads` turns this behavior on
+or off. The default is `true`.
cache `"git_tags"`::
+
@@ -1158,17 +1165,6 @@ Allow blame on side by side diff. If set to false, blame cannot be used.
+
Default is true.
-[[change.allowDrafts]]change.allowDrafts::
-+
-Legacy support for drafts workflow. If set to true, pushing a new change
-with draft option will create a private change. Pushing with draft option
-to an existing change will create change edit.
-+
-Enabling this option allows to push to the `refs/drafts/branch`. When
-disabled any push to `refs/drafts/branch` will be rejected.
-+
-Default is false.
-
[[change.api.excludeMergeableInChangeInfo]]change.api.excludeMergeableInChangeInfo::
+
If true, the mergeability bit in
@@ -1210,6 +1206,25 @@ in change tables and user dashboards.
+
By default 500.
+[[change.maxUpdates]]change.maxUpdates::
++
+Maximum number of updates to a change. Counts only updates to the main NoteDb
+meta ref; draft comments, robot comments, stars, etc. do not count towards the
+total.
++
+Many NoteDb operations require walking the entire change meta ref and loading
+its contents into memory, so changes with arbitrarily many updates may cause
+high CPU usage, memory pressure, persistent cache bloat, and other problems.
++
+The following operations are allowed even when a change is at the limit:
+* Abandon
+* Submit
+* Submit by push with `%submit`
+* Auto-close by pushing directly to the branch
+* Fix with link:rest-api-changes.html#fix-input[`expect_merged_as`]
++
+By default 1000.
+
[[change.move]]change.move::
+
Whether the link:rest-api-changes.html#move-change[Move Change] REST
@@ -1547,11 +1562,17 @@ line, separated by spaces.
Execute `java -jar gerrit.war daemon --help` to see all possible
options.
+[[container.replica]]container.replica::
++
+Used on Gerrit replica installations. If set to true the Gerrit JVM is
+called with the '--replica' switch, enabling replica mode. If no value is
+set (or any other value), Gerrit defaults to primary mode enabling write
+operations.
+
[[container.slave]]container.slave::
+
-Used on Gerrit slave installations. If set to true the Gerrit JVM is
-called with the '--slave' switch, enabling slave mode. If no value is
-set (or any other value), Gerrit defaults to master mode.
+Backward compatibility for link:#container.replica[`container.replica`]
+config setting.
[[container.startupTimeout]]container.startupTimeout::
+
@@ -1578,6 +1599,10 @@ If not set, defaults to '$site_path/bin/gerrit.war', or to
[[core]]
=== Section core
+[NOTE]
+The link:#jgitConfig[etc/jgit.config] file supports configuration of all JGit
+options.
+
[[core.packedGitWindowSize]]core.packedGitWindowSize::
+
Number of bytes of a pack file to load into memory in a single
@@ -2122,6 +2147,36 @@ A good name should be short but precise enough so that users can identify the in
+
Defaults to the full hostname of the Gerrit server.
+[[gerrit.experimentalRollingUpgrade]]gerrit.experimentalRollingUpgrade::
++
+Enable Gerrit rolling upgrade to the next version.
+For example if Gerrit v3.1 is version N (All-Projects:refs/meta/version=181)
+then its next version N+1 is v3.2 (All-Projects:refs/meta/version=183).
+Allow Gerrit to start even if the underlying schema version has been bumped to
+the next Gerrit version.
++
+Set to true if Gerrit is installed in
+[high-availability configuration](https://gerrit.googlesource.com/plugins/high-availability/+/refs/heads/master/README.md)
+during the rolling upgrade to the next version.
++
+By default false.
++
+The rolling upgrade process, at high level, assumes that Gerrit is installed
+on two or more nodes sharing the repositories over NFS. The upgrade is composed
+of the following steps:
++
+1. Set gerrit.experimentalRollingUpgrade to true on all Gerrit masters
+2. Set the first master unhealthy
+3. Shutdown the first master and [upgrade](install.html#init) to the next version
+4. Startup the first master, wait for the online reindex to complete
+5. Verify the the first master upgrade is successful and online reindex is complete
+6. Set the first master healthy
+7. Repeat steps 2. to 6. for all the other Gerrit nodes
++
+[WARNING]
+Rolling upgrade may or may not be possible depending on the changes introduced
+by the target version of the upgrade. Refer to the release notes and check whether
+the rolling upgrade is possible or not and the associated constraints.
[[gerrit.serverId]]gerrit.serverId::
+
@@ -2826,28 +2881,28 @@ Defaults to false.
==== Subsection index.scheduledIndexer
This section configures periodic indexing. Periodic indexing is
-intended to run only on slaves and only updates the group index.
-Replication to slaves happens on Git level so that Gerrit is not aware
-of incoming replication events. But slaves need an updated group index
+intended to run only on replicas and only updates the group index.
+Replication to replicas happens on Git level so that Gerrit is not aware
+of incoming replication events. But replicas need an updated group index
to resolve memberships of users for ACL validation. To keep the group
-index in slaves up-to-date the Gerrit slave periodically scans the
+index in replicas up-to-date the Gerrit replica periodically scans the
group refs in the All-Users repository to reindex groups if they are
stale.
The scheduled reindexer is not able to detect group deletions that
-happened while the slave was offline, but since group deletions are not
+happened while the replica was offline, but since group deletions are not
supported this should never happen. If nevertheless groups refs were
-deleted while a slave was offline a full offline link:pgm-reindex.html[
+deleted while a replica was offline a full offline link:pgm-reindex.html[
reindex] must be performed.
-This section is only used if Gerrit runs in slave mode, otherwise it is
+This section is only used if Gerrit runs in replica mode, otherwise it is
ignored.
[[index.scheduledIndexer.runOnStartup]]index.scheduledIndexer.runOnStartup::
+
Whether the scheduled indexer should run once immediately on startup.
-If set to `true` the slave startup is blocked until all stale groups
-were reindexed. Enabling this allows to prevent that slaves that were
+If set to `true` the replica startup is blocked until all stale groups
+were reindexed. Enabling this allows to prevent that replicas that were
offline for a longer period of time run with outdated group information
until the first scheduled indexing is done.
+
@@ -2857,7 +2912,7 @@ Defaults to `true`.
+
Whether the scheduled indexer is enabled. If the scheduled indexer is
disabled you must implement other means to keep the group index for the
-slave up-to-date (e.g. by using ElasticSearch for the indexes).
+replica up-to-date (e.g. by using ElasticSearch for the indexes).
+
Defaults to `true`.
@@ -3065,6 +3120,31 @@ Password used to connect to Elasticsearch.
+
Not set by default.
+[[event]]
+=== Section event
+
+[[event.payload.listChangeOptions]]events.payload.listChangeOptions::
++
+List of options that Gerrit applies when rendering the payload of an
+internal event. This is the same set of options that are documented
+link:rest-api-changes.html#query-options[here].
++
+Depending on the setup, these events might get serialized using stream
+events.
++
+This can be set to the set of minimal options that consumers of Gerrit's
+events need. A minimal set would be (`SKIP_MERGEABLE`,`SKIP_DIFFSTAT`).
++
+Every option that gets added here will have a performance impact. The
+general recommendation is therefore to set this to a minimal set of
+required options.
++
+Defaults to all available options minus `CHANGE_ACTIONS`,
+`CURRENT_ACTIONS` and `CHECK`. This is a rich default to make sure the
+config is backwards compatible with what the default was before the config
+was added.
+
+
[[ldap]]
=== Section ldap
@@ -3640,6 +3720,14 @@ Enable remote installation, enable and disable of plugins over HTTP
and SSH. If set to true Administrators can install new plugins
remotely, or disable existing plugins. Defaults to false.
+[[plugins.mandatory]]plugins.mandatory::
++
+List of mandatory plugins. If a plugin from this list does not load,
+Gerrit will fail to start.
++
+Disabling and restarting of a mandatory plugin is rejected, but reloading
+of a mandatory plugin is still possible.
+
[[plugins.jsLoadTimeout]]plugins.jsLoadTimeout::
+
Set the timeout value for loading JavaScript plugins in Gerrit UI.
@@ -3662,17 +3750,6 @@ Name of the groups of users that are allowed to execute
If no groups are added, any user will be allowed to execute
'receive-pack' on the server.
-[[receive.allowPushToRefsChanges]]receive.allowPushToRefsChanges::
-+
-If true, it is possible to push directly to a change using `refs/changes/`.
-The possibility to push to `refs/changes/` is deprecated and it might be
-removed in future releases.
-See link:user-upload.html#manual_replacement_mapping[Manual Replacement Mapping].
-+
-False means pushing to `refs/changes/` is prohibited.
-+
-Defaults to false.
-
[[receive.certNonceSeed]]receive.certNonceSeed::
+
If set to a non-empty value and server-side signed push validation is
@@ -3955,6 +4032,14 @@ attempt fails. `<operationType>` can be `ACCOUNT_UPDATE`, `CHANGE_UPDATE`,
Defaults to link:#retry.timeout[`retry.timeout`]; unit suffixes are supported,
and assumes milliseconds if not specified.
+[[retry.retryWithTraceOnFailure]]retry.retryWithTraceOnFailure::
++
+Whether Gerrit should automatically retry operations on failure with tracing
+enabled. The automatically generated traces can help with debugging. Please
+note that only some of the REST endpoints support automatic retry.
++
+By default this is set to false.
+
[[rules]]
=== Section rules
@@ -4371,8 +4456,8 @@ through slow networks, gits with huge amount of refs can benefit from
SSH-compression since git does not compress the ref announcement during
handshake.
+
-Compression can be especially useful when Gerrit slaves are being used
-for the larger clones and fetches and the master server mostly takes
+Compression can be especially useful when Gerrit replicas are being used
+for the larger clones and fetches and the primary server mostly takes
small receive-packs.
+
By default, `false`.
@@ -4728,6 +4813,72 @@ used for suggesting accounts when adding members to a group.
+
By default 0.
+[[tracing]]
+=== Section tracing
+
+[[tracing.performanceLogging]]tracing.performanceLogging::
++
+Whether performance logging is enabled.
++
+When performance logging is enabled, performance events for some
+operations are collected in memory while a request is running. At the
+end of the request the performance events are handed over to the
+link:dev-plugins.html#performance-logger[PerformanceLogger] plugins.
+This means if performance logging is enabled, the memory footprint of
+requests is slightly increased.
++
+This setting has no effect if no
+link:dev-plugins.html#performance-logger[PerformanceLogger] plugins are
+installed, because then performance logging is always disabled.
++
+By default, true.
+
+[[tracing.traceid]]
+==== Subsection tracing.<trace-id>
+
+There can be multiple `tracing.<trace-id>` subsections to configure
+automatic tracing of requests. To be traced a request must match all
+conditions of one `tracing.<trace-id>` subsection. The subsection name
+is used as trace ID. Using this trace ID administrators can find
+matching log entries.
+
+[[tracing.traceid.requestType]]tracing.<trace-id>.requestType::
++
+Type of request for which request tracing should be always enabled (can
+be `GIT_RECEIVE`, `GIT_UPLOAD`, `REST` and `SSH`).
++
+May be specified multiple times.
++
+By default, unset (all request types are matched).
+
+[[tracing.traceid.requestUriPattern]]tracing.<trace-id>.requestUriPattern::
++
+Regular expression to match request URIs for which request tracing
+should be always enabled. Request URIs are only available for REST
+requests. Request URIs never include the '/a' prefix.
++
+May be specified multiple times.
++
+By default, unset (all request URIs are matched).
+
+[[tracing.traceid.account]]tracing.<trace-id>.account::
++
+Account ID of an account for which request tracing should be always
+enabled.
++
+May be specified multiple times.
++
+By default, unset (all accounts are matched).
+
+[[tracing.traceid.projectPattern]]tracing.<trace-id>.projectPattern::
++
+Regular expression to match project names for which request tracing
+should be always enabled.
++
+May be specified multiple times.
++
+By default, unset (all projects are matched).
+
[[trackingid]]
=== Section trackingid
@@ -5081,6 +5232,36 @@ Other files support site customization.
+
* link:config-themes.html[Themes]
+[[jgitConfig]]
+== File `etc/jgit.config`
+
+Gerrit uses the `$site_path/etc/jgit.config` file instead of the
+system-wide and user-global Git configuration for its runtime JGit
+configuration.
+
+Sample `etc/jgit.config` file:
+----
+[core]
+ trustFolderStat = false
+----
+
+[[jgit-protocol]]
+=== Section protocol
+
+[[protocol.version]]protocol.version::
++
+If set, the server will accept requests from a client attempting to communicate
+using the specified protocol version. Otherwise communication falls back to version 0.
+If set in file `etc/jgit.config` this option will be used for all repositories of
+the site. It can be overridden for a given repository by configuring a different
+value in the repository's `config` file.
++
+Supported versions:
+0:: the original wire protocol.
+1:: the original wire protocol with the addition of a version string in the initial response from the server.
+2:: wire protocol version 2. Speeds up fetches from repositories with many refs by allowing the client
+ to specify which refs to list before the server lists them.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/config-groups.txt b/Documentation/config-groups.txt
index 4db4cb367c..afabbfce10 100644
--- a/Documentation/config-groups.txt
+++ b/Documentation/config-groups.txt
@@ -103,7 +103,7 @@ Group] reindex the affected groups manually.
== Replication
-In a replicated setting (eg. backups and or master/slave
-configurations), all refs in the `All-Users` project must be copied
-onto all replicas, including `refs/groups/*`, `refs/meta/group-names`
-and `refs/sequences/groups`.
+In a replicated setting (eg. backups and or primary/replica configurations), all
+refs in the `All-Users` project on primary nodes must be copied onto all
+replicas, including `refs/groups/*`, `refs/meta/group-names` and
+`refs/sequences/groups`.
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index ff43520962..193a96f6b7 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -262,6 +262,12 @@ the past and affect submission somehow.
Defaults to true.
+[[label_copyAnyScore]]
+=== `label.Label-Name.copyAnyScore`
+
+If true, any score for the label is copied forward when a new patch
+set is uploaded. Defaults to false.
+
[[label_copyMinScore]]
=== `label.Label-Name.copyMinScore`
@@ -297,12 +303,13 @@ Defaults to false.
[[label_copyAllScoresOnTrivialRebase]]
=== `label.Label-Name.copyAllScoresOnTrivialRebase`
-If true, all scores for the label are copied forward when a new patch
-set is uploaded that is a trivial rebase. A new patch set is considered
-as trivial rebase if the commit message is the same as in the previous
-patch set and if it has the same code delta as the previous patch set.
-This is the case if the change was rebased onto a different parent, or
-if the parent did not change at all.
+If true, all scores for the label are copied forward when a new patch set is
+uploaded that is a trivial rebase. A new patch set is considered to be trivial
+rebase if the commit message is the same as in the previous patch set and if it
+has the same diff (including context lines) as the previous patch set. This is
+the case if the change was rebased onto a different parent and that rebase did
+not require git to perform any conflict resolution, or if the parent did not
+change at all.
This can be used to enable sticky approvals, reducing turn-around for
trivial rebases prior to submitting a change.
@@ -313,13 +320,13 @@ Defaults to false.
[[label_copyAllScoresIfNoCodeChange]]
=== `label.Label-Name.copyAllScoresIfNoCodeChange`
-If true, all scores for the label are copied forward when a new patch
-set is uploaded that has the same parent tree as the previous patch
-set and the same code delta as the previous patch set. This means only
-the commit message is different. This can be used to enable sticky
-approvals on labels that only depend on the code, reducing turn-around
-if only the commit message is changed prior to submitting a change.
-For the Verified label that is optionally installed by the
+If true, all scores for the label are copied forward when a new patch set is
+uploaded that has the same parent tree as the previous patch set and the same
+code diff (including context lines) as the previous patch set. This means only
+the commit message is different; the change hasn't even been rebased. This can
+be used to enable sticky approvals on labels that only depend on the code,
+reducing turn-around if only the commit message is changed prior to submitting a
+change. For the Verified label that is optionally installed by the
link:pgm-init.html[init] site program this is enabled by default.
Defaults to false.
diff --git a/Documentation/config-robot-comments.txt b/Documentation/config-robot-comments.txt
index 00776972b3..f5185a4868 100644
--- a/Documentation/config-robot-comments.txt
+++ b/Documentation/config-robot-comments.txt
@@ -36,7 +36,6 @@ Robot comments can be dropped by deleting this ref.
== Limitations
-* Robot comments are not displayed in the web UI yet.
* There is no support for draft robot comments, but robot comments are
always published and visible to everyone who can see the change.
diff --git a/Documentation/config-validation.txt b/Documentation/config-validation.txt
index 24932a8d00..cb953c1288 100644
--- a/Documentation/config-validation.txt
+++ b/Documentation/config-validation.txt
@@ -122,6 +122,15 @@ Plugins implementing the `AccountActivationValidationListener` interface can
perform validation when an account is activated or deactivated via the Gerrit
REST API or the Java extension API.
+[[review-comment-validation]]
+== Review comment validation
+
+
+The `CommentValidator` interface allows plugins to validate all review comments,
+i.e. inline comments, file comments and the review message. This works for the
+REST API, for `git push` when `--publish-comments` is used and for comments sent
+via email.
+
GERRIT
------
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index 3f9ff2e84f..05e658d0a1 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -8,7 +8,8 @@ To build Gerrit from source, you need:
* A Linux or macOS system (Windows is not supported at this time)
* A JDK for Java 8|9|10|11|...
* Python 2 or 3
-* Node.js
+* link:https://github.com/nodesource/distributions/blob/master/README.md[Node.js (including npm)]
+* Bower (`sudo npm install -g bower`)
* link:https://docs.bazel.build/versions/master/install.html[Bazel] -launched with
link:https://github.com/bazelbuild/bazelisk[Bazelisk]
* Maven
@@ -29,17 +30,31 @@ seamlessly uses Bazelisk, which then runs the proper `bazel` binary version.
[[java]]
=== Java
-[[java-10]]
-==== Java 10 support
+==== MacOS
-Java 10 (and newer) is supported through vanilla java toolchain
+On MacOS, ensure that "Java for MacOS X 10.5 Update 4" (or higher) is installed
+and that `JAVA_HOME` is set to the
+link:install.html#Requirements[required Java version].
+
+Java installations can typically be found in
+"/System/Library/Frameworks/JavaVM.framework/Versions".
+
+To check the installed version of Java, open a terminal window and run:
+
+`java -version`
+
+[[java-12]]
+==== Java 12 support
+
+Java 12 (and newer) is supported through vanilla java toolchain
link:https://docs.bazel.build/versions/master/toolchains.html[Bazel option].
-To build Gerrit with Java 10 and newer, specify vanilla java toolchain and
+To build Gerrit with Java 12 and newer, specify vanilla java toolchain and
provide the path to JDK home:
```
$ bazel build \
- --define=ABSOLUTE_JAVABASE=<path-to-java-10> \
+ --define=ABSOLUTE_JAVABASE=<path-to-java-12> \
+ --javabase=@bazel_tools//tools/jdk:absolute_javabase \
--host_javabase=@bazel_tools//tools/jdk:absolute_javabase \
--host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \
--java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \
@@ -51,7 +66,7 @@ bazel test runs the test using the target javabase:
```
$ bazel test \
- --define=ABSOLUTE_JAVABASE=<path-to-java-10> \
+ --define=ABSOLUTE_JAVABASE=<path-to-java-12> \
--javabase=@bazel_tools//tools/jdk:absolute_javabase \
--host_javabase=@bazel_tools//tools/jdk:absolute_javabase \
--host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \
@@ -64,7 +79,7 @@ they could be added to ~/.bazelrc resource file:
```
$ cat << EOF > ~/.bazelrc
-build --define=ABSOLUTE_JAVABASE=<path-to-java-10>
+build --define=ABSOLUTE_JAVABASE=<path-to-java-12>
build --javabase=@bazel_tools//tools/jdk:absolute_javabase
build --host_javabase=@bazel_tools//tools/jdk:absolute_javabase
build --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla
@@ -75,36 +90,24 @@ EOF
Now, invoking Bazel with just `bazel build :release` would include
all those options.
-Note that the follow option must be added to `container.javaOptions`
-in `$gerrit_site/etc/gerrit.config` to run Gerrit with Java 10|11|...:
+[[java-11]]
+==== Java 11 support
-```
-[container]
- javaOptions = --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
-```
-
-[[java-9]]
-==== Java 9 support
-
-Java 9 is supported through alternative java toolchain
+Java 11 is supported through alternative java toolchain
link:https://docs.bazel.build/versions/master/toolchains.html[Bazel option].
-The Java 9 support is backwards compatible. Java 8 is still the default.
-To build Gerrit with Java 9, specify JDK 9 java toolchain:
+To build Gerrit with Java 11, specify JDK 11 java toolchain:
```
$ bazel build \
- --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java9 \
- --java_toolchain=@bazel_tools//tools/jdk:toolchain_java9 \
+ --host_javabase=@bazel_tools//tools/jdk:remote_jdk11 \
+ --javabase=@bazel_tools//tools/jdk:remote_jdk11 \
+ --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java11 \
+ --java_toolchain=@bazel_tools//tools/jdk:toolchain_java11 \
:release
```
-Note that the follow option must be added to `container.javaOptions`
-in `$gerrit_site/etc/gerrit.config` to run Gerrit with Java 9:
-
-```
-[container]
- javaOptions = --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
-```
+=== Node.js and npm packages
+See link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/README.md#installing-node_js-and-npm-packages[Installing Node.js and npm packages].
[[build]]
== Building on the Command Line
@@ -117,10 +120,6 @@ To build the Gerrit web application:
bazel build gerrit
----
-[NOTE]
-PolyGerrit UI may require additional tools (such as npm). Please read
-the polygerrit-ui/README.md for more info.
-
The output executable WAR will be placed in:
----
@@ -219,13 +218,6 @@ The output JAR file will be be placed in:
Note that when building an individual plugin, the `core.zip` package
is not regenerated.
-To build with all Error Prone warnings activated, run:
-
-----
- bazel build --java_toolchain //tools:error_prone_warnings_toolchain //...
-----
-
-
[[IDEs]]
== Using an IDE.
@@ -323,6 +315,12 @@ To exclude tests that require a Docker host:
bazel test --test_tag_filters=-docker //...
----
+To exclude tests that require very recent git client version:
+
+----
+ bazel test --test_tag_filters=-git-protocol-v2 //...
+----
+
To ignore cached test results:
----
@@ -343,6 +341,7 @@ The following values are currently supported for the group name:
* edit
* elastic
* git
+* git-protocol-v2
* git-upload-archive
* notedb
* pgm
@@ -419,16 +418,16 @@ repository has precedence.
== Building against unpublished Maven JARs
-To build against unpublished Maven JARs, like gwtorm or PrologCafe, the custom
-JARs must be installed in the local Maven repository (`mvn clean install`) and
+To build against unpublished Maven JARs, like PrologCafe, the custom JARs must
+be installed in the local Maven repository (`mvn clean install`) and
`maven_jar()` must be updated to point to the `MAVEN_LOCAL` Maven repository for
that artifact:
[source,python]
----
maven_jar(
- name = 'gwtorm',
- artifact = 'gwtorm:gwtorm:42',
+ name = 'prolog-runtime',
+ artifact = 'com.googlecode.prolog-cafe:prolog-runtime:42',
repository = MAVEN_LOCAL,
)
----
@@ -475,11 +474,18 @@ And corresponding WORKSPACE excerpt:
)
----
-[[consume-jgit-from-development-tree]]
+== Building against SNAPSHOT Maven JARs
+
+To build against SNAPSHOT Maven JARs, the complete SNAPSHOT version must be used:
-To consume the JGit dependency from the development tree, edit
-`lib/jgit/jgit.bzl` setting LOCAL_JGIT_REPO to a directory holding a
-JGit repository.
+[source,python]
+----
+ maven_jar(
+ name = "pac4j-core",
+ artifact = "org.pac4j:pac4j-core:3.5.0-SNAPSHOT-20190112.120241-16",
+ sha1 = "da2b1cb68a8f87bfd40813179abd368de9f3a746",
+ )
+----
[[bazel-local-caches]]
diff --git a/Documentation/dev-build-plugins.txt b/Documentation/dev-build-plugins.txt
index 60c6b9de0f..219861f6be 100644
--- a/Documentation/dev-build-plugins.txt
+++ b/Documentation/dev-build-plugins.txt
@@ -75,6 +75,12 @@ bazel-bin/plugins/<plugin-name>/<plugin-name>.jar
Some plugins describe their build process in `src/main/resources/Documentation/build.md`
file. It may worth checking.
+=== Error Prone checks
+
+Error Prone checks are enabled by default for core Gerrit and all core plugins. To
+enable the checks for custom plugins, add it in the `error_prone_packages` group
+in `tools/BUILD`.
+
=== Plugins with external dependencies ===
If the plugin has external dependencies, then they must be included from Gerrit's
diff --git a/Documentation/dev-cla.txt b/Documentation/dev-cla.txt
new file mode 100644
index 0000000000..5bc34a7737
--- /dev/null
+++ b/Documentation/dev-cla.txt
@@ -0,0 +1,26 @@
+= Gerrit Code Review - Contributor License Agreement
+
+In order to link:dev-community.html#how-to-contribute[contribute] to
+Gerrit a Contributor License Agreement must be completed before
+contributions are accepted. To view and accept the agreements do the
+following:
+
+. Click 'Sign In' at the top right corner of
+ https://gerrit-review.googlesource.com/
+. Sign In with your Google account
+. After signing in, go to the
+ link:https://gerrit-review.googlesource.com/#/settings/agreements[Agreements]
+ tab on the settings page
+. Click on 'New Contributor Agreement' and follow the instructions
+
+For reference, the actual agreements are linked below:
+
+* link:https://cla.developers.google.com/about/google-individual[Individual Agreement]
+* link:https://cla.developers.google.com/about/google-corporate[Corporate Agreement]
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-community.txt b/Documentation/dev-community.txt
new file mode 100644
index 0000000000..4fb025f3fd
--- /dev/null
+++ b/Documentation/dev-community.txt
@@ -0,0 +1,70 @@
+= Gerrit Community
+
+Gerrit is developed as a
+link:https://gerrit-review.googlesource.com/[self-hosting open source project]
+and very much welcomes contributions from anyone with a
+link:dev-cla.html[contributor's agreement] on file with the project.
+
+[[project-information]]
+== Project Information
+
+* link:https://www.gerritcodereview.com/[Project Homepage]
+* link:https://www.gerritcodereview.com/codeofconduct.html[Code of Conduct]
+* link:https://www.gerritcodereview.com/releases-readme.html[Release Versions]
+* link:https://gerrit.googlesource.com/gerrit[Source]
+* link:https://bugs.chromium.org/p/gerrit/issues/list[Issue Tracking]
+* link:https://gerrit-review.googlesource.com/q/status:open+project:gerrit[Change Review]
+* link:dev-design.html[System Design]
+* Processes
+** link:dev-processes.html#project-governance[Project Governance / Engineering Steering Committee]
+** link:#how-to-contribute[Contribution Processes]
+** link:dev-design-docs.html#review[Design doc reviews]
+** link:dev-processes.html#dev-in-stable-branches[Development in stable branches]
+** link:dev-processes.html#backporting[Backporting to stable branches]
+** link:dev-processes.html#security-issues[Dealing with Security Issues]
+** link:dev-processes.html#upgrading-libraries[Upgrading Libraries]
+** link:dev-processes.html#deprecating-features[Deprecating features]
+* Roles
+** link:dev-roles.html#supporter[Supporter]
+** link:dev-roles.html#contributor[Contributor]
+** link:dev-roles.html#maintainer[Maintainer]
+** link:dev-roles.html#mentor[Mentor]
+** link:dev-roles.html#steering-committee-member[Engineering Steering Committee Member]
+** link:dev-roles.html#community-manager[Community Manager]
+** link:dev-roles.html#release-manager[Release Manager]
+
+[[how-to-contribute]]
+== How to Contribute
+* link:dev-cla.html[Contributor License Agreement]
+* link:dev-contributing.html#contribution-processes[Contribution Processes]
+** link:dev-contributing.html#lightweight-contribution-process[Lightweight Contribution Process]
+** link:dev-contributing.html#design-driven-contribution-process[Design-Driven Contribution Process]
+** link:dev-contributing.html#mentorship[Mentorship]
+* link:dev-design-docs.html[Design Docs]
+* link:dev-readme.html[Developer Setup]
+* link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui[Polymer Frontend Developer Setup]
+* link:dev-crafting-changes.html[Crafting Changes]
+* link:dev-starter-projects.html[Starter Projects]
+
+[[plugin-development]]
+== Plugin Development
+* link:dev-plugins-lifecycle.html[Plugin Lifecycle]
+* link:dev-plugins.html[Developing Plugins]
+* link:dev-build-plugins.html[Building Gerrit plugins]
+* link:js-api.html[JavaScript Plugin API]
+* link:config-validation.html[Validation Interfaces]
+* link:dev-stars.html[Starring Changes]
+* link:quota.html[Quota Enforcement]
+
+[[maintainer]]
+== Maintainer
+* link:dev-release.html[Making a Gerrit Release]
+* link:dev-release-subproject.html[Making a Release of a Gerrit Subproject]
+* link:https://www.gerritcodereview.com/publishing.html[Publish Gerrit Homepage]
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index 0a7c1003eb..49f8fcf45e 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -1,410 +1,331 @@
= Gerrit Code Review - Contributing
-== Introduction
-Gerrit is developed as a
-link:https://gerrit-review.googlesource.com/[self-hosting open source project]
-and very much welcomes contributions from anyone with a contributor's
-agreement on file with the project.
-
+[[cla]]
== Contributor License Agreement
-A Contributor License Agreement must be completed before contributions
-are accepted. To view and accept the agreements do the following:
-
-* Click 'Sign In' at the top right corner of https://gerrit-review.googlesource.com/
-* Sign In with your Google account
-* After signing in, go to the
-link:https://gerrit-review.googlesource.com/#/settings/agreements[Agreements]
-tab on the settings page
-* Click 'New Contributor Agreement' and follow the instructions
-
-For reference, the actual agreements are linked below:
-* link:https://cla.developers.google.com/about/google-individual[Individual Agreement]
-* link:https://cla.developers.google.com/about/google-corporate[Corporate Agreement]
+In order to contribute to Gerrit a link:dev-cla.html[Contributor
+License Agreement] must be completed before contributions are accepted.
+
+[[contribution-processes]]
+== Contribution Processes
+
+The Gerrit project offers two contribution processes:
+
+* link:#lightweight-contribution-process[Lightweight Contribution
+ Process]
+* link:#design-driven-contribution-process[Design-Driven Contribution
+ Process]
+
+The lightweight contribution process has little overhead and is best
+suited for small contributions (documentation updates, bug fixes, small
+features). Contributions are pushed as changes and
+link:dev-roles.html#maintainer[maintainers] review them adhoc.
+
+For large/complex features, it is required to follow the
+link:#design-driven-contribution-process[design-driven contribution
+process] and specify the feature in a link:dev-design-docs.html[design
+doc] before starting with the implementation.
+
+If link:dev-roles.html#contributor[contributors] choose the lightweight
+contribution process and during the review it turns out that the feature
+is too large or complex, link:dev-roles.html#maintainer[maintainers] can
+require to follow the design-driven contribution process instead.
+
+If you are in doubt which process is right for you, consult the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+mailing list.
+
+These contribution processes apply to everyone who contributes code to
+the Gerrit project, including link:dev-roles.html#maintainer[maintainers].
+When reading this document, keep in mind that maintainers are also
+contributors when they contribute code.
+
+If a new feature is large or complex, it is often difficult to find a
+maintainer who can take the time that is needed for a thorough review,
+and who can help with getting the changes submitted. To avoid that this
+results in unpredictable long waiting times during code review,
+contributors can ask for link:#mentorship[mentor support]. A mentor
+helps with timely code reviews and technical guidance. Doing the
+implementation is still the responsibility of the contributor.
+
+[[comparison]]
+=== Quick Comparison
+
+[options="header"]
+|======================
+| |Lightweight Contribution Process|Design-Driven Contribution Process
+|Overhead|low (write good commit message, address review comments)|
+high (write link:dev-design-docs.html[design doc] and get it approved)
+|Technical Guidance|by reviewer|during the design review and by
+reviewer/mentor
+|Review |adhoc (when reviewer is available)|by a dedicated mentor (if
+a link:#mentorship[mentor] was assigned)
+|Caveats |features may get vetoed after the implementation was already
+done, maintainers may make the design-driven contribution process
+required if a change gets too complex/large|design doc must stay open
+for a minimum of 10 calendar days, a mentor may not be available
+immediately
+|Applicable to|documentation updates, bug fixes, small features|
+large/complex features
+|======================
+
+[[lightweight-contribution-process]]
+=== Lightweight Contribution Process
+
+The lightweight contribution process has little overhead and is best
+suited for small contributions (documentation updates, bug fixes, small
+features). For large/complex features the
+link:#design-driven-contribution-process[design-driven contribution
+process] is required.
-== Code Review
As Gerrit is a code review tool, naturally contributions will
be reviewed before they will get submitted to the code base. To
start your contribution, please make a git commit and upload it
-for review to the main Gerrit review server. To help speed up the
-review of your change, review these guidelines before submitting
-your change. You can view the pending Gerrit contributions and
-their statuses
+for review to the link:https://gerrit-review.googlesource.com/[
+gerrit-review.googlesource.com] Gerrit server. To help speed up the
+review of your change, review these link:dev-crafting-changes.html[
+guidelines] before submitting your change. You can view the pending
+Gerrit contributions and their statuses
link:https://gerrit-review.googlesource.com/#/q/status:open+project:gerrit[here].
Depending on the size of that list it might take a while for
your change to get reviewed. Naturally there are fewer
-approvers than contributors; so anything that you can do to
-ensure that your contribution will undergo fewer revisions
-will speed up the contribution process. This includes helping
-out reviewing other people's changes to relieve the load from
-the approvers. Even if you are not familiar with Gerrit's
-internals, it would be of great help if you can download, try
-out, and comment on new features. If it works as advertised,
-say so, and if you have the privileges to do so, go ahead
-and give it a +1 Verified. If you would find the feature
-useful, say so and give it a +1 code review.
-
-And finally, the quicker you respond to the comments of your
-reviewers, the quicker your change might get merged! Try to
-reply to every comment after submitting your new patch,
-particularly if you decided against making the suggested change.
-Reviewers don't want to seem like nags and pester you if you
-haven't replied or made a fix, so it helps them know if you
-missed it or decided against it.
-
-
-== Review Criteria
-
-Here are some hints as to what approvers may be looking for
-before approving or submitting changes to the Gerrit project.
-Let's start with the simple nit picky stuff. You are likely
-excited that your code works; help us share your excitement
-by not distracting us with the simple stuff. Thanks to Gerrit,
-problems are often highlighted and we find it hard to look
-beyond simple spacing issues. Blame it on our short attention
-spans, we really do want your code.
-
-
-[[commit-message]]
-=== Commit Message
-
-It is essential to have a good commit message if you want your
-change to be reviewed.
-
- * Keep lines no longer than 72 chars
- * Start with a short one line summary
- * Followed by a blank line
- * Followed by one or more explanatory paragraphs
- * Use the present tense (fix instead of fixed)
- * Use the past tense when describing the status before this commit
- * Include a `Bug: Issue <#>` line if fixing a Gerrit issue, or a
- `Feature: Issue <#>` line if implementing a feature request.
- * Include a `Change-Id` line
-
-=== Setting up Vim for Git commit message
-
-Git uses Vim as the default commit message editor. Put this into your
-`$HOME/.vimrc` file to configure Vim for Git commit message formatting
-and writing:
-
-====
- " Enable spell checking, which is not on by default for commit messages.
- au FileType gitcommit setlocal spell
-
- " Reset textwidth if you've previously overridden it.
- au FileType gitcommit setlocal textwidth=72
-====
-
-
-[[git_commit_settings]]
-=== A sample good Gerrit commit message:
-====
- Add sample commit message to guidelines doc
-
- The original patch set for the contributing guidelines doc did not
- include a sample commit message, this new patchset does. Hopefully this
- makes things a bit clearer since examples can sometimes help when
- explanations don't.
-
- Note that the body of this commit message can be several paragraphs, and
- that I word wrap it at 72 characters. Also note that I keep the summary
- line under 50 characters since it is often truncated by tools which
- display just the git summary.
-
- Bug: Issue 98765605
- Change-Id: Ic4a7c07eeb98cdeaf44e9d231a65a51f3fceae52
-====
-
-The `Change-Id` line is, as usual, created by a local git hook. To install it,
-simply copy it from the checkout and make it executable:
-
-====
- cp ./gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg .git/hooks/
- chmod +x .git/hooks/commit-msg
-====
-
-If you are working on core plugins, you will also need to install the
-same hook in the submodules:
-
-====
- export hook=$(pwd)/.git/hooks/commit-msg
- git submodule foreach 'cp -p "$hook" "$(git rev-parse --git-dir)/hooks/"'
-====
-
-
-To set up git's remote for easy pushing, run the following:
-
-====
- git remote add gerrit https://gerrit.googlesource.com/gerrit
-====
-
-The HTTPS access requires proper username and password; this can be obtained
-by clicking the 'Obtain Password' link on the
-link:https://gerrit-review.googlesource.com/#/settings/http-password[HTTP
-Password tab of the user settings page].
-
-Alternately, you may use the
-link:https://pypi.org/project/git-review/[git-review] tool to submit changes
-to Gerrit. If you do, it will set up the Change-Id hook and `gerrit` remote
-for you. You will still need to do the HTTP access step.
-
-[[style]]
-=== Style
-
-This project has a policy of Eclipse's warning free code. Eclipse
-configuration is added to git and we expect the changes to be
-warnings free.
-
-We do not ask you to use Eclipse for editing, obviously. We do ask you
-to provide Eclipse's warning free patches only. If for some reasons, you
-are not able to set up Eclipse and verify, that your patch hasn't
-introduced any new Eclipse warnings, mention this in a comment to your
-change, so that reviewers will do it for you. Yes, the way to go is to
-extend gerrit CI to take care of this, but it's not yet implemented.
-
-Gerrit follows the
-link:https://google.github.io/styleguide/javaguide.html[Google Java Style
-Guide].
-
-To format Java source code, Gerrit uses the
-link:https://github.com/google/google-java-format[`google-java-format`]
-tool (version 1.7), and to format Bazel BUILD, WORKSPACE and .bzl files the
-link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`]
-tool (version 3.5.0). Unused dependencies are found and removed using the
-link:https://github.com/bazelbuild/buildtools/tree/master/unused_deps[`unused_deps`]
-build tool, a sibling of `buildifier`.
-
-These tools automatically apply format according to the style guides; this
-streamlines code review by reducing the need for time-consuming, tedious,
-and contentious discussions about trivial issues like whitespace.
-
-You may download and run `google-java-format` on your own, or you may
-run `./tools/setup_gjf.sh` to download a local copy and set up a
-wrapper script. If you run your own copy, please use the same version,
-as there may be slight differences between versions.
-
-When to use `final` modifier and when not (in new code):
-
-Always:
-
- * final fields: marking fields as final forces them to be
- initialized in the constructor or at declaration
- * final static fields: clearly communicates the intent
- * to use final variables in inner anonymous classes
-
-Optional:
-
- * final classes: use when appropriate, e.g. API restriction
- * final methods: similar to final classes
-
-Never:
-
- * local variables: it clutters the code, and makes the code less
- readable. When copying old code to new location, finals should
- be removed
- * method parameters: similar to local variables
-
-=== Code Organization
-
-Do your best to organize classes and methods in a logical way.
-Here are some guidelines that Gerrit uses:
-
- * Ensure a standard copyright header is included at the top
- of any new files (copy it from another file, update the year).
- * Always place loggers first in your class!
- * Define any static interfaces next in your class.
- * Define non static interfaces after static interfaces in your
- class.
- * Next you should define static types, static members, and
- static methods, in decreasing order of visibility (public to private).
- * Finally instance types, instance members, then constructors,
- and then instance methods.
- * Some common exceptions are private helper static methods, which
- might appear near the instance methods which they help (but may
- also appear at the top).
- * Getters and setters for the same instance field should usually
- be near each other barring a good reason not to.
- * If you are using assisted injection, the factory for your class
- should be before the instance members.
- * Annotations should go before language keywords (`final`, `private`, etc) +
- Example: `@Assisted @Nullable final type varName`
- * Prefer to open multiple AutoCloseable resources in the same
- try-with-resources block instead of nesting the try-with-resources
- blocks and increasing the indentation level more than necessary.
-
-Wow that's a lot! But don't worry, you'll get the habit and most
-of the code is organized this way already; so if you pay attention
-to the class you are editing you will likely pick up on it.
-Naturally new classes are a little harder; you may want to come
-back and consult this section when creating them.
-
-
-=== Design
-
-Here are some design level objectives that you should keep in mind
-when coding:
-
- * Most client pages should perform only one RPC to load so as to
- keep latencies down. Exceptions would apply to RPCs which need
- to load large data sets if splitting them out will help the
- page load faster. Generally page loads are expected to complete
- in under 100ms. This will be the case for most operations,
- unless the data being fetched is not using Gerrit's caching
- infrastructure. In these slower cases, it is worth considering
- mitigating this longer load by using a second RPC to fill in
- this data after the page is displayed (or alternatively it might
- be worth proposing caching this data).
- * `@Inject` should be used on constructors, not on fields. The
- current exceptions are the ssh commands, these were implemented
- earlier in Gerrit's development. To stay consistent, new ssh
- commands should follow this older pattern; but eventually these
- should get converted to eliminate this exception.
- * Don't leave repository objects (git or schema) open. A .close()
- after every open should be placed in a finally{} block.
- * Don't leave UI components, which can cause new actions to occur,
- enabled during RPCs which update Git repositories, including NoteDb.
- This is to prevent people from submitting actions more than once
- when operating on slow links. If the action buttons are disabled,
- they cannot be resubmitted and the user can see that Gerrit is still
- busy.
- * ...and so is Guava (previously known as Google Collections).
-
-
-=== Tests
-
- * Tests for new code will greatly help your change get approved.
-
-
-=== Change Size/Number of Files Touched
-
-And finally, I probably cannot say enough about change sizes.
-Generally, smaller is better, hopefully within reason. Do try to
-keep things which will be confusing on their own together,
-especially if changing one without the other will break something!
-
- * If a new feature is implemented and it is a larger one, try to
- identify if it can be split into smaller logical features; when
- in doubt, err on the smaller side.
- * Separate bug fixes from feature improvements. The bug fix may
- be an easy candidate for approval and should not need to wait
- for new features to be approved. Also, combining the two makes
- reviewing harder since then there is no clear line between the
- fix and the feature.
- * Separate supporting refactoring from feature changes. If your
- new feature requires some refactoring, it helps to make the
- refactoring a separate change which your feature change
- depends on. This way, reviewers can easily review the refactor
- change as a something that should not alter the current
- functionality, and feel more confident they can more easily
- spot errors this way. Of course, it also makes it easier to
- test and locate later on if an unfortunate error does slip in.
- Lastly, by not having to see refactoring changes at the same
- time, it helps reviewers understand how your feature changes
- the current functionality.
- * Separate logical features into separate changes. This
- is often the hardest part. Here is an example: when adding a
- new ability, make separate changes for the UI and the ssh
- commands if possible.
- * Do only what the commit message describes. In other words, things which
- are not strictly related to the commit message shouldn't be part of
- a change, even trivial things like externalizing a string somewhere
- or fixing a typo. This helps keep `git blame` more useful in the future
- and it also makes `git revert` more useful.
- * Use topics to link your separate changes together.
-
-[[process]]
-== Process
-
-[[dev-in-stable-branches]]
-=== Development in stable branches
-
-As their name suggests stable branches are intended to be stable. This means that generally
-only bug-fixes should be done on stable branches, however this is not strictly enforced and
-exceptions may apply:
-
- * When a stable branch is initially created to prepare a new release the Gerrit community
- discusses on the mailing list if there are pending features which should still make it into the
- release. Those features are blocking the release and should be implemented on the stable
- branch before the first release candidate is created.
- * To stabilize the code before doing a major release several release candidates are created. Once
- the first release candidate was done no more features should be accepted on the stable branch.
- If more features are found to be required they should be discussed with the Gerrit maintainers
- and should only be allowed if the risk of breaking things is considered to be low.
- * Once a major release is done only bug-fixes and documentation updates should be done on the
- stable branch. These updates will be included in the next minor release.
- * For minor releases new features are only acceptable if they are important to the Gerrit
- community, if they are backwards compatible and the risk of breaking things is low and if there
- are no objections from the Gerrit community.
- * In cases of doubt it's the responsibility of the release maintainer to evaluate the risk of new
- features and make a decision based on these rules and opinions from the Gerrit community.
- * The older a stable branch is the more stable it should be. This means old stable branches
- should only receive bug-fixes that are either important or low risk. Security fixes, including
- security updates for third party dependencies, are always considered as important and hence can
- always be done on stable branches.
-
-=== Backporting to stable branches
-
-From time to time bug fix releases are made for existing stable branches.
-
-Developers concerned with stable branches are encouraged to backport or push fixes to these
-branches, even if no new release is planned. Backporting features is only possible in compliance
-with the rules link:#dev-in-stable-branches[above].
-
-Fixes that are known to be needed for a particular release should be pushed for review on that
-release's stable branch. They will then be included into the master branch when the stable branch
-is merged back.
-
-=== Finding starter projects to work on
-
-We have created a
-link:https://bugs.chromium.org/p/gerrit/issues/list?can=2&q=label%3AStarterProject[StarterProject]
-category in the issue tracker and try to assign easy hack projects to it. If in
-doubt, do not hesitate to ask on the developer
-link:https://groups.google.com/forum/#!forum/repo-discuss[mailing list].
-
-=== Upgrading Libraries
-
-Gerrit's library dependencies should only be upgraded if the new version contains
-something we need in Gerrit. This includes new features, API changes as well as bug
-or security fixes.
-An exception to this rule is that right after a new Gerrit release was branched
-off, all libraries should be upgraded to the latest version to prevent Gerrit
-from falling behind. Doing those upgrades should conclude at the latest two
-months after the branch was cut. This should happen on the master branch to ensure
-that they are vetted long enough before they go into a release and we can be sure
-that the update doesn't introduce a regression.
-
-[[deprecating-features]]
-=== Deprecating features
-
-Gerrit should be as stable as possible and we aim to add only features that last.
-However, sometimes we are required to deprecate and remove features to be able
-to move forward with the project and keep the code-base clean. The following process
-should serve as a guideline on how to deprecate functionality in Gerrit. Its purpose
-is that we have a structured process for deprecation that users, administrators and
-developers can agree and rely on.
-
-General process:
-
- * Make sure that the feature (e.g. a field on the API) is not needed anymore or blocks
- further development or improvement. If in doubt, consult the mailing list.
- * If you can provide a schema migration that moves users to a comparable feature, do
- so and stop here.
- * Mark the feature as deprecated in the documentation and release notes.
- * If possible, mark the feature deprecated in any user-visible interface. For example,
- if you are deprecating a Git push option, add a message to the Git response if
- the user provided the option informing them about deprecation.
- * Annotate the code with `@Deprecated` and `@RemoveAfter(x.xx)` if applicable.
- Alternatively, use `// DEPRECATED, remove after x.xx` (where x.xx is the version
- number that has to be branched off before removing the feature)
- * Gate the feature behind a config that is off by default (forcing admins to turn
- the deprecated feature on explicitly).
- * After the next release was branched off, remove any code that backed the feature.
-
-You can optionally consult the mailing list to ask if there are users of the feature you
-wish to deprecate. If there are no major users, you can remove the feature without
-following this process and without the grace period of one release.
+link:dev-roles.html#maintainer[maintainers], that can approve changes,
+than link:dev-roles.html#contributor[contributors];
+so anything that you can do to ensure that your contribution will undergo fewer
+revisions will speed up the contribution process. This includes
+helping out reviewing other people's changes to relieve the load from
+the maintainers. Even if you are not familiar with Gerrit's internals,
+it would be of great help if you can download, try out, and comment on
+new features. If it works as advertised, say so, and if you have the
+privileges to do so, go ahead and give it a `+1 Verified`. If you
+would find the feature useful, say so and give it a `+1 Code Review`.
+
+And finally, the quicker you respond to the comments of your reviewers,
+the quicker your change might get merged! Try to reply to every
+comment after submitting your new patch, particularly if you decided
+against making the suggested change. Reviewers don't want to seem like
+nags and pester you if you haven't replied or made a fix, so it helps
+them know if you missed it or decided against it.
+
+[[design-driven-contribution-process]]
+=== Design-driven Contribution Process
+
+The design-driven contribution process applies to large/complex
+features.
+
+For large/complex features it is important to:
+
+* agree on the functionality and scope before spending too much time
+ on the implementation
+* ensure that they are in line with Gerrit's project scope and vision
+* ensure that they are well aligned with other features
+* think about possibilities how the feature could be evolved over time
+
+This is why for large/complex features it is required to describe the
+feature in a link:dev-design-docs.html[design doc] and get it approved
+by the link:dev-processes.html#steering-committee[steering committee],
+before starting the implementation.
+
+The design-driven contribution process has the following steps:
+
+* A link:dev-roles.html#contributor[contributor]
+ link:dev-design-docs.html#propose[proposes] a new feature by uploading
+ a change with a link:dev-design-docs.html[design doc].
+* The design doc is link:dev-design-docs.html#review[reviewed]
+ by interested parties from the community. The design review is public
+ and everyone can comment and raise concerns.
+* Design docs should stay open for a minimum of 10 calendar days so
+ that everyone has a fair chance to join the review.
+* Within 14 calendar days the contributor should hear back from the
+ link:dev-processes.html#steering-committee[steering committee]
+ whether the proposed feature is in scope of the project and if it can
+ be accepted.
+* To be submitted, the design doc needs to be approved by the
+ link:dev-processes.html#steering-committee[steering committee].
+* After the design was approved, the implementation is done by pushing
+ changes for review, see link:#lightweight-contribution-process[
+ lightweight contribution process]. Changes that are associated with
+ a design should all share a common hashtag. The contributor is the
+ main driver of the implementation and responsible that it is done.
+ Others from the Gerrit community are usually much welcome to help
+ with the implementation.
+
+In order to be accepted/submitted, it is not necessary that the design
+doc fully specifies all the details, but the idea of the feature and
+how it fits into Gerrit should be sufficiently clear (judged by the
+steering committee). Contributors are expected to keep the design doc
+updated and fill in gaps while they go forward with the implementation.
+We expect that implementing the feature and updating the design doc
+will be an iterative process.
+
+While the design doc is still in review, contributors may already start
+with the implementation (e.g. do some prototyping to demonstrate parts
+of the proposed design), but those changes should not be submitted
+while the design wasn't approved yet.
+
+By approving a design, the steering committee commits to:
+
+* Accepting the feature when it is implemented.
+* Supporting the feature by assigning a link:dev-roles.html#mentor[
+ mentor] (if requested, see link:#mentorship[mentorship]).
+
+If the implementation of a feature gets stuck and it's unclear whether
+the feature gets fully done, it should be discussed with the steering
+committee how to proceed. If the contributor cannot commit to finish
+the implementation and no other contributor can take over, changes that
+have already been submitted for the feature might get reverted so that
+there is no unused or half-finished code in the code base.
+
+For contributors, the design-driven contribution process has the
+following advantages:
+
+* By writing a design doc, the feature gets more attention. During the
+ design review, feedback from various sides can be collected, which
+ likely leads to improvements of the feature.
+* Once a design was approved by the
+ link:dev-processes.html#steering-committee[steering committee],
+ the contributor can be almost certain that the feature will be accepted.
+ Hence, there is only a low risk to invest into implementing a feature
+ and see it being rejected later during the code review, as it can
+ happen with the lightweight contribution process.
+* The contributor can link:#mentorship[get a dedicated mentor assigned]
+ who provides timely reviews and serves as a contact person for
+ technical questions and discussing details of the design.
+
+[[mentorship]]
+== Mentorship
+
+For features for which a link:dev-design-docs.html[design]
+has been approved (see link:#design-driven-contribution-process[design-driven
+contribution process]), contributors can gain the support of a mentor
+if they are committed to implement the feature.
+
+A link:dev-roles.html#mentor[mentor] helps with:
+
+* doing timely reviews
+* providing technical guidance during code reviews
+* discussing details of the design
+* ensuring that the quality standards are met (well documented,
+ sufficient test coverage, backwards compatible etc.)
+
+A feature can have more than one mentor. To be able to deliver the
+promised support, at least one of the mentors must be a
+link:dev-roles.html#maintainer[maintainer].
+
+Mentors are assigned by the link:dev-processes.html#steering-committee[
+steering committee]. To gain a mentor, ask for a
+mentor in the link:dev-design-doc-template.html#implementation-plan[Implementation
+Plan] section of the design doc or ask the steering
+committee after the design has been approved.
+
+Mentors may not be available immediately. In this case, the steering
+committee should include the approved feature into the roadmap or
+prioritize it in the backlog. This way, it is transparent for the
+contributor when they can expect to be able to work on the feature with
+mentor support.
+
+Once the implementation phase starts, the contributor is expected to do
+the implementation in a timely manner.
+
+For every mentorship, the end must be clearly defined. The design doc
+must specify:
+
+* a maximum time frame for the mentorship, after which the mentorship
+ automatically ends, even if the feature is not done yet
+* done criteria that define when the feature is done and the mentorship
+ ends
+
+If a feature is not finished in time, it should be discussed with the
+steering committee how to proceed. If the contributor cannot commit to
+finish the implementation in time and no other contributor can take
+over, changes that have already been submitted for the feature might
+get reverted so that there is no unused or half-finished code in the
+code base.
+
+[[esc-dd-evaluation]]
+== How the ESC evaluates design documents
+This section describes how the ESC evaluates design documents. It’s
+meant as a guideline rather than being prescriptive for both ESC
+members and contributors.
+
+=== General Process
+As part of the design process, the ESC makes a final decision if a
+design gets to be implemented. If there are multiple alternative
+solutions, the ESC will decide which solution can be implemented.
+
+The ESC should wait until all contributors had the chance to
+voice their opinion in review comments or by proposing alternative
+solutions. Due to the infrequent ESC meetings (every 2-4 weeks)
+the ESC might discuss documents in cases where the discussion is
+already advanced far enough, but not make a decision yet. In this
+case, contributors can still voice concerns or discuss alternatives.
+The decision can be at the next meeting or via email in between
+meetings.
+
+=== Evaluation
+Product/Vision fit
+
+Q: `Do we believe this feature belongs to Gerrit Code Review use-cases?`
+
+* Yes: Customizable dashboards
+* No: UI for managing an LDAP server
+
+Q: `Is the proposed solution aligned with Gerrit’s vision?`
+
+* Yes: Showing comments of older patch sets in newer patch sets to
+ keep track (core code review)
+* No: Implement a bug tracker in Gerrit (not core code review).
+
+=== Impact
+Q: `Will the new feature have a measurable, positive impact?`
+
+* Yes: Increased productivity, faster/smoother workflow, etc.
+* Yes: Better latency, more reliable system.
+* No: Unclear impact or lacking metrics to measure.
+
+=== Complexity
+Q: `Can we support/maintain this feature once it is in Gerrit?`
+
+* Yes: Code will fit into codebase, be well tested, easy to
+ understand.
+* No: Will add code or a workflow that is hard to understand
+ and easy to misinterpret.
+
+Q: `Is the proposed feature or rework adding unnecessary complexity?`
+
+* Yes: Adding a dependency on a well-supported library.
+* No: Adding a dependency on a library that is not widely used
+ or not actively maintained.
+
+=== Core vs. Plugin decision
+Q: `Would this fit better in a plugin?`
+
+* Yes:The proposed feature or rework is an implementation (e.g. Lucene
+ is an index implementation) of a generic concept that others
+ might want to implement differently.
+* Yes: The proposed feature or rework is very specific to a custom setup.
+* No: The proposed feature or rework is applicable to a wider user
+ base.
+* No: The proposed feature or rework is a `core code review feature`.
+
+=== Commitment
+Q: `Is someone willing to implement it?` (this question is
+especially important when reviewers propose alternative designs
+to the author’s own solution).
+
+* Yes: The author or someone else commits to implementing the
+ proposed solution.
+* Yes: If a mentorship is required, a mentor is willing to help.
+* No: Unclear ownership, mentorship or implementation plan.
+
+=== Community
+Q: `If in doubt, is there a substantial benefit to a long-standing
+community member with many users?`
+
+* The community shapes the future of Gerrit as a product. In
+ cases of doubt, the ESC can be more generous with long-standing
+ community members compared to `drive-by` contributions.
GERRIT
------
diff --git a/Documentation/dev-crafting-changes.txt b/Documentation/dev-crafting-changes.txt
new file mode 100644
index 0000000000..d71d227c7b
--- /dev/null
+++ b/Documentation/dev-crafting-changes.txt
@@ -0,0 +1,297 @@
+= Gerrit Code Review - Crafting Changes
+
+Here are some hints as to what approvers may be looking for
+before approving or submitting changes to the Gerrit project.
+Let's start with the simple nit picky stuff. You are likely
+excited that your code works; help us share your excitement
+by not distracting us with the simple stuff. Thanks to Gerrit,
+problems are often highlighted and we find it hard to look
+beyond simple spacing issues. Blame it on our short attention
+spans, we really do want your code.
+
+
+[[branch]]
+== Branch
+
+Gerrit provides support for more than one version, which naturally
+raises the question of which branch you should start your contribution
+on. There are no hard and fast rules, but below we try to outline some
+guidelines:
+
+* Genuinely new and/or disruptive features, should generally start on
+ `master`. Also consider submitting a
+ link:dev-design-docs.html[design doc] beforehand to allow discussion
+ by the ESC and the community.
+* Improvements of existing features should also generally go into
+ `master`. But we understand that if you cannot run `master`, it
+ might take a while until you could benefit from it. In that case,
+ start on the newest `stable-*` branch that you can run.
+* Bug-fixes should generally at least cover the oldest affected and
+ still supported version. If you're affected and run an even older
+ version, you're welcome to upload to that older version, even if
+ it is no longer officially supported, bearing in mind that
+ verification and release may happen only once merged upstream.
+
+Regardless of the above, changes might get moved to a different branch
+before being submitted or might get cherry-picked/re-merged to a
+different branch even after they've landed.
+
+For each of the above items, you'll find ad-hoc exceptions. The point
+is: We'd much rather see your code and fixes than not see them.
+
+
+[[commit-message]]
+== Commit Message
+
+It is essential to have a good commit message if you want your
+change to be reviewed.
+
+ * Keep lines no longer than 72 chars
+ * Start with a short one line summary
+ * Followed by a blank line
+ * Followed by one or more explanatory paragraphs
+ * Use the present tense (fix instead of fixed)
+ * Use the past tense when describing the status before this commit
+ * Include a `Bug: Issue <#>` line if fixing a Gerrit issue, or a
+ `Feature: Issue <#>` line if implementing a feature request.
+ * Include a `Change-Id` line
+
+[[vim-setup]]
+=== Setting up Vim for Git commit message
+
+Git uses Vim as the default commit message editor. Put this into your
+`$HOME/.vimrc` file to configure Vim for Git commit message formatting
+and writing:
+
+====
+ " Enable spell checking, which is not on by default for commit messages.
+ au FileType gitcommit setlocal spell
+
+ " Reset textwidth if you've previously overridden it.
+ au FileType gitcommit setlocal textwidth=72
+====
+
+
+[[git-commit-settings]]
+=== A sample good Gerrit commit message:
+====
+ Add sample commit message to guidelines doc
+
+ The original patch set for the contributing guidelines doc did not
+ include a sample commit message, this new patchset does. Hopefully this
+ makes things a bit clearer since examples can sometimes help when
+ explanations don't.
+
+ Note that the body of this commit message can be several paragraphs, and
+ that I word wrap it at 72 characters. Also note that I keep the summary
+ line under 50 characters since it is often truncated by tools which
+ display just the git summary.
+
+ Bug: Issue 98765605
+ Change-Id: Ic4a7c07eeb98cdeaf44e9d231a65a51f3fceae52
+====
+
+The `Change-Id` line is, as usual, created by a local git hook. To install it,
+simply copy it from the checkout and make it executable:
+
+====
+ cp ./gerrit-server/src/main/resources/com/google/gerrit/server/tools/root/hooks/commit-msg .git/hooks/
+ chmod +x .git/hooks/commit-msg
+====
+
+If you are working on core plugins, you will also need to install the
+same hook in the submodules:
+
+====
+ export hook=$(pwd)/.git/hooks/commit-msg
+ git submodule foreach 'cp -p "$hook" "$(git rev-parse --git-dir)/hooks/"'
+====
+
+
+To set up git's remote for easy pushing, run the following:
+
+====
+ git remote add gerrit https://gerrit.googlesource.com/gerrit
+====
+
+The HTTPS access requires proper username and password; this can be obtained
+by clicking the 'Obtain Password' link on the
+link:https://gerrit-review.googlesource.com/#/settings/http-password[HTTP
+Password tab of the user settings page].
+
+Alternately, you may use the
+link:https://pypi.org/project/git-review/[git-review] tool to submit changes
+to Gerrit. If you do, it will set up the Change-Id hook and `gerrit` remote
+for you. You will still need to do the HTTP access step.
+
+[[style]]
+== Style
+
+This project has a policy of Eclipse's warning free code. Eclipse
+configuration is added to git and we expect the changes to be
+warnings free.
+
+We do not ask you to use Eclipse for editing, obviously. We do ask you
+to provide Eclipse's warning free patches only. If for some reasons, you
+are not able to set up Eclipse and verify, that your patch hasn't
+introduced any new Eclipse warnings, mention this in a comment to your
+change, so that reviewers will do it for you. Yes, the way to go is to
+extend gerrit CI to take care of this, but it's not yet implemented.
+
+Gerrit follows the
+link:https://google.github.io/styleguide/javaguide.html[Google Java Style
+Guide].
+
+To format Java source code, Gerrit uses the
+link:https://github.com/google/google-java-format[`google-java-format`]
+tool (version 1.7), and to format Bazel BUILD, WORKSPACE and .bzl files the
+link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`]
+tool (version 3.5.0). Unused dependencies are found and removed using the
+link:https://github.com/bazelbuild/buildtools/tree/master/unused_deps[`unused_deps`]
+build tool, a sibling of `buildifier`.
+
+These tools automatically apply format according to the style guides; this
+streamlines code review by reducing the need for time-consuming, tedious,
+and contentious discussions about trivial issues like whitespace.
+
+You may download and run `google-java-format` on your own, or you may
+run `./tools/setup_gjf.sh` to download a local copy and set up a
+wrapper script. If you run your own copy, please use the same version,
+as there may be slight differences between versions.
+
+When to use `final` modifier and when not (in new code):
+
+Always:
+
+ * final fields: marking fields as final forces them to be
+ initialized in the constructor or at declaration
+ * final static fields: clearly communicates the intent
+ * to use final variables in inner anonymous classes
+
+Optional:
+
+ * final classes: use when appropriate, e.g. API restriction
+ * final methods: similar to final classes
+
+Never:
+
+ * local variables: it clutters the code, and makes the code less
+ readable. When copying old code to new location, finals should
+ be removed
+ * method parameters: similar to local variables
+
+[[code-organization]]
+== Code Organization
+
+Do your best to organize classes and methods in a logical way.
+Here are some guidelines that Gerrit uses:
+
+ * Ensure a standard copyright header is included at the top
+ of any new files (copy it from another file, update the year).
+ * Always place loggers first in your class!
+ * Define any static interfaces next in your class.
+ * Define non static interfaces after static interfaces in your
+ class.
+ * Next you should define static types, static members, and
+ static methods, in decreasing order of visibility (public to private).
+ * Finally instance types, instance members, then constructors,
+ and then instance methods.
+ * Some common exceptions are private helper static methods, which
+ might appear near the instance methods which they help (but may
+ also appear at the top).
+ * Getters and setters for the same instance field should usually
+ be near each other barring a good reason not to.
+ * If you are using assisted injection, the factory for your class
+ should be before the instance members.
+ * Annotations should go before language keywords (`final`, `private`, etc) +
+ Example: `@Assisted @Nullable final type varName`
+ * Prefer to open multiple AutoCloseable resources in the same
+ try-with-resources block instead of nesting the try-with-resources
+ blocks and increasing the indentation level more than necessary.
+
+Wow that's a lot! But don't worry, you'll get the habit and most
+of the code is organized this way already; so if you pay attention
+to the class you are editing you will likely pick up on it.
+Naturally new classes are a little harder; you may want to come
+back and consult this section when creating them.
+
+[[design]]
+== Design
+
+Here are some design level objectives that you should keep in mind
+when coding:
+
+ * Most client pages should perform only one RPC to load so as to
+ keep latencies down. Exceptions would apply to RPCs which need
+ to load large data sets if splitting them out will help the
+ page load faster. Generally page loads are expected to complete
+ in under 100ms. This will be the case for most operations,
+ unless the data being fetched is not using Gerrit's caching
+ infrastructure. In these slower cases, it is worth considering
+ mitigating this longer load by using a second RPC to fill in
+ this data after the page is displayed (or alternatively it might
+ be worth proposing caching this data).
+ * `@Inject` should be used on constructors, not on fields. The
+ current exceptions are the ssh commands, these were implemented
+ earlier in Gerrit's development. To stay consistent, new ssh
+ commands should follow this older pattern; but eventually these
+ should get converted to eliminate this exception.
+ * Don't leave repository objects (git or schema) open. Use a
+ try-with-resources statement to ensure that repository objects get
+ closed after use.
+ * Don't leave UI components, which can cause new actions to occur,
+ enabled during RPCs which update Git repositories, including NoteDb.
+ This is to prevent people from submitting actions more than once
+ when operating on slow links. If the action buttons are disabled,
+ they cannot be resubmitted and the user can see that Gerrit is still
+ busy.
+
+[[tests]]
+== Tests
+
+ * Tests for new code will greatly help your change get approved.
+
+[[change-size]]
+== Change Size/Number of Files Touched
+
+And finally, I probably cannot say enough about change sizes.
+Generally, smaller is better, hopefully within reason. Do try to
+keep things which will be confusing on their own together,
+especially if changing one without the other will break something!
+
+ * If a new feature is implemented and it is a larger one, try to
+ identify if it can be split into smaller logical features; when
+ in doubt, err on the smaller side.
+ * Separate bug fixes from feature improvements. The bug fix may
+ be an easy candidate for approval and should not need to wait
+ for new features to be approved. Also, combining the two makes
+ reviewing harder since then there is no clear line between the
+ fix and the feature.
+ * Separate supporting refactoring from feature changes. If your
+ new feature requires some refactoring, it helps to make the
+ refactoring a separate change which your feature change
+ depends on. This way, reviewers can easily review the refactor
+ change as a something that should not alter the current
+ functionality, and feel more confident they can more easily
+ spot errors this way. Of course, it also makes it easier to
+ test and locate later on if an unfortunate error does slip in.
+ Lastly, by not having to see refactoring changes at the same
+ time, it helps reviewers understand how your feature changes
+ the current functionality.
+ * Separate logical features into separate changes. This
+ is often the hardest part. Here is an example: when adding a
+ new ability, make separate changes for the UI and the ssh
+ commands if possible.
+ * Do only what the commit message describes. In other words, things which
+ are not strictly related to the commit message shouldn't be part of
+ a change, even trivial things like externalizing a string somewhere
+ or fixing a typo. This helps keep `git blame` more useful in the future
+ and it also makes `git revert` more useful.
+ * Use topics to link your separate changes together.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-design-doc-conclusion-template.md b/Documentation/dev-design-doc-conclusion-template.md
new file mode 100644
index 0000000000..0625f2b90d
--- /dev/null
+++ b/Documentation/dev-design-doc-conclusion-template.md
@@ -0,0 +1,18 @@
+---
+title: "Design Doc - ${title} - Conclusion"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-conclusion.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
+# Conclusion
+
+Describe which decision was made and what were the reasons for it.
+
+## <a id="implementation-plan"> Implementation Plan
+
+If known, say who is driving the implementation, for when the
+implementation is planned and which priority it has.
diff --git a/Documentation/dev-design-doc-index-template.md b/Documentation/dev-design-doc-index-template.md
new file mode 100644
index 0000000000..10b4a8137a
--- /dev/null
+++ b/Documentation/dev-design-doc-index-template.md
@@ -0,0 +1,18 @@
+---
+title: "Design Doc - ${title}"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
+# Design Doc - ${title}
+
+* [Use Cases](use-cases.html)
+* [Solution - ${solution-name-1}](solution-1.html)
+* [Solution - ${solution-name-2}](solution-2.html)
+* ...
+* [Conclusion](conclusion.html)
+
diff --git a/Documentation/dev-design-doc-solution-template.md b/Documentation/dev-design-doc-solution-template.md
new file mode 100644
index 0000000000..8b2a8c0f9e
--- /dev/null
+++ b/Documentation/dev-design-doc-solution-template.md
@@ -0,0 +1,72 @@
+---
+title: "Design Doc - ${title} - Solution - ${solution-name}"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-solution-${solution-name}.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
+# Solution - ${solution-name}
+
+## <a id="overview"> Overview
+
+High-level overview; put details in the next section and background in
+the 'Background' section (see dev-design-doc-use-cases-template.txt).
+
+Should be understandable by engineers that are not working on Gerrit.
+
+If a solution is a variant of another solution, that other solution
+should be linked here.
+
+## <a id="detailed-design"> Detailed Design
+
+How does the overall design work? Details about the algorithms,
+storage format, APIs, etc., should be included here.
+
+For the initial review, it is ok for this to lack implementation
+details of minor importance.
+
+### <a id="scalability"> Scalability
+
+How does the solution scale?
+
+If applicable, consider:
+
+* data size increase
+* traffic increase
+* effects on replication across sites (master-replica and master-master)
+
+## <a id="alternatives-considered"> Alternatives Considered
+
+Within the scope of this solution you may need to describe what you did
+not do or why simpler approaches don't work. Mention other things to
+watch out for (if any).
+
+Do not describe alternative solutions in this section, as each solution
+should be described in a separate file.
+
+## <a id="pros-and-cons"> Pros and Cons
+
+Objectively list all points that speak in favor/against this solution.
+
+## <a id="implementation-plan"> Implementation Plan
+
+If known, say who would be willing to drive the implementation.
+
+It is possible to contribute solutions without having resources to do
+the implementation. In this case, say so here.
+
+If mentor support is desired, say so here. Also briefly describe any
+circumstances that can help with finding a suitable mentor.
+
+## <a id="time-estimation"> Time Estimation
+
+A rough itemized estimation of how much time it takes to implement this
+feature. Break down the feature into work items and estimate each item
+separately.
+
+If a mentor is assigned, this section must define a maximum time frame
+after which the mentorship automatically ends even if the feature isn't
+fully done yet.
diff --git a/Documentation/dev-design-doc-use-cases-template.md b/Documentation/dev-design-doc-use-cases-template.md
new file mode 100644
index 0000000000..02c2fb508b
--- /dev/null
+++ b/Documentation/dev-design-doc-use-cases-template.md
@@ -0,0 +1,48 @@
+---
+title: "Design Doc - ${title} - Use Cases"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-use-cases.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
+# Use Cases
+
+In a few sentences, describe the use-cases as interactions between a
+user and a system to attain particular goals.
+
+Should be understandable by anyone who is familiar with using Gerrit.
+
+Optionally, differentiate between primary and secondary use-cases.
+Secondary use-cases are related to the primary use-cases, but
+addressing them within the scope of this design is not mandatory. This
+means they may not be covered by all proposed solutions. Secondary
+use-cases that are not addressed by the concluded solution, may be
+discussed in separate design docs. In this case links to these design
+docs should be added here.
+
+Optionally, define non-goals.
+
+It is possible that use-cases are specific to custom setups (e.g. the
+multi-master setup at Google). In this case, say so here.
+
+## <a id="acceptance-criteria"> Acceptance Criteria
+
+Describe conditions that must be satisfied to consider the feature as
+done.
+
+If a mentor is assigned, the mentorship ends when this state is reached.
+Please note that a mentorship can also end earlier if the maximum time
+frame for the mentorship has exceeded (see section 'Time Estimation'
+in dev-design-doc-conclusion-template.txt).
+
+## <a id="background"> Background
+
+Stuff one needs to know to understand the use-cases (e.g. motivating
+examples, previous versions and problems, links to related
+changes/design docs, etc.).
+
+Note: this is background; do not write about your design or ideas to
+solve problems here.
diff --git a/Documentation/dev-design-docs.txt b/Documentation/dev-design-docs.txt
new file mode 100644
index 0000000000..a339da396a
--- /dev/null
+++ b/Documentation/dev-design-docs.txt
@@ -0,0 +1,153 @@
+= Gerrit Code Review - Design Docs
+
+For the link:dev-contributing.html#design-driven-contribution-process[
+design-driven contribution process] it is required to specify features
+upfront in a design doc.
+
+[[structure]]
+== Design Doc Structure
+
+A design doc should discuss the following aspects:
+
+* Use-Cases:
+ The interactions between a user and a system to attain particular
+ goals.
+* Acceptance Criteria
+ Conditions that must be satisfied to consider the feature as done.
+* Background:
+ Stuff one needs to know to understand the use-cases (e.g. motivating
+ examples, previous versions and problems, links to related
+ changes/design docs, etc.)
+* Possible Solutions:
+ Possible solutions with the pros and cons, and explanation of
+ implementation details.
+* Conclusion:
+ Which decision was made and what were the reasons for it.
+
+[[collaboration]]
+As community we want to collaborate on design docs as much as possible
+and write them together, in an iterative manner. To make this work well
+design docs are split into multiple files that can be written and
+refined by several persons in parallel:
+
+* `index.md`:
+ Entry file that links to the files below (also see
+ 'dev-design-doc-index-template.md').
+* `use-cases.md`:
+ Describes the use-cases, acceptance criteria and background (also see
+ 'dev-design-doc-use-cases-template.md').
+* `solution-<n>.md`:
+ Each possible solution (with the pros and cons, and implementation
+ details) is described in a separate file (also see
+ 'dev-design-doc-solution-template.md').
+* `conclusion.md`:
+ Describes the conclusion of the design discussion (also see
+ 'dev-design-doc-conclusion-template.md').
+
+[[expectation]]
+It is expected that:
+
+* An agreement on the use-cases is achieved before solutions are being
+ discussed in detail.
+* Anyone who has ideas for an alternative solution uploads a change
+ with a `solution-<n>.md` that describes their solution. In case of
+ doubt whether an idea is a refinement of an existing solution or an
+ alternative solution, it's up to the owner of the discussed solution
+ to decide if the solution should be updated, or if the proposer
+ should start a new alternative solution.
+* All possible solutions are fairly discussed with their pros and cons,
+ and treated equally until a conclusion is made.
+* Unrelated issues (judged by the design doc owner) that are identified
+ during discussions may be extracted into new design docs (initially
+ consisting only of an `index.md` and a `use-cases.md` file). Doing so
+ is optional yet can be done by either the design owner or reviewers.
+* Changes making iterative improvements can be submitted frequently
+ (e.g. additional uses-cases can be added later, solutions can be
+ submitted without describing implementation details, etc.).
+* After a conclusion has been approved contributors are expected to
+ keep the design doc updated and fill in gaps while they go forward
+ with the implementation.
+
+[[propose]]
+== How to propose a new design?
+
+To propose a new design, upload a change to the
+link:https://gerrit-review.googlesource.com/admin/repos/homepage[
+homepage] repository that adds a new folder under `pages/design-docs/`
+which contains at least an `index.md` and a `uses-cases.md` file (see
+link:#structure[design doc structure] above).
+
+Pushing a design doc for review requires to be a
+link:dev-roles.html#contributor[contributor].
+
+When contributing design docs, contributors should make clear whether
+they are committed to do the implementation. It is possible to
+contribute designs without having resources to do the implementation,
+but in this case the implementation is only done if someone volunteers
+to do it (which is not guaranteed to happen).
+
+Only very few maintainers actively watch out for uploaded design docs.
+To raise awareness you may want to send a notification to the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+mailing list about your uploaded design doc. But the discussion should
+not take place on the mailing list, comments should be made by reviewing
+the change in Gerrit.
+
+[[review]]
+== Design doc review
+
+Everyone in the link:dev-roles.html[Gerrit community] is welcome to
+take part in the design review and comment on the design. As such, every
+design reviewer is expected to respect the community
+link:https://www.gerritcodereview.com/codeofconduct.html[Code of Conduct].
+
+Ideas for alternative solutions should be uploaded as a change that
+describes the solution (see link:#collaboration[above]). This should be
+done as early as possible during the review process, so that related
+comment threads stop there and do not clutter the current review. It is up
+to the alternative reviews to then host their related comments.
+
+Verification should be based on the generated `jekyll` site using the
+local `docker`, rather than via the rendering in `gitiles` (via
+`gerrit-review`).
+
+Changes which make a conclusion on a design (changes that add/change
+the `conclusion.md` file, see link:#structure[Design Doc Structure])
+should stay open for a minimum of 10 calendar days so that everyone has
+a fair chance to see them. It is important that concerns regarding a
+feature are raised during this time frame since once a conclusion is
+approved and submitted the implementation may start immediately.
+
+Other design doc changes can and should be submitted quickly so that
+collaboration and iterative refinements work smoothly (see
+link:#collaboration[above]).
+
+For proposed features the contributor should hear back from the
+link:dev-processes.html#steering-committee[engineering steering
+committee] within 14 calendar days whether the proposed feature is in
+scope of the project and if it can be accepted.
+
+[[meetings]]
+=== Meeting discussions
+
+If the Gerrit review doesn't start efficiently enough, stalls, gets off-track
+too much or becomes overly complex, one can use a meeting to refocus it. From
+that review thread, the organizer can volunteer oneself, or be proposed (even
+requested) by a reviewer. link:https://www.gerritcodereview.com/members.html#community-managers[
+Community managers] may help facilitate that if ultimately necessary.
+
+[[watch-designs]]
+== How to get notified for new design docs?
+
+. Go to the
+ link:https://gerrit-review.googlesource.com/settings/#Notifications[
+ notification settings]
+. Add a project watch for the `homepage` repository with the following
+ query: `dir:pages/design-docs`
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-design.txt b/Documentation/dev-design.txt
index 69af18d3a6..fd53cac066 100644
--- a/Documentation/dev-design.txt
+++ b/Documentation/dev-design.txt
@@ -178,17 +178,6 @@ has been migrated out of the database and into the git
repositories for each project.
-== Project Information
-
-Gerrit is developed as a self-hosting open source project:
-
-* link:https://www.gerritcodereview.com/[Project Homepage]
-* link:https://www.gerritcodereview.com/download/index.html[Release Versions]
-* link:https://gerrit.googlesource.com/gerrit[Source]
-* link:https://bugs.chromium.org/p/gerrit/issues/list[Issue Tracking]
-* link:https://review.source.android.com/[Change Review]
-
-
== Internationalization and Localization
As a source code review system for open source projects, where the
@@ -204,8 +193,6 @@ Gerrit code base. Some portions of the code have tried to take
RTL into consideration, while others probably need to be modified
before translating the UI to an RTL language.
-* link:i18n-readme.html[Gerrit's i18n Support]
-
== Accessibility Considerations
@@ -533,7 +520,7 @@ from an actual installation's performance.)
Because of the distributed nature of Git, end-users don't need to
contact the central Gerrit Code Review server very often. For `git
-fetch` traffic, link:pgm-daemon.html[slave mode] is known to be an
+fetch` traffic, link:pgm-daemon.html[replica mode] is known to be an
effective way to offload traffic from the main server, permitting it
to scale to a large user base without needing an excessive number of
cores in a single system.
@@ -640,29 +627,6 @@ logs may be mined for usage information. This is outside of the
scope of Gerrit.
-== Testing Plan
-
-Gerrit is currently manually tested through its web UI.
-
-JGit has a fairly extensive automated unit test suite. Most new
-changes to JGit are rejected unless corresponding automated unit
-tests are included.
-
-
-== Caveats
-
-Rietveld can't be used as it does not provide the "submit over the
-web" feature that Gerrit provides for Git.
-
-Gitosis can't be used as it does not provide any code review
-features, but it does provide basic access controls.
-
-Email based code review does not scale to a project as large and
-complex as Android. Most contributors at least need some sort of
-dashboard to keep track of any pending reviews, and some way to
-correlate updated revisions back to the comments written on prior
-revisions of the same logical change.
-
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index 420151b140..bdd6360c7d 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -86,11 +86,6 @@ Java 8 is still the default:
* Add JRE, e.g.: directory: /usr/lib64/jvm/java-9-openjdk, name: java-9-openjdk-9
* Change execution environment for gerrit project to: JavaSE-9 (java-9-openjdk-9)
* Check that compiler compliance level in gerrit project is set to: 9
-* Add this parameter to VM argument for gerrit_daemin launcher:
-----
- --add-modules java.activation \
- --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
-----
[[Formatting]]
== Code Formatter Settings
@@ -98,7 +93,7 @@ Java 8 is still the default:
To format source code, Gerrit uses the
link:https://github.com/google/google-java-format[`google-java-format`]
tool (version 1.7), which automatically formats code to follow the
-style guide. See link:dev-contributing.html#style[Code Style] for the
+style guide. See link:dev-crafting-changes.html#style[Code Style] for the
instruction how to set up command line tool that uses this formatter.
The Eclipse plugin is provided that allows to format with the same
formatter from within the Eclipse IDE. See
diff --git a/Documentation/dev-inspector.txt b/Documentation/dev-inspector.txt
index b1559caf7d..39736d7a0e 100644
--- a/Documentation/dev-inspector.txt
+++ b/Documentation/dev-inspector.txt
@@ -11,7 +11,7 @@ _java_ -jar gerrit.war _daemon_
[--enable-httpd | --disable-httpd]
[--enable-sshd | --disable-sshd]
[--console-log]
- [--slave]
+ [--replica]
-s
--
diff --git a/Documentation/dev-intellij.txt b/Documentation/dev-intellij.txt
index 50770798cc..81790dbbab 100644
--- a/Documentation/dev-intellij.txt
+++ b/Documentation/dev-intellij.txt
@@ -104,7 +104,7 @@ CodeStyleManager with a custom one. Thus, uses of *Reformat Code* either via
*Code -> Reformat Code*, keyboard shortcuts, or the commit dialog will use the
custom style defined by the `google-java-format` plugin.
-Please refer to the documentation on the <<dev-contributing#style,code style>>
+Please refer to the documentation on the <<dev-crafting-changes#style,code style>>
for which version of `google-java-format` is used with Gerrit.
==== Code style settings
@@ -159,7 +159,7 @@ This section is only relevant in case you want to use the Git integration
plugin in IntelliJ IDEA.
To simplify the creation of commit messages which are compliant with the
-<<dev-contributing#commit-message,Commit Message>> format, do the following:
+<<dev-crafting-changes#commit-message,Commit Message>> format, do the following:
. Go to *File -> Settings -> Version Control -> Commit Dialog*.
. In the *Commit message inspections*, activate the three inspections:
@@ -171,7 +171,7 @@ To simplify the creation of commit messages which are compliant with the
right margin*.
In addition, you should follow the instructions of
-<<dev-contributing#git_commit_settings,this section>> (if you haven't
+<<dev-crafting-changes#git-commit-settings,this section>> (if you haven't
done so already):
* Install the Git commit message hook for the `Change-Id` line.
diff --git a/Documentation/dev-plugins-lifecycle.txt b/Documentation/dev-plugins-lifecycle.txt
new file mode 100644
index 0000000000..b552472efe
--- /dev/null
+++ b/Documentation/dev-plugins-lifecycle.txt
@@ -0,0 +1,254 @@
+= Plugin Lifecycle
+
+Most of the plugins are hosted on the same instance as the
+link:https://gerrit-review.googlesource.com[Gerrit project itself] to make them
+more discoverable and have more chances to be reviewed by the whole community.
+
+[[hosting_lifecycle]]
+== Hosting Lifecycle
+
+The process of writing a new plugin goes through different phases:
+
+- Ideation and Discussion:
++
+The idea of creating a new plugin is posted and discussed on the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss] mailing list.
++
+Also see section link#ideation_discussion[Ideation and discussion] below.
+
+- Prototyping (optional):
++
+The author of the plugin creates a working prototype on a public repository
+accessible to the community.
++
+Also see section link#plugin_prototyping[Plugin Prototyping] below.
+
+- Proposal and Hosting:
++
+The author proposes to release the plugin under the
+link:https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 OpenSource
+license] and requests the plugin to be hosted on
+link:https://gerrit-review.googlesource.com[the Gerrit project site]. The
+proposal must be accepted by at least one Gerrit maintainer. In case of
+disagreement between maintainers, the issue can be escalated to the
+link:dev-processes.html#steering-committee[Engineering Steering Committee]. If
+the plugin is accepted, the Gerrit maintainer creates the project under the
+plugins path on link:https://gerrit-review.googlesource.com[the Gerrit project
+site].
++
+Also see section link#plugin_proposal[Plugin Proposal] below.
+
+- Build:
++
+To make the consumption of the plugin easy and to notice plugin breakages early
+the plugin author should setup build jobs on
+link:https://gerrit-ci.gerritforge.com[the GerritForge CI] that build the
+plugin for each Gerrit version that it supports.
++
+Also see section link#build[Build] below.
+
+- Development and Contribution:
++
+The author develops a production-ready code base of the plugin, with
+contributions, reviews, and help from the Gerrit community.
++
+Also see section link#development_contribution[Development and contribution]
+below.
+
+- Release:
++
+The author releases the plugin by creating a Git tag and announcing the plugin
+on the link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+mailing list.
++
+Also see section link#plugin_release[Plugin release] below.
+
+- Maintenance:
++
+The author maintains their plugins as new Gerrit versions are released, updates
+them when necessary, develops further existing or new features and reviews
+incoming contributions.
+
+- Deprecation:
++
+The author declares that the plugin is not maintained anymore or is deprecated
+and should not be used anymore.
++
+Also see section link#plugin_deprecation[Plugin deprecation] below.
+
+[[ideation_discussion]]
+== Ideation and Discussion
+
+Starting a new plugin project is a community effort: it starts with the
+identification of a gap in the Gerrit Code Review product but evolves with the
+contribution of ideas and suggestions by the whole community.
+
+The ideator of the plugin starts with an RFC (Request For Comments) post on the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss] mailing list
+with a description of the main reasons for starting a new plugin.
+
+Example of a post:
+
+----
+ [RFC] Code-Formatter plugin
+
+ Hello, community,
+ I am proposing to create a new plugin for Gerrit called 'Code-Formatter', see
+ the details below.
+
+ *The gap*
+ Often, when I post a new change to Gerrit, I forget to run the common code
+ formatting tool (e.g. Google-Java-Format for the Gerrit project). I would
+ like Gerrit to be in charge of highlighting these issues to me and save many
+ people's time.
+
+ *The proposal*
+ The Code-Formatter plugin reads the formatting rules in the project config
+ and applies them automatically to every patch-set. Any issue is reported as a
+ regular review comment to the patchset, highlighting the part of the code to
+ be changed.
+
+ What do you think? Did anyone have the same idea or need?
+----
+
+The idea is discussed on the mailing list and can evolve based on the needs and
+inputs from the entire community.
+
+After the discussion, the ideator of the plugin can decide to start prototyping
+on it or park the proposal, if the feedback provided an alternative solution to
+the problem. The prototype phase can be optionally skipped if the idea is clear
+enough and receives a general agreement from the Gerrit maintainers. The author
+can be given a "leap of faith" and can go directly to the format plugin
+proposal (see below) and the creation of the plugin repository.
+
+[[plugin_prototyping]]
+== Plugin Prototyping
+
+The initial idea is translated to code by the plugin author. The development
+can happen on any public or private source code repository and can involve one
+or more contributors. The purpose of prototyping is to verify that the idea can
+be implemented and provides the expected benefits.
+
+Once a working prototype is ready, it can be announced as a follow-up to the
+initial RFC proposal so that other members of the community can see the code
+and try the plugin themselves.
+
+[[plugin_proposal]]
+== Plugin Proposal
+
+The author decides that the plugin prototype makes sense as a general purpose
+plugin and decides to release the code with the same
+link:https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license]
+as the Gerrit Code Review project and have it hosted on
+link:https://gerrit-review.googlesource.com[the Gerrit project site].
+
+The plugin author formalizes the proposal with a follow-up of the initial RFC
+post and asks for public opinion on it.
+
+Example:
+
+----
+ Re - [RFC] Code-Formatter plugin
+
+ Hello, community,
+ thanks for your feedback on the prototype. I have now decided to donate the
+ project to the Gerrit Code Review project and make it a plugin:
+
+ Plugin name:
+ /plugins/code-formatter
+
+ Plugin description:
+ Plugin to allow automatic posting review based on code-formatting rules
+----
+
+The community discusses the proposal and the value of the plugin for the whole
+project; the result of the discussion can end up in one of the following cases:
+
+- The plugin's project request is widely appreciated and formally accepted by
+ at least one Gerrit maintainer who creates the repository as child project of
+ 'Public-Projects' on link:https://gerrit-review.googlesource.com[the Gerrit
+ project site], creates an associated plugin owners group with "Owner"
+ permissions for the plugin and adds the plugin's author as member of it.
+- The plugin's project is widely appreciated; however, another existing plugin
+ already partially covers the same use-case and thus it would make more sense
+ to have the features integrated into the existing plugin. The new plugin's
+ author contributes his prototype commits refactored to be included as change
+ into the existing plugin.
+- The plugin's project is found useful; however, it is too specific to the
+ author's use-case and would not make sense outside of it. The plugin remains
+ in a public repository, widely accessible and OpenSource, but not hosted on
+ link:https://gerrit-review.googlesource.com[the Gerrit project site].
+
+[[build]]
+== Build
+
+The plugin's maintainer creates a job on the
+link:https://gerrit-ci.gerritforge.com[GerritForge CI] by creating a new YAML
+definition in the link:https://gerrit.googlesource.com/gerrit-ci-scripts[Gerrit
+CI Scripts] repository.
+
+Example of a YAML CI job for plugins:
+
+----
+ - project:
+ name: code-formatter
+ jobs:
+ - 'plugin-{name}-bazel-{branch}':
+ branch:
+ - master
+----
+
+[[development_contribution]]
+== Development and Contribution
+
+The plugin follows the same lifecycle as Gerrit Code Review and needs to be
+kept up-to-date with the current active branches, according to the
+link:https://www.gerritcodereview.com/#support[current support policy].
+During the development, the plugin's maintainer can reward contributors
+requesting to be more involved and making them maintainers of his plugin,
+adding them to the list of the project owners.
+
+[[plugin_release]]
+== Plugin Release
+
+The plugin's maintainer is the only person responsible for making and
+announcing the official releases, typically, but not limited to, in conjunction
+with the major releases of Gerrit Code Review. The plugin's maintainer may tag
+his plugin and follow the notation and semantics of the Gerrit Code Review
+project; however it is not mandatory and many of the plugins do not have any
+tags or releases.
+
+Example of a YAML CI job for a plugin compatible with multiple Gerrit versions:
+
+----
+ - project:
+ name: code-formatter
+ jobs:
+ - 'plugin-{name}-bazel-{branch}-{gerrit-branch}':
+ branch:
+ - master
+ gerrit-branch:
+ - master
+ - stable-3.0
+ - stable-2.16
+----
+
+[[plugin_deprecation]]
+== Plugin Deprecation
+
+The plugin's maintainer and the community have agreed that the plugin is not
+useful anymore or there isn't anyone willing to contribute to bringing it
+forward and keeping it up-to-date with the recent versions of Gerrit Code
+Review.
+
+The plugin's maintainer puts a deprecation notice in the README.md of the
+plugin and pushes it for review. If nobody is willing to bring the code
+forward, the change gets merged, and the master branch is removed from the list
+of branches to be built on the GerritFoge CI.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 8ad55358c7..4408d9329d 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -1,7 +1,8 @@
= Gerrit Code Review - Plugin Development
The Gerrit server functionality can be extended by installing plugins.
-This page describes how plugins for Gerrit can be developed.
+This page describes how plugins for Gerrit can be developed and hosted
+on gerrit-review.googlesource.com.
For PolyGerrit-specific plugin development, consult with
link:pg-plugin-dev.html[PolyGerrit Plugin Development] guide.
@@ -389,6 +390,10 @@ Allows to listen to events visible to the specified user. These are the
same link:cmd-stream-events.html#events[events] that are also streamed
by the link:cmd-stream-events.html[gerrit stream-events] command.
+* `com.google.gerrit.extensions.events.AccountActivationListener`:
++
+User account got activated or deactivated
+
* `com.google.gerrit.extensions.events.LifecycleListener`:
+
Plugin start and stop
@@ -963,6 +968,11 @@ Output:
}
----
+Implementors of the `ChangeAttributeFactory` interface should check whether
+they need to contribute to the link:#change-etag-computation[change ETag
+computation] to prevent callers using ETags from potentially seeing outdated
+plugin attributes.
+
[[simple-configuration]]
== Simple Configuration in `gerrit.config`
@@ -2048,6 +2058,14 @@ MySecureStore(@SitePath java.io.File sitePath) {
No Guice bindings or modules are required. Gerrit will automatically
discover and bind the implementation.
+[[gerrit-replica]]
+== Gerrit Replica
+
+Gerrit can be run as a read-only replica. Some plugins may need to know
+whether Gerrit is run as a primary- or a replica instance. For that purpose
+Gerrit exposes the `@GerritIsReplica` annotation. A boolean annotated with
+this annotation will indicate whether Gerrit is run as a replica.
+
[[accountcreation]]
== Account Creation
@@ -2300,7 +2318,8 @@ flags is growing without bound. The store must be able handle this data
volume efficiently.
Gerrit implements this extension point, but plugins may bind another
-implementation, e.g. one that supports multi-master.
+implementation, e.g. one that supports cluster setup with multiple
+primary Gerrit nodes handling write operations.
----
DynamicItem.bind(binder(), AccountPatchReviewStore.class)
@@ -2467,10 +2486,10 @@ the `addreviewer.pluginName-exportName.weight` value in `gerrit.config`.
[source, java]
----
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import java.util.Set;
@@ -2594,10 +2613,8 @@ If a change is ready to be submitted, `OK`. If it is not ready and requires
modifications, `NOT_READY`. Other statuses are available for particular cases.
A change can be submitted if all the plugins accept the change.
-Plugins may also decide not to vote on a given change by returning an empty
-Collection (ie: the plugin is not enabled for this repository), or to vote
-several times (ie: one SubmitRecord per project in the hierarchy).
-The results are handled as if multiple plugins voted for the change.
+Plugins may also decide not to vote on a given change by returning an
+`Optional.empty()` (ie: the plugin is not enabled for this repository).
If a plugin decides not to vote, it's name will not be displayed in the UI and
it will not be recoded in the database.
@@ -2632,20 +2649,20 @@ changes that are marked as WIP or that are closed (abandoned, merged) can't be m
[source, java]
----
-import java.util.Collection;
+import java.util.Optional;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRecord.Status;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.rules.SubmitRule;
public class MyPluginRules implements SubmitRule {
- public Collection<SubmitRecord> evaluate(ChangeData changeData) {
+ public Optional<SubmitRecord> evaluate(ChangeData changeData) {
// Implement your submitability logic here
// Assuming we want to prevent this change from being submitted:
- SubmitRecord record;
+ SubmitRecord record = new SubmitRecord();
record.status = Status.NOT_READY;
- return record;
+ return Optional.of(record);
}
}
----
@@ -2678,6 +2695,82 @@ to change in order to be compliant. These requirements should be kept once they
are met, but marked as `OK`. If the requirements were not displayed, reviewers
would need to use their precious time to manually check that they were met.
+Implementors of the `SubmitRule` interface should check whether they need to
+contribute to the link:#change-etag-computation[change ETag computation] to
+prevent callers using ETags from potentially seeing outdated submittability
+information.
+
+[[change-etag-computation]]
+== Change ETag Computation
+
+By implementing the `com.google.gerrit.server.change.ChangeETagComputation`
+interface plugins can contribute a value to the change ETag computation.
+
+Plugins can affect the result of the get change / get change details REST
+endpoints by:
+
+* providing link:#query_attributes[plugin defined attributes] in
+ link:rest-api-changes.html#change-info[ChangeInfo]
+* implementing a link:#pre-submit-evaluator[pre-submit evaluator] which affects
+ the computation of `submittable` field in
+ link:rest-api-changes.html#change-info[ChangeInfo]
+
+If the plugin defined part of link:rest-api-changes.html#change-info[
+ChangeInfo] depends on plugin specific data, callers that use change ETags to
+avoid unneeded recomputations of ChangeInfos may see outdated plugin attributes
+and/or outdated submittable information, because a ChangeInfo is only reloaded
+if the change ETag changes.
+
+By implementating the `com.google.gerrit.server.change.ChangeETagComputation`
+interface plugins can contribute to the ETag computation and thus ensure that
+the change ETag changes when the plugin data was changed. This way it can be
+ensured that callers do not see outdated ChangeInfos.
+
+IMPORTANT: Change ETags are computed very frequently and the computation must
+be cheap. Take good care to not perform any expensive computations when
+implementing this.
+
+[source, java]
+----
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.hash.Hasher;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.change.ChangeETagComputation;
+
+public class MyPluginChangeETagComputation implements ChangeETagComputation {
+ public String getETag(Project.NameKey projectName, Change.Id changeId) {
+ Hasher hasher = Hashing.murmur3_128().newHasher();
+
+ // Add hashes for all plugin-specific data that affects change infos.
+ hasher.putString(sha1OfPluginSpecificChangeRef, UTF_8);
+
+ return hasher.hash().toString();
+ }
+}
+----
+
+[[exception-hook]]
+== ExceptionHook
+
+An `ExceptionHook` allows implementors to control how certain
+exceptions should be handled.
+
+This interface is intended to be implemented for multi-master setups to
+control the behavior for handling exceptions that are thrown by a lower
+layer that handles the consensus and synchronization between different
+server nodes. E.g. if an operation fails because consensus for a Git
+update could not be achieved (e.g. due to slow responding server nodes)
+this interface can be used to retry the request instead of failing it
+immediately.
+
+[[mail-soy-template-provider]]
+== MailSoyTemplateProvider
+
+This extension point allows to provide soy templates for registration
+so that they can be used for sending emails from a plugin.
+
[[quota-enforcer]]
== Quota Enforcer
@@ -2766,6 +2859,20 @@ class ApiQpsEnforcer implements QuotaEnforcer {
}
----
+[[performance-logger]]
+== Performance Logger
+
+`com.google.gerrit.server.logging.PerformanceLogger` is an extension point that
+is invoked for all operations for which the execution time is measured. The
+invocation of the extension point does not happen immediately, but only at the
+end of a request (REST call, SSH call, git push). Implementors can write the
+execution times into a performance log for further analysis.
+
+[[request-listener]]
+== Request Listener
+
+`com.google.gerrit.server.RequestListener` is an extension point that is
+invoked each time the server executes a request from a user.
== SEE ALSO
diff --git a/Documentation/dev-processes.txt b/Documentation/dev-processes.txt
new file mode 100644
index 0000000000..09dcbee9bc
--- /dev/null
+++ b/Documentation/dev-processes.txt
@@ -0,0 +1,355 @@
+= Gerrit Code Review - Development Processes
+
+[[project-governance]]
+[[steering-committee]]
+== Project Governance / Engineering Steering Committee
+
+The Gerrit project has an engineering steering committee (ESC) that is
+in charge of:
+
+* Gerrit core (the `gerrit` project) and the core plugins
+* defining the project vision and the project scope
+* maintaining a roadmap, a release plan and a prioritized backlog
+* ensuring timely design reviews
+* ensuring that new features are compatible with the project vision and
+ are well aligned with other features (give feedback on new
+ link:dev-design-docs.html[design docs] within 14 calendar days)
+* approving/rejecting link:dev-design-docs.html[designs], vetoing new
+ features
+* assigning link:dev-roles.html#mentor[mentors] for approved features
+* accepting new plugins as core plugins
+* making changes to the project governance process and the
+ link:dev-contributing.html#contribution-processes[contribution
+ processes]
+
+The steering committee has 5 members:
+
+* 3 Googlers that are appointed by Google
+* 2 non-Google maintainers, elected by non-Google maintainers for the
+ period of 1 year (see link:#steering-committee-election[below])
+
+Refer to the project homepage for the link:https://www.gerritcodereview.com/members.html#engineering-steering-committee[
+list of current committee members].
+
+The steering committee should act in the interest of the Gerrit project
+and the whole Gerrit community.
+
+For decisions, consensus between steering committee members and all
+other maintainers is desired. If consensus cannot be reached, decisions
+can also be made by simple majority in the steering committee (should
+be applied only in exceptional situations).
+
+The steering committee is empowered to overrule positive/negative votes
+from individual maintainers, but should do so only in exceptional
+situations after attempts to reach consensus have failed.
+
+As an integral part of the Gerrit community, the steering committee is
+committed to transparency and to answering incoming requests in a
+timely manner.
+
+[[steering-committee-election]]
+=== Election of non-Google steering committee members
+
+The election of the non-Google steering committee members happens once
+a year in May. Non-Google link:dev-roles.html#maintainer[maintainers]
+can nominate themselves by posting an informal application on the
+non-public maintainers mailing list by end of April (deadline for 2019
+is Mon 13th of May). By applying to be steering committee member, the
+candidate confirms to be able to dedicate the time that is needed to
+fulfill this role (also see
+link:dev-roles.html#steering-committee-member[steering committee
+member]).
+
+Each non-Google maintainer can vote for 2 candidates. The voting
+happens by posting on the maintainer mailing list. The voting period is
+14 calendar days from the nomination deadline (except for 2019, where
+the initial steering committee should be confirmed during the Munich
+hackathon, the voting period goes from 14th May to 16th May).
+
+Google maintainers do not take part in this vote, because Google
+already has dedicated seats in the steering committee (see section
+link:#steering-committee[steering committee]).
+
+[[contribution-process]]
+== Contribution Process
+
+See link:dev-contributing.html[here].
+
+[[design-doc-review]]
+== Design Doc Review
+
+See link:dev-design-docs.html#review[here].
+
+[[versioning]]
+== Semantic versioning
+
+Gerrit follows a light link:https://semver.org/[semantic versioning scheme] MAJOR.MINOR[.PATCH[.HOTFIX]]
+format:
+
+ * MAJOR is incremented when there are substantial incompatible changes and/or
+ new features in Gerrit.
+ * MINOR is incremented when there are changes that are typically backward compatible
+ with the earlier minor version. Features can be removed following the
+ link:#deprecating-features[feature deprecation process]. Dependencies can be upgraded
+ according to the link:dev-processes.html#upgrading-libraries[libraries upgrade policy].
+ * PATCH is incremented when there are backward-compatible bug fixes in Gerrit or its
+ dependencies. When PATCH is zero, it can be omitted.
+ * HOTFIX is present only when immediately after a patch release, some urgent
+ fixes in the code or the packaging format are required but do not justify a
+ new patch release.
+
+For every MAJOR.MINOR release there is an associated stable branch that follows well defined
+link:#dev-in-stable-branches[rules of development].
+
+Within a stable branch, there are multiple MAJOR.MINOR.PATCH tags created associated to the
+bug-fix releases of that stable release.
+
+Examples:
+
+* Gerrit v3.0.0 contains breaking incompatible changes in the functionality because
+ the ReviewDb storage has been totally removed.
+* Gerrit v2.15 contains brand-new features like NoteDb, however, still supports the existing
+ ReviewDb storage for changes and thus is considered a minor release.
+* Gerrit v2.14.20 is the 20th patch-release of the stable Gerrit v2.14.* and thus does not contain
+ new features but only bug-fixes.
+
+[[dev-in-stable-branches]]
+== Development in stable branches
+
+As their name suggests stable branches are intended to be stable. This means that generally
+only bug-fixes should be done on stable branches, however this is not strictly enforced and
+exceptions may apply:
+
+ * When a stable branch is initially created to prepare a new release the Gerrit community
+ discusses on the mailing list if there are pending features which should still make it into the
+ release. Those features are blocking the release and should be implemented on the stable
+ branch before the first release candidate is created.
+ * To stabilize the code before doing a major release several release candidates are created. Once
+ the first release candidate was done no more features should be accepted on the stable branch.
+ If more features are found to be required they should be discussed with the steering committee
+ and should only be allowed if the risk of breaking things is considered to be low.
+ * Once a major release is done only bug-fixes and documentation updates should be done on the
+ stable branch. These updates will be included in the next minor release.
+ * For minor releases new features could be acceptable if the following conditions are met:
+ ** they are result of a new feature introduced through a merge of an earlier stable branch
+ ** they are justified for completing, extending or fixing an existing feature
+ ** does not involve API, user-interface changes or data migrations
+ ** is backward compatible with all existing features
+ ** the parts of the code in common with existing features are properly covered by end-to-end tests
+ ** is important to the Gerrit community and no Gerrit maintainers have raised objections.
+ * In cases of doubt or conflicting opinions on new features, it's the responsibility of the
+ steering committee to evaluate the risk of new features and make a decision based on these
+ rules and opinions from the Gerrit community.
+ * The older a stable branch is the more stable it should be. This means old stable branches
+ should only receive bug-fixes that are either important or low risk. Security fixes, including
+ security updates for third party dependencies, are always considered as important and hence can
+ always be done on stable branches.
+
+Examples:
+
+* Gerrit v3.0.0-rc1 and v3.0.0-rc2 may contain new features and API changes without notice,
+ even if they are both cut on the same stable-3.0 branch.
+* Gerrit v2.14.8 introduced the support for ElasticSearch as a new feature. This was an exception
+ agreed amongst the Gerrit maintainers, did not touch the Lucene indexing code-base, was supported
+ by container-based E2E tests and represents a completion of an high-level feature.
+
+[[backporting]]
+== Backporting to stable branches
+
+From time to time bug fix releases are made for existing stable branches.
+
+Developers concerned with stable branches are encouraged to backport or push fixes to these
+branches, even if no new release is planned. Backporting features is only possible in compliance
+with the rules link:#dev-in-stable-branches[above].
+
+Fixes that are known to be needed for a particular release should be pushed for review on that
+release's stable branch. They will then be included into the master branch when the stable branch
+is merged back.
+
+[[security-issues]]
+== Dealing with Security Issues
+
+If a security vulnerability in Gerrit is discovered, we place an link:#embargo[
+embargo] on it until a fixed release or mitigation is available. Fixing the
+issue is usually pursued with high priority (depends on the severity of the
+security vulnerability). The embargo is lifted and the vulnerability is
+disclosed to the community as soon as a fix release or another mitigation is
+available.
+
+[[report-security-issue]]
+=== How to report a security vulnerability?
+
+To report a security vulnerability file a
+link:https://bugs.chromium.org/p/gerrit/issues/entry?template=Security+Issue[
+security issue] in the Gerrit issue tracker. The visibility of issues that are
+created with the `Security Issue` template is automatically restricted to
+Gerrit maintainers and a few long-term contributors. This means as a reporter
+you may not be able to see the issue once it is created. Security issues are
+created on the `ESC` component so that they will be discussed at the next
+meeting of the link:#steering-committee[Engineering Steering Committee] which
+takes place biweekly.
+
+If an existing issue is found to be a security vulnerability it should be
+turned into a security issue by:
+
+. Setting the component to `ESC`
+. Adding the labels `Security` and `NonPublic`
+
+In case of doubt, or if an issue cannot wait until the next ESC meeting,
+contact the link:#steering-committee[Engineering Steering Committee] directly
+by sending them an mailto:gerritcodereview-esc@googlegroups.com[email].
+
+If needed, the ESC will contact the reporter for additional details.
+
+[[embargo]]
+=== The Embargo
+
+Once an issue has been identified as security vulnerability, we keep it under
+embargo until a fixed release or a mitigation is available. This means that the
+issue is not discussed publicly, but only on issues with restricted visibility
+(see link:#report-security-issue[above]) and at the mailing lists of the ESC,
+community managers and Gerrit maintainers. Since the `repo-discuss` mailing
+list is public, security issues must not be discussed on this mailing list
+while the embargo is in place.
+
+The reason for keeping an embargo is to prevent attackers from taking advantage
+of a vulnerability while no fixed releases are available yet, and Gerrit
+administrators cannot make their systems secure.
+
+Once a fix release or mitigation is available, the embargo is lifted and the
+community is informed about the security vulnerability with the advise to
+address the security vulnerability immediately (either by upgrading to a fixed
+release or applying the mitigation). The information about the security
+vulnerability is disclosed via the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss] mailing list.
+
+[[handle-security-issue]]
+=== Handling of the Security Vulnerability
+
+. Engineering Steering Committee evaluates the security vulnerability:
++
+The ESC discusses the security vulnerability and which actions should be taken
+to address it. One person, usually one of the Gerrit maintainers, should be
+appointed to drive and coordinate the investigation and the fix of the security
+vulnerability. This coordinator doesn't need to do all the work alone, but is
+responsible that the security vulnerability is getting fixed in a timely
+manner.
++
+If the security vulnerability affects multiple or older releases the ESC should
+decide which of the releases should be fixed. For critical security issue we
+also consider fixing old releases that are otherwise not receiving any
+bug-fixes anymore.
++
+It's also possible that the ESC decides that an issue is not a security issue
+and the embargo is lifted immediately.
+
+. Implementation of the security fix:
++
+To keep the embargo intact, security fixes cannot be developed and reviewed in
+the public `gerrit` repository. In particular it's not secure to use private
+changes for implementing and reviewing security fixes (see general notes about
+link:intro-user.html[security-fixes]).
++
+Instead security fixes should be implemented and reviewed in the non-public
+link:https://gerrit-review.googlesource.com/admin/repos/gerrit-security-fixes[
+gerrit-security-fixes] repository which is only accessible by Gerrit
+maintainers and Gerrit community members that work on security fixes.
++
+The change that fixes the security vulnerability should contain an integration
+test that verifies that the security vulnerability is no longer present.
++
+Review and approval of the security fixes must be done by the Gerrit
+maintainers. Verifications must be done manually since the Gerrit CI doesn't
+build and test changes of the `gerrit-security-fixes` repository (and it
+shouldn't because everything on the CI server is public which would break
+the embargo).
++
+Once a security fix is ready and submitted, it should be cherry-picked to all
+branches that should be fixed.
+
+. Creation of fixed releases and announcement of the security vulnerability:
++
+A release manager should create new bug fix releases for all fixed branches.
++
+The new releases should be tested against the security vulnerability to
+double-check that the release was built from the correct source that contains
+the fix for the security vulnerability.
++
+Before publishing the fixed releases, an announcement to the Gerrit community
+should be prepared. The announcement should clearly describe the security
+vulnerability, which releases are affected and which releases contain the fix.
+The announcement should recommend to upgrade to fixed releases immediately.
++
+Once all releases are ready and tested and the announcement is prepared, the
+releases should be all published at the same time. Immediately after that, the
+announcement should be sent out to the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss] mailing list.
++
+This ends the embargo and any issue that discusses the security vulnerability
+should be made public.
+
+. Follow-Up
++
+The ESC should discuss if there are any learnings from the security
+vulnerability and define action items to follow up in the
+link:https://bugs.chromium.org/p/gerrit[issue tracker].
+
+[[upgrading-libraries]]
+== Upgrading Libraries
+
+Changes that add new libraries or upgrade existing libraries require an approval on the
+`Library-Compliance` label. For an approval the following things are checked:
+
+* The library has a license that is suitable for use within Gerrit.
+* If the library is used within Google, the version of the library must be compatible with the
+ version that is used at Google.
+
+Only maintainers from Google can vote on the `Library-Compliance` label.
+
+Gerrit's library dependencies should only be upgraded if the new version contains
+something we need in Gerrit. This includes new features, API changes as well as bug
+or security fixes.
+An exception to this rule is that right after a new Gerrit release was branched
+off, all libraries should be upgraded to the latest version to prevent Gerrit
+from falling behind. Doing those upgrades should conclude at the latest two
+months after the branch was cut. This should happen on the master branch to ensure
+that they are vetted long enough before they go into a release and we can be sure
+that the update doesn't introduce a regression.
+
+[[deprecating-features]]
+== Deprecating features
+
+Gerrit should be as stable as possible and we aim to add only features that last.
+However, sometimes we are required to deprecate and remove features to be able
+to move forward with the project and keep the code-base clean. The following process
+should serve as a guideline on how to deprecate functionality in Gerrit. Its purpose
+is that we have a structured process for deprecation that users, administrators and
+developers can agree and rely on.
+
+General process:
+
+ * Make sure that the feature (e.g. a field on the API) is not needed anymore or blocks
+ further development or improvement. If in doubt, consult the mailing list.
+ * If you can provide a schema migration that moves users to a comparable feature, do
+ so and stop here.
+ * Mark the feature as deprecated in the documentation and release notes.
+ * If possible, mark the feature deprecated in any user-visible interface. For example,
+ if you are deprecating a Git push option, add a message to the Git response if
+ the user provided the option informing them about deprecation.
+ * Annotate the code with `@Deprecated` and `@RemoveAfter(x.xx)` if applicable.
+ Alternatively, use `// DEPRECATED, remove after x.xx` (where x.xx is the version
+ number that has to be branched off before removing the feature)
+ * Gate the feature behind a config that is off by default (forcing admins to turn
+ the deprecated feature on explicitly).
+ * After the next release was branched off, remove any code that backed the feature.
+
+You can optionally consult the mailing list to ask if there are users of the feature you
+wish to deprecate. If there are no major users, you can remove the feature without
+following this process and without the grace period of one release.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index c014687cb2..ad25147a95 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -3,7 +3,9 @@
To build a developer instance, you'll need link:https://bazel.build/[Bazel] to
compile the code, preferably launched with link:https://github.com/bazelbuild/bazelisk[Bazelisk].
-== Getting the Source
+== Git Setup
+
+=== Getting the Source
Create a new client workspace:
@@ -12,62 +14,88 @@ Create a new client workspace:
cd gerrit
----
-The `--recursive` option is needed on `git clone` to ensure that
-the core plugins, which are included as git submodules, are also
-cloned.
+The `--recurse-submodules` option is needed on `git clone` to ensure that the
+core plugins, which are included as git submodules, are also cloned.
+
+=== Switching between branches
+
+When using `git checkout` without `--recurse-submodules` to switch between
+branches, submodule revisions are not altered, which can result in:
+
+* Incorrect or unneeded plugin revisions.
+* Missing plugins.
+
+After you switch branches, ensure that you have the correct versions of
+the submodules.
+
+CAUTION: If you store Eclipse or IntelliJ project files in the Gerrit source
+directories, do *_not_* run `git clean -fdx`. Doing so may remove untracked files and damage your project. For more information, see
+link:https://git-scm.com/docs/git-clean[git-clean].
+
+Run the following:
+
+----
+ git submodule update
+ git clean -ffd
+----
[[compile_project]]
== Compiling
For details, see <<dev-bazel#,Building with Bazel>>.
-== Configuring Eclipse
-To use the Eclipse IDE for development, see
-link:dev-eclipse.html[Eclipse Setup].
+== Testing
-To configure the Eclipse workspace with Bazel, see
-link:dev-bazel.html#eclipse[Eclipse integration with Bazel].
+[[tests]]
+=== Running the acceptance tests
-== Configuring IntelliJ IDEA
+Gerrit contains acceptance tests that validate the Gerrit daemon via REST, SSH,
+and the Git protocol.
-See <<dev-intellij#,IntelliJ Setup>> for details.
+A new review site is created for each test and the Gerrit daemon is
+then started on that site. When the test is completed, the Gerrit daemon is
+shut down.
-== MacOS
+For instructions on running the acceptance tests with Bazel,
+see <<dev-bazel#tests,Running Unit Tests with Bazel>>.
-On MacOS, ensure that "Java for MacOS X 10.5 Update 4" (or higher) is installed
-and that `JAVA_HOME` is set to the
-link:install.html#Requirements[required Java version].
+[[e2e]]
+=== End-to-end tests
-Java installations can typically be found in
-"/System/Library/Frameworks/JavaVM.framework/Versions".
+<<dev-e2e-tests#,This document>> describes how `e2e` (load or functional) test
+scenarios are implemented using link:https://gatling.io/[`Gatling`].
-To check the installed version of Java, open a terminal window and run:
-`java -version`
+== Local server
[[init]]
-== Site Initialization
+=== Site Initialization
After you compile the project <<compile_project,(above)>>, run the Gerrit
`init`
command to create a test site:
----
+ export GERRIT_SITE=~/gerrit_testsite
$(bazel info output_base)/external/local_jdk/bin/java \
- -jar bazel-bin/gerrit.war init -d ../gerrit_testsite
+ -jar bazel-bin/gerrit.war init --batch --dev -d $GERRIT_SITE
----
[[special_bazel_java_version]]
NOTE: You must use the same Java version that Bazel used for the build, which
is available at `$(bazel info output_base)/external/local_jdk/bin/java`.
-During initialization, change two settings from the defaults:
+This command takes two parameters:
-* To ensure the development instance is not externally accessible, change the
-listen addresses from '*' to 'localhost'.
-* To allow yourself to create and act as arbitrary test accounts on your
-development instance, change the auth type from 'OPENID' to 'DEVELOPMENT_BECOME_ANY_ACCOUNT'.
+* `--batch` assigns default values to several Gerrit configuration
+ options. To learn more about these options, see
+ link:config-gerrit.html[Configuration].
+* `--dev` configures the Gerrit server to use the authentication
+ option, `DEVELOPMENT_BECOME_ANY_ACCOUNT`, which enables you to
+ switch between different users to explore how Gerrit works. To learn more
+ about setting up Gerrit for development, see
+ link:dev-readme.html[Gerrit Code Review: Developer Setup].
After initializing the test site, Gerrit starts serving in the background. A
web browser displays the Start page.
@@ -81,12 +109,12 @@ On the Start page, you can:
To shut down the daemon, run:
----
- ../gerrit_testsite/bin/gerrit.sh stop
+ $GERRIT_SITE/bin/gerrit.sh stop
----
[[localdev]]
-== Working with the Local Server
+=== Working with the Local Server
To create more accounts on your development instance:
@@ -103,32 +131,26 @@ interface, run:
git clone ssh://username@localhost:29418/projectname
----
-To create changes as users of Gerrit would, run:
+To use the `HTTP` protocol, run:
----
-git push origin HEAD:refs/for/master
+git clone http://username@localhost:8080/projectname
----
-== Testing
+The default password for user `admin` is `secret`. You can regenerate a
+password in the UI under User Settings -- HTTP credentials. The password can be
+stored locally to avoid retyping it:
-[[tests]]
-=== Running the acceptance tests
-
-Gerrit contains acceptance tests that validate the Gerrit daemon via REST, SSH,
-and the Git protocol.
-
-A new review site is created for each test and the Gerrit daemon is
-then started on that site. When the test is completed, the Gerrit daemon is
-shut down.
-
-For instructions on running the acceptance tests with Bazel,
-see <<dev-bazel#tests,Running Unit Tests with Bazel>>.
+----
+git config --global credential.helper store
+git pull
+----
-[[e2e]]
-=== End-to-end tests
+To create changes as users of Gerrit would, run:
-<<dev-e2e-tests#,This document>> describes how `e2e` (load or functional) test
-scenarios are implemented using link:https://gatling.io/[`Gatling`].
+----
+git push origin HEAD:refs/for/master
+----
[[run_daemon]]
=== Running the Daemon
@@ -138,7 +160,7 @@ copying to the test site:
----
$(bazel info output_base)/external/local_jdk/bin/java \
- -jar bazel-bin/gerrit.war daemon -d ../gerrit_testsite \
+ -jar bazel-bin/gerrit.war daemon -d $GERRIT_SITE \
--console-log
----
@@ -170,7 +192,7 @@ To start the Inspector, add the '-s' option to the daemon start command:
----
$(bazel info output_base)/external/local_jdk/bin/java \
- -jar bazel-bin/gerrit.war daemon -d ../gerrit_testsite -s
+ -jar bazel-bin/gerrit.war daemon -d $GERRIT_SITE -s
----
NOTE: To learn why using `java -jar` isn't sufficient, see
@@ -194,27 +216,24 @@ interfaces (including HTTP and SSH) are available.
CAUTION: When using the Inspector, be careful not to modify the internal state
of the system.
-== Switching between branches
-When using `git checkout` without `--recurse-submodules` to switch between
-branches, submodule revisions are not altered, which can result in:
+== Setup for backend developers
-* Incorrect or unneeded plugin revisions.
-* Missing plugins.
+=== Configuring Eclipse
-After you switch branches, ensure that you have the correct versions of
-the submodules.
+To use the Eclipse IDE for development, see
+link:dev-eclipse.html[Eclipse Setup].
-CAUTION: If you store Eclipse or IntelliJ project files in the Gerrit source
-directories, do *_not_* run `git clean -fdx`. Doing so may remove untracked files and damage your project. For more information, see
-link:https://git-scm.com/docs/git-clean[git-clean].
+To configure the Eclipse workspace with Bazel, see
+link:dev-bazel.html#eclipse[Eclipse integration with Bazel].
-Run the following:
+=== Configuring IntelliJ IDEA
+
+See <<dev-intellij#,IntelliJ Setup>> for details.
+
+== Setup for frontend developers
+See link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/README.md[Frontend Developer Setup].
-----
- git submodule update
- git clean -ffd
-----
GERRIT
------
diff --git a/Documentation/dev-release-deploy-config.txt b/Documentation/dev-release-deploy-config.txt
index 541192775a..98a3df572d 100644
--- a/Documentation/dev-release-deploy-config.txt
+++ b/Documentation/dev-release-deploy-config.txt
@@ -91,7 +91,7 @@ Jar.
* `gerrit-maven`:
+
-Bucket to store Gerrit Subproject Artifacts (e.g. `gwtorm` etc.).
+Bucket to store Gerrit Subproject Artifacts (e.g. Prolog Cafe).
To upload artifacts to a bucket the user must authenticate with a
username and password. The username and password need to be retrieved
diff --git a/Documentation/dev-release-jgit.txt b/Documentation/dev-release-jgit.txt
deleted file mode 100644
index 1a8b501a3c..0000000000
--- a/Documentation/dev-release-jgit.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-= Making a Snapshot Release of JGit
-
-This step is only necessary if we need to create an unofficial JGit
-snapshot release and publish it to the
-link:https://developers.google.com/storage/[Google Cloud Storage].
-
-[[prepare-environment]]
-== Prepare the Maven Environment
-
-First, make sure you have done the necessary
-link:dev-release-deploy-config.html#deploy-configuration-settings-xml[
-configuration in Maven `settings.xml`].
-
-To apply the necessary settings in JGit's `pom.xml`, follow the instructions
-in link:dev-release-deploy-config.html#deploy-configuration-subprojects[
-Configuration for Subprojects in `pom.xml`], or apply the provided diff by
-executing the following command in the JGit workspace:
-
-----
- git apply /path/to/gerrit/tools/jgit-snapshot-deploy-pom.diff
-----
-
-[[prepare-release]]
-== Prepare the Release
-
-Since JGit has its own release process we do not push any release tags. Instead
-we will use the output of `git describe` as the version of the current JGit
-snapshot.
-
-In the JGit workspace, execute the following command:
-
-----
- ./tools/version.sh --release $(git describe)
-----
-
-[[publish-release]]
-== Publish the Release
-
-To deploy the new snapshot, execute the following command in the JGit
-workspace:
-
-----
- mvn deploy
-----
-
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index c10457d84e..2131d00a69 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -150,7 +150,7 @@ Setting `GPG_TTY` this way or similar might also be necessary:
Tag the plugins:
----
- git submodule foreach git tag -s -m "v$version" "v$version"
+ git submodule foreach '[ "$path" == "modules/jgit" ] || git tag -s -m "v$version" "v$version"'
----
[[build-gerrit]]
@@ -409,7 +409,7 @@ master branch that were gated on the next release. Mostly, these are
feature-deprecations that we were holding off on to have a stable release where
the feature is still contained, but marked as deprecated.
-See link:dev-contributing.html#deprecating-features[Deprecating features] for
+See link:dev-processes.html#deprecating-features[Deprecating features] for
details.
GERRIT
diff --git a/Documentation/dev-roles.txt b/Documentation/dev-roles.txt
new file mode 100644
index 0000000000..9dbc450696
--- /dev/null
+++ b/Documentation/dev-roles.txt
@@ -0,0 +1,378 @@
+= Gerrit Code Review - Supporting Roles
+
+As an open source project Gerrit has a large community of people
+driving the project forward. There are many ways to engage with
+the project and get involved.
+
+[[supporter]]
+== Supporter
+
+Supporters are individuals who help the Gerrit project and the Gerrit
+community in any way. This includes users that provide feedback to the
+Gerrit community or get in touch by other means.
+
+There are many possibilities to support the project, e.g.:
+
+* get involved in discussions on the
+ link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+ mailing list (post your questions, provide feedback, share your
+ experiences, help other users)
+* attend community events like user summits (see
+ link:https://calendar.google.com/calendar?cid=Z29vZ2xlLmNvbV91YmIxcGxhNmlqNzg1b3FianI2MWg0dmRpc0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t[
+ community calendar])
+* report link:https://bugs.chromium.org/p/gerrit/issues/list[issues]
+ and help to clarify existing issues
+* provide feedback on
+ link:https://www.gerritcodereview.com/releases-readme.html[new
+ releases and release candidates]
+* review
+ link:https://gerrit-review.googlesource.com/q/status:open[changes]
+ and help to verify that they work as advertised, comment if you like
+ or dislike a feature
+* serve as contact person for a proprietary Gerrit installation and
+ channel feedback from users back to the Gerrit community
+
+Supporters can:
+
+* post on the
+ link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+ mailing list (Please note that the `repo-discuss` mailing list is
+ managed to prevent spam posts. This means posts from new participants
+ must be approved manually before they appear on the mailing list.
+ Approvals normally happen within 1 work day. Posts of people who
+ participate in mailing list discussions frequently are approved
+ automatically)
+* comment on
+ link:https://gerrit-review.googlesource.com/q/status:open[changes]
+ and vote from `-1` to `+1` on the `Code-Review` label (these votes
+ are important to understand the interest in a change and to address
+ concerns early, however link:#maintainer[maintainers] can
+ overrule/ignore these votes)
+* download changes to try them out, feedback can be provided as
+ comments and by voting (preferably on the `Verified` label,
+ permissions to vote on the `Verified` label are granted by request,
+ see below)
+* file issues in the link:https://bugs.chromium.org/p/gerrit/issues/list[
+ issue tracker] and comment on existing issues
+* support the
+ link:dev-processes.html#design-driven-contribution-process[
+ design-driven contribution process] by reviewing incoming
+ link:dev-design-docs.html[design docs] and raising concerns during
+ the design review
+
+Supporters who want to engage further can get additional privileges
+on request (ask for it on the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+mailing list):
+
+* become member of the `gerrit-verifiers` group, which allows to:
+** vote on the `Verified` and `Code-Style` labels
+** edit hashtags on all changes
+** edit topics on all open changes
+** abandon changes
+* approve posts to the
+ link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+ mailing list
+* administrate issues in the
+ link:https://bugs.chromium.org/p/gerrit/issues/list[issue tracker]
+
+Supporters can become link:#contributor[contributors] by signing a
+contributor license agreement and contributing code to the Gerrit
+project.
+
+[[contributor]]
+== Contributor
+
+Everyone who has a valid link:dev-cla.html[contributor license
+agreement] and who has link:dev-contributing.html[contributed] at least
+one change to any project on
+link:https://gerrit-review.googlesource.com/[
+gerrit-review.googlesource.com] is a contributor.
+
+Contributions can be:
+
+* new features
+* bug fixes
+* code cleanups
+* documentation updates
+* release notes updates
+* propose link:#dev-design-docs[design docs] as part of the
+ link:dev-contributing.html#design-driven-contribution-process[
+ design-driven contribution process]
+* scripts which are of interest to the community
+
+Contributors have all the permissions that link:#supporter[supporters]
+have. In addition they have signed a link:dev-cla.html[contributor
+license agreement] which enables them to push changes.
+
+Regular contributors can ask to be added to the `gerrit-verifiers`
+group, which allows to:
+
+* add patch sets to changes of other users
+* propose project config changes (push changes for the
+ `refs/meta/config` branch
+
+Being member of the `gerrit-verifiers` group includes further
+permissions (see link:#supporter[supporter] section above).
+
+It's highly appreciated if contributors engage in code reviews,
+link:dev-design-docs.html#review[design reviews] and mailing list
+discussions. If wanted, contributors can also serve as link:#mentor[
+mentors] to support other contributors with getting their features
+done.
+
+Contributors may also be invited to join the Gerrit hackathons which
+happen regularly (e.g. twice a year). Hackathons are announced on the
+link:https://groups.google.com/d/forum/repo-discuss[repo-discuss]
+mailing list (also see
+link:https://calendar.google.com/calendar?cid=Z29vZ2xlLmNvbV91YmIxcGxhNmlqNzg1b3FianI2MWg0dmRpc0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t[
+community calendar]).
+
+Outstanding contributors that are actively engaged in the community, in
+activities outlined above, may be nominated as link:#maintainer[
+maintainers].
+
+[[maintainer]]
+== Maintainer
+
+Maintainers are the gatekeepers of the project and are in charge of
+approving and submitting changes. Refer to the project homepage for
+the link:https://www.gerritcodereview.com/members.html#maintainers[
+list of current maintainers].
+
+Maintainers should only approve changes that:
+
+* they fully understand
+* are in line with the project vision and project scope that are
+ defined by the link:dev-processes.html#steering-committee[engineering steering
+ committee], and should consult them, when in doubt
+* meet the quality expectations of the project (well-tested, properly
+ documented, scalable, backwards-compatible)
+* implement usable features or bug fixes (no incomplete/unusable
+ things)
+* are not authored by themselves (exceptions are changes which are
+ trivial according to the judgment of the maintainer and changes that
+ are required by the release process and branch management)
+
+Maintainers are trusted to assess changes, but are also expected to
+align with the other maintainers, especially if large new features are
+being added.
+
+Maintainers are highly encouraged to dedicate some of their time to the
+following tasks (but are not required to do so):
+
+* reviewing changes
+* mailing list discussions and support
+* bug fixing and bug triaging
+* supporting the
+ link:dev-processes.html#design-driven-contribution-process[
+ design-driven contribution process] by reviewing incoming
+ link:dev-design-docs.html[design docs] and raising concerns during
+ the design review
+* serving as link:#mentor[mentor]
+* doing releases (see link#release-manager[release manager])
+
+Maintainers can:
+
+* approve changes (vote `+2` on the `Code-Review` label); when
+ approving changes, `-1` votes on the `Code-Review` label can be
+ ignored if there is a good reason, in this case the reason should be
+ clearly communicated on the change
+* submit changes
+* block submission of changes if they disagree with how a feature is
+ being implemented (vote `-2` on the `Code-Review` label), but their
+ vote can be overruled by the steering committee, see
+ link:dev-processes.html#project-governance[Project Governance]
+* nominate new maintainers and vote on nominations (see below)
+* administrate the link:https://groups.google.com/d/forum/repo-discuss[
+ mailing list], the
+ link:https://bugs.chromium.org/p/gerrit/issues/list[issue tracker]
+ and the link:https://www.gerritcodereview.com/[homepage]
+* gain permissions to do Gerrit releases and publish release artifacts
+* create new projects and groups on
+ link:https://gerrit-review.googlesource.com/[
+ gerrit-review.googlesource.com]
+* administrate the Gerrit projects on
+ link:https://gerrit-review.googlesource.com/[
+ gerrit-review.googlesource.com] (e.g. edit ACLs, update project
+ configuration)
+* create events in the
+ link:https://calendar.google.com/calendar?cid=Z29vZ2xlLmNvbV91YmIxcGxhNmlqNzg1b3FianI2MWg0dmRpc0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t[
+ community calendar]
+* discuss with other maintainers on the private maintainers mailing
+ list and Slack channel
+
+In addition, maintainers from Google can:
+
+* approve/reject changes that update project dependencies (vote `-1` to
+ `+1` on the `Library-Compliance` label), see
+ link:dev-processes.html#upgrading-libraries[Upgrading Libraries]
+* edit permissions on the Gerrit core projects
+
+[[maintainer-election]]
+Maintainers can nominate new maintainers by posting a nomination on the
+non-public maintainers mailing list. Nominations should stay open for
+at least 14 calendar days so that all maintainers have a chance to
+vote. To be approved as maintainer a minimum of 5 positive votes and no
+negative votes is required. This means if 5 positive votes without
+negative votes have been reached and 14 calendar days have passed, any
+maintainer can close the vote and welcome the new maintainer. Extending
+the voting period during holiday season or if there are not enough
+votes is possible, but the voting period should not exceed 1 month. If
+there are negative votes that are considered unjustified, the
+link:dev-processes.html#steering-committee[engineering steering
+committee] may get involved to decide whether the new maintainer can be
+accepted anyway.
+
+To become a maintainer, a link:#contributor[contributor] should have a
+history of deep technical contributions across different parts of the
+core Gerrit codebase. However, it is not required to be an expert on
+everything. Things that we want to see from potential maintainers
+include:
+
+* high quality code contributions
+* high quality code reviews
+* activity on the mailing list
+
+[[steering-committee-member]]
+== Engineering Steering Committee Member
+
+The Gerrit project has an Engineering Steering Committee (ESC) that
+governs the project, see link:dev-processes.html#project-governance[Project Governance].
+
+Members of the steering committee are expected to act in the interest
+of the Gerrit project and the whole Gerrit community. Refer to the project
+homepage for the link:https://www.gerritcodereview.com/members.html#engineering-steering-committee[
+list of current committee members].
+
+For those that are familiar with scrum, the steering committee member
+role is similar to the role of an agile product owner.
+
+Steering committee members must be able to dedicate sufficient time to
+their role so that the steering committee can satisfy its
+responsibilities and live up to the promise of answering incoming
+requests in a timely manner.
+
+Community members may submit new items under the
+link:https://bugs.chromium.org/p/gerrit/issues/list?q=component:ESC[ESC component]
+in the issue tracker, or add that component to existing items, to raise them to
+the attention of ESC members.
+
+Community members may contact the ESC members directly using
+mailto:gerritcodereview-esc@googlegroups.com[this mailing list].
+This is a group that remains private between the individual community
+member and ESC members.
+
+link:#maintainer[Maintainers] can become steering committee member by
+election, or by being appointed by Google (only for the seats that
+belong to Google).
+
+[[mentor]]
+== Mentor
+
+A mentor is a link:#maintainer[maintainer] or link:#contributor[
+contributor] who is assigned to support the development of a feature
+that was specified in a link:dev-design-docs.html[design doc] and was
+approved by the link:dev-processes.html#steering-committee[steering
+committee].
+
+The goal of the mentor is to make the feature successful by:
+
+* doing timely reviews
+* providing technical guidance during code reviews
+* discussing details of the design
+* ensuring that the quality standards are met (well documented,
+ sufficient test coverage, backwards compatible etc.)
+
+The implementation is fully done by the contributor, but optionally
+mentors can help out with contributing some changes.
+
+link:#maintainer[Maintainers] and link:#contributor[contributors] can
+volunteer to generally serve as mentors, or to mentor specific features
+(e.g. if they see an upcoming feature on the roadmap that they are
+interested in). To volunteer as mentor, contact the
+link:dev-processes.html#steering-committee[steering committee] or
+comment on a change that adds a link:dev-design-docs.html#propose[
+design doc].
+
+[[community-manager]]
+== Community Manager
+
+Community managers should act as stakeholders for the Gerrit community
+and focus on the health of the community. Refer to the project homepage
+for the link:https://www.gerritcodereview.com/members.html#community-managers[
+list of current community managers].
+
+Tasks:
+
+* act as stakeholder for the Gerrit community towards the
+ link:dev-processes.html#steering-committee[steering committee]
+* ensure that the link:dev-contributing.html#mentorship[mentorship
+ process] works
+* deescalate conflicts in the Gerrit community
+* constantly improve community processes (e.g. contribution process)
+* watch out for community issues and address them proactively
+* serve as contact person for community issues
+
+Community members may submit new items under the
+link:https://bugs.chromium.org/p/gerrit/issues/list?q=component:Community[Community component]
+backlog, for community managers to refine. Only public topics should be
+issued through that backlog.
+
+Sensitive topics are to be privately discussed using
+mailto:gerritcodereview-community-managers@googlegroups.com[this mailing list].
+This is a group that remains private between the individual community
+member and community managers.
+
+The community managers should be a pair or trio that shares the work:
+
+* One Googler that is appointed by Google.
+* One or two non-Googlers, elected by the community if there are more
+ than two candidates. If there is no candidate, we only have the one
+ community manager from Google.
+
+Community managers must not be link:#steering-committee-member[
+steering committee members] at the same time so that they can represent
+the community without conflict of interest.
+
+Anybody from the Gerrit community can candidate as community manager.
+This means, in contrast to candidating for the ESC, candidating as
+community manager is not limited to Gerrit maintainers. Otherwise the
+nomination process, election process and election period for the
+non-Google community manager are the same as for
+link:dev-processes.html#steering-committee-election[steering committee
+members].
+
+[[release-manager]]
+== Release Manager
+
+Each major Gerrit release is driven by a Gerrit link:#maintainer[
+maintainer], the so called release manager.
+
+The release manager is responsible for:
+
+* identifying release blockers and informing about them
+* creating stable branches and updating version numbers
+* creating release candidates, the final major release and minor
+ releases
+* announcing releases on the mailing list and collecting feedback
+* ensuring that releases meet minimal quality expectations (Gerrit
+ starts, upgrade from previous version works)
+* publishing release artifacts
+* ensuring quality and completeness of the release notes
+* cherry-picking bug fixes, see link:dev-processes.html#backporting[
+ Backporting to stable branches]
+* estimating the risk of new features that are added on stable
+ branches, see link:dev-processes.html#dev-in-stable-branches[
+ Development in stable branches]
+
+Before each release, the release manager is appointed by consensus among
+the maintainers. Volunteers are welcome, but it's also a goal to fairly
+share this work between maintainers and contributing companies.
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/dev-starter-projects.txt b/Documentation/dev-starter-projects.txt
new file mode 100644
index 0000000000..ae40ea645f
--- /dev/null
+++ b/Documentation/dev-starter-projects.txt
@@ -0,0 +1,14 @@
+= Gerrit Code Review - Starter Projects
+
+We have created a
+link:https://bugs.chromium.org/p/gerrit/issues/list?can=2&q=label%3AStarterProject[StarterProject]
+category in the issue tracker and try to assign easy hack projects to it. If in
+doubt, do not hesitate to ask on the developer
+link:https://groups.google.com/forum/#!forum/repo-discuss[mailing list].
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-change-does-not-belong-to-project.txt b/Documentation/error-change-does-not-belong-to-project.txt
deleted file mode 100644
index 21596b1467..0000000000
--- a/Documentation/error-change-does-not-belong-to-project.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-= change ... does not belong to project ...
-
-With this error message Gerrit rejects to push a commit to a change
-that belongs to another project.
-
-This error message means that the user explicitly pushed a commit to
-a change that belongs to another project by specifying it as target
-ref. This way of adding a new patch set to a change is deprecated as
-explained link:user-upload.html#manual_replacement_mapping[here]. It is recommended to only rely on Change-Ids for
-link:user-upload.html#push_replace[replacing changes].
-
-
-GERRIT
-------
-Part of link:error-messages.html[Gerrit Error Messages]
-
-SEARCHBOX
----------
diff --git a/Documentation/error-change-not-found.txt b/Documentation/error-change-not-found.txt
deleted file mode 100644
index df99388122..0000000000
--- a/Documentation/error-change-not-found.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-= change ... not found
-
-With this error message Gerrit rejects to push a commit to a change
-that cannot be found.
-
-This error message means that the user explicitly pushed a commit to
-a non-existing change by specifying it as target ref. This way of
-adding a new patch set to a change is deprecated as explained link:user-upload.html#manual_replacement_mapping[here].
-It is recommended to only rely on Change-Ids for link:user-upload.html#push_replace[replacing changes].
-
-
-GERRIT
-------
-Part of link:error-messages.html[Gerrit Error Messages]
-
-SEARCHBOX
----------
diff --git a/Documentation/error-messages.txt b/Documentation/error-messages.txt
index b52366342b..eedae39fde 100644
--- a/Documentation/error-messages.txt
+++ b/Documentation/error-messages.txt
@@ -9,8 +9,6 @@ occurring and what can be done to solve it.
* link:error-branch-not-found.html[branch ... not found]
* link:error-change-closed.html[change ... closed]
-* link:error-change-does-not-belong-to-project.html[change ... does not belong to project ...]
-* link:error-change-not-found.html[change ... not found]
* link:error-commit-already-exists.html[commit already exists]
* link:error-contains-banned-commit.html[contains banned commit ...]
* link:error-has-duplicates.html[... has duplicates]
@@ -35,7 +33,6 @@ occurring and what can be done to solve it.
* link:error-same-change-id-in-multiple-changes.html[same Change-Id in multiple changes]
* link:error-too-many-commits.html[too many commits]
* link:error-upload-denied.html[Upload denied for project \'...']
-* link:error-push-refschanges-not-allowed.html[upload to refs/changes not allowed]
* link:error-not-allowed-to-upload-merges.html[you are not allowed to upload merges]
diff --git a/Documentation/error-push-refschanges-not-allowed.txt b/Documentation/error-push-refschanges-not-allowed.txt
deleted file mode 100644
index 2bbdc3e85a..0000000000
--- a/Documentation/error-push-refschanges-not-allowed.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-= upload to refs/changes not allowed
-
-Pushing to `refs/changes/` is deprecated and is not allowed on this Gerrit server.
-See the documentation for link:user-upload.html#push_create[creating changes] for
-alternate ways to push to existing changes.
-
-
-GERRIT
-------
-Part of link:error-messages.html[Gerrit Error Messages]
-
-SEARCHBOX
----------
diff --git a/Documentation/i18n-readme.txt b/Documentation/i18n-readme.txt
deleted file mode 100644
index 180fc5385b..0000000000
--- a/Documentation/i18n-readme.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-= Gerrit Code Review - i18n
-
-Aside from actually writing translations, there are some issues with
-the way the code produces output. Most of the UI should support
-right-to-left (RTL) languages.
-
-== Labels
-
-Labels and their values are defined in project.config by the Gerrit
-administrator or project owners. Only a single translation of these
-strings is supported.
-
-== /Gerrit Gerrit.html
-
-* The title of the host page is not translated.
-
-* The <noscript> tag is not translated.
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/index.txt b/Documentation/index.txt
index f9e39b5c2a..4de55a75b4 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -10,7 +10,11 @@
. link:intro-how-gerrit-works.html[How Gerrit Works]
. link:intro-gerrit-walkthrough.html[Basic Gerrit Walkthrough]
-== Guides
+== Contributor Guides
+. link:dev-community.html[Gerrit Community]
+. link:dev-community.html#how-to-contribute[How to Contribute]
+
+== User Guides
. link:intro-user.html[User Guide]
. link:intro-project-owner.html[Project Owner Guide]
. link:https://source.android.com/source/developing[Default Android Workflow] (external)
@@ -73,28 +77,6 @@
. link:config-accounts.html[Accounts on NoteDb]
. link:config-groups.html[Groups on NoteDb]
-== Developer
-. Getting Started
-.. link:dev-readme.html[Developer Setup]
-.. link:dev-bazel.html[Building with Bazel]
-.. link:dev-eclipse.html[Eclipse Setup]
-.. link:dev-intellij.html[IntelliJ Setup]
-.. link:dev-contributing.html[Contributing to Gerrit]
-. Plugin Development
-.. link:dev-plugins.html[Developing Plugins]
-.. link:dev-build-plugins.html[Building Gerrit plugins]
-.. link:js-api.html[JavaScript Plugin API]
-.. link:config-validation.html[Validation Interfaces]
-.. link:dev-stars.html[Starring Changes]
-.. link:quota.html[Quota Enforcement]
-. link:dev-design.html[System Design]
-. link:i18n-readme.html[i18n Support]
-
-== Maintainer
-. link:dev-release.html[Making a Gerrit Release]
-. link:dev-release-subproject.html[Making a Release of a Gerrit Subproject]
-. link:dev-release-jgit.html[Making a Release of JGit]
-
== Concepts
. link:config-labels.html[Review Labels]
. link:access-control.html[Access Controls]
@@ -105,7 +87,7 @@
== Resources
* link:licenses.html[Licenses and Notices]
* link:https://www.gerritcodereview.com/[Homepage]
-* link:https://www.gerritcodereview.com/download/index.html[Downloads]
+* link:https://gerrit-releases.storage.googleapis.com/index.html[Downloads]
* link:https://bugs.chromium.org/p/gerrit/issues/list[Issue Tracking]
* link:https://gerrit.googlesource.com/gerrit[Source Code]
* link:https://www.gerritcodereview.com/about.md[A History of Gerrit Code Review]
diff --git a/Documentation/install.txt b/Documentation/install.txt
index aaefc86fbc..09ebbba6cd 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -46,7 +46,7 @@ found in `README.txt`.
== Download Gerrit
Current and past binary releases of Gerrit can be obtained from
-the link:https://www.gerritcodereview.com/download/index.html[
+the link:https://gerrit-releases.storage.googleapis.com/index.html[
Gerrit Releases site].
Download any current `*.war` package. The war will be referred to as
diff --git a/Documentation/intro-gerrit-walkthrough.txt b/Documentation/intro-gerrit-walkthrough.txt
index 1fba1dc04b..b4f799c220 100644
--- a/Documentation/intro-gerrit-walkthrough.txt
+++ b/Documentation/intro-gerrit-walkthrough.txt
@@ -28,7 +28,7 @@ project he works on. His first step is to get the source code that he wants to
modify. To get this code, he runs the following `git clone` command:
----
-clone ssh://gerrithost:29418/RecipeBook.git RecipeBook
+git clone ssh://gerrithost:29418/RecipeBook.git RecipeBook
----
After he clones the repository, he runs a couple of commands to add a
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index 04778f1e8f..a54774bf2c 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -726,36 +726,6 @@ The user's preferences are stored in a `git config` style file named
The following preferences can be configured:
-- [[review-category]]`Display In Review Category`:
-+
-This setting controls how the values of the review labels in change
-lists and dashboards are visualized.
-+
-** `None`:
-+
-For each review label only the voting value is shown. Approvals are
-rendered as a green check mark icon, vetoes as a red X icon.
-+
-** `Show Name`:
-+
-For each review label the voting value is shown together with the full
-name of the voting user.
-+
-** `Show Email`:
-+
-For each review label the voting value is shown together with the email
-address of the voting user.
-+
-** `Show Username`:
-+
-For each review label the voting value is shown together with the
-username of the voting user.
-+
-** `Show Abbreviated Name`:
-+
-For each review label the voting value is shown together with the
-initials of the full name of the voting user.
-
- [[page-size]]`Maximum Page Size`:
+
The maximum number of entries that are shown on one page, e.g. used
diff --git a/Documentation/js-api.txt b/Documentation/js-api.txt
index 4ef2a6ce8d..030541d43b 100644
--- a/Documentation/js-api.txt
+++ b/Documentation/js-api.txt
@@ -24,123 +24,17 @@ Gerrit.install(function (self) {
The plugin instance is passed to the plugin's initialization function
and provides a number of utility services to plugin authors.
-[[self_delete]]
-=== self.delete() / self.del()
-Issues a DELETE REST API request to the Gerrit server.
-
-.Signature
-[source,javascript]
-----
-Gerrit.delete(url, callback)
-Gerrit.del(url, callback)
-----
-
-* url: URL relative to the plugin's URL space. The JavaScript
- library prefixes the supplied URL with `/plugins/{getPluginName}/`.
-
-* callback: JavaScript function to be invoked with the parsed
- JSON result of the API call. DELETE methods often return
- `204 No Content`, which is passed as null.
-
-[[self_get]]
-=== self.get()
-Issues a GET REST API request to the Gerrit server.
-
-.Signature
-[source,javascript]
-----
-self.get(url, callback)
-----
-
-* url: URL relative to the plugin's URL space. The JavaScript
- library prefixes the supplied URL with `/plugins/{getPluginName}/`.
-
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
-
[[self_getServerInfo]]
=== self.getServerInfo()
Returns the server's link:rest-api-config.html#server-info[ServerInfo]
data.
-[[self_getCurrentUser]]
-=== self.getCurrentUser()
-Returns the currently signed in user's AccountInfo data; empty account
-data if no user is currently signed in.
-
-[[Gerrit_getUserPreferences]]
-=== Gerrit.getUserPreferences()
-Returns the preferences of the currently signed in user; the default
-preferences if no user is currently signed in.
-
-[[Gerrit_refreshUserPreferences]]
-=== Gerrit.refreshUserPreferences()
-Refreshes the preferences of the current user.
-
[[self_getPluginName]]
=== self.getPluginName()
Returns the name this plugin was installed as by the server
administrator. The plugin name is required to access REST API
views installed by the plugin, or to access resources.
-[[self_post]]
-=== self.post()
-Issues a POST REST API request to the Gerrit server.
-
-.Signature
-[source,javascript]
-----
-self.post(url, input, callback)
-----
-
-* url: URL relative to the plugin's URL space. The JavaScript
- library prefixes the supplied URL with `/plugins/{getPluginName}/`.
-
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-self.post(
- '/my-servlet',
- {start_build: true, platform_type: 'Linux'},
- function (r) {});
-----
-
-[[self_put]]
-=== self.put()
-Issues a PUT REST API request to the Gerrit server.
-
-.Signature
-[source,javascript]
-----
-self.put(url, input, callback)
-----
-
-* url: URL relative to the plugin's URL space. The JavaScript
- library prefixes the supplied URL with `/plugins/{getPluginName}/`.
-
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-self.put(
- '/builds',
- {start_build: true, platform_type: 'Linux'},
- function (r) {});
-----
-
[[self_on]]
=== self.on()
Register a JavaScript callback to be invoked when events occur within
@@ -149,7 +43,7 @@ the web interface.
.Signature
[source,javascript]
----
-Gerrit.on(event, callback);
+self.on(event, callback);
----
* event: A supported event type. See below for description.
@@ -194,39 +88,26 @@ Supported events:
This event can be used to register a new language highlighter with
the highlight.js library before syntax highlighting begins.
-[[self_onAction]]
-=== self.onAction()
-Register a JavaScript callback to be invoked when the user clicks
-on a button associated with a server side `UiAction`.
+[[self_changeActions]]
+=== self.changeActions()
+Returns an instance of ChangeActions API.
.Signature
[source,javascript]
----
-self.onAction(type, view_name, callback);
+self.changeActions();
----
-* type: `'change'`, `'edit'`, `'revision'`, `'project'`, or `'branch'`
- indicating which type of resource the `UiAction` was bound to
- in the server.
-
-* view_name: string appearing in URLs to name the view. This is the
- second argument of the `get()`, `post()`, `put()`, and `delete()`
- binding methods in a `RestApiModule`.
-
-* callback: JavaScript function to invoke when the user clicks. The
- function will be passed a link:#ActionContext[action context].
-
[[self_screen]]
=== self.screen()
-Register a JavaScript callback to be invoked when the user navigates
+Register a module to be attached when the user navigates
to an extension screen provided by the plugin. Extension screens are
usually linked from the link:dev-plugins.html#top-menu-extensions[top menu].
-The callback can populate the DOM with the screen's contents.
.Signature
[source,javascript]
----
-self.screen(pattern, callback);
+self.screen(pattern, opt_moduleName);
----
* pattern: URL token pattern to identify the screen. Argument can be
@@ -234,52 +115,34 @@ self.screen(pattern, callback);
If a RegExp is used the matching groups will be available inside of
the context as `token_match`.
-* callback: JavaScript function to invoke when the user navigates to
+* opt_moduleName: The module to load when the user navigates to
the screen. The function will be passed a link:#ScreenContext[screen context].
-[[self_settingsScreen]]
-=== self.settingsScreen()
-Register a JavaScript callback to be invoked when the user navigates
-to an extension settings screen provided by the plugin. Extension settings
-screens are automatically linked from the settings menu under the given
-menu entry.
-The callback can populate the DOM with the screen's contents.
+[[self_settings]]
+=== self.settings()
+Returns the Settings API.
.Signature
[source,javascript]
----
-self.settingsScreen(path, menu, callback);
+self.settings();
----
-* path: URL path to identify the settings screen.
-
-* menu: The name of the menu entry in the settings menu that should
- link to the settings screen.
-
-* callback: JavaScript function to invoke when the user navigates to
- the settings screen. The function will be passed a
- link:#SettingsScreenContext[settings screen context].
-
-[[self_panel]]
-=== self.panel()
-Register a JavaScript callback to be invoked when a screen with the
-given extension point is loaded.
-The callback can populate the DOM with the panel's contents.
+[[self_registerCustomComponent]]
+=== self.registerCustomComponent()
+Register a custom component to a specific endpoint.
.Signature
[source,javascript]
----
-self.panel(extensionpoint, callback);
+self.registerCustomComponent(endpointName, opt_moduleName, opt_options);
----
-* extensionpoint: The name of the extension point that marks the
- position where the panel is added to an existing screen. The
- available extension points are described in the
- link:dev-plugins.html#panels[plugin development documentation].
+* endpointName: The endpoint this plugin should be reigistered to.
+
+* opt_moduleName: The module name the custom component will use.
-* callback: JavaScript function to invoke when a screen with the
- extension point is loaded. The function will be passed a
- link:#PanelContext[panel context].
+* opt_options: Options to register this custom component.
[[self_url]]
=== self.url()
@@ -293,398 +156,260 @@ self.url(); // "https://gerrit-review.googlesource.com/plugin
self.url('/static/icon.png'); // "https://gerrit-review.googlesource.com/plugins/demo/static/icon.png"
----
+[[self_restApi]]
+=== self.restApi()
+Returns an instance of the Plugin REST API.
-[[ActionContext]]
-== Action Context
-A new action context is passed to the `onAction` callback function
-each time the associated action button is clicked by the user. A
-context is initialized with sufficient state to issue the associated
-REST API RPC.
+.Signature
+[source,javascript]
+----
+self.restApi(prefix_url)
+----
-[[context_action]]
-=== context.action
-An link:rest-api-changes.html#action-info[ActionInfo] object instance
-supplied by the server describing the UI button the user used to
-invoke the action.
+* prefix_url: Base url for subsequent .get(), .post() etc requests.
-[[context_call]]
-=== context.call()
-Issues the REST API call associated with the action. The HTTP method
-used comes from `context.action.method`, hiding the JavaScript from
-needing to care.
+[[PluginRestAPI]]
+== Plugin Rest API
+
+[[plugin_rest_delete]]
+=== restApi.delete()
+Issues a DELETE REST API request to the Gerrit server.
+Returns a promise with the response of the request.
.Signature
[source,javascript]
----
-context.call(input, callback)
+restApi.delete(url)
----
-* input: JavaScript object to serialize as the request payload. This
- parameter is ignored for GET and DELETE methods.
+* url: URL relative to the base url.
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
+[[plugin_rest_get]]
+=== restApi.get()
+Issues a GET REST API request to the Gerrit server.
+Returns a promise with the response of the request.
+.Signature
[source,javascript]
----
-context.call(
- {message: "..."},
- function (result) {
- // ... use result here ...
- });
+restApi.get(url)
----
-[[context_change]]
-=== context.change
-When the action is invoked on a change a
-link:rest-api-changes.html#change-info[ChangeInfo] object instance
-describing the change. Available fields of the ChangeInfo may vary
-based on the options used by the UI when it loaded the change.
+* url: URL relative to the base url.
-[[context_delete]]
-=== context.delete()
-Issues a DELETE REST API call to the URL associated with the action.
+[[plugin_rest_post]]
+=== restApi.post()
+Issues a POST REST API request to the Gerrit server.
+Returns a promise with the response of the request.
.Signature
[source,javascript]
----
-context.delete(callback)
+restApi.post(url, opt_payload, opt_errFn, opt_contentType)
----
-* callback: JavaScript function to be invoked with the parsed
- JSON result of the API call. DELETE methods often return
- `204 No Content`, which is passed as null.
+* url: URL relative to the base url.
+
+* opt_payload: JavaScript object to serialize as the request payload.
+
+* opt_errFn: JavaScript function to be invoked when error occured.
+
+* opt_contentType: Content-Type to be sent along with the request.
[source,javascript]
----
-context.delete(function () {});
+restApi.post(
+ '/my-servlet',
+ {start_build: true, platform_type: 'Linux'});
----
-[[context_get]]
-=== context.get()
-Issues a GET REST API call to the URL associated with the action.
+[[plugin_rest_put]]
+=== restApi.put()
+Issues a PUT REST API request to the Gerrit server.
+Returns a promise with the response of the request.
.Signature
[source,javascript]
----
-context.get(callback)
+restApi.put(url, opt_payload, opt_errFn, opt_contentType)
----
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
+* url: URL relative to the base url.
+
+* opt_payload: JavaScript object to serialize as the request payload.
+
+* opt_errFn: JavaScript function to be invoked when error occured.
+
+* opt_contentType: Content-Type to be sent along with the request.
[source,javascript]
----
-context.get(function (result) {
- // ... use result here ...
-});
+restApi.put(
+ '/builds',
+ {start_build: true, platform_type: 'Linux'});
----
-[[context_go]]
-=== context.go()
-Go to a screen. Shorthand for link:#Gerrit_go[`Gerrit.go()`].
+[[ChangeActions]]
+== Change Actions API
+A new Change Actions API instance will be created when `changeActions()`
+is invoked.
-[[context_hide]]
-=== context.hide()
-Hide the currently visible popup displayed by
-link:#context_popup[`context.popup()`].
-
-[[context_post]]
-=== context.post()
-Issues a POST REST API call to the URL associated with the action.
+[[change_actions_add]]
+=== changeActions.add()
+Adds a new action to the change actions section.
+Returns the key of the newly added action.
.Signature
[source,javascript]
----
-context.post(input, callback)
+changeActions.add(type, label)
----
-* input: JavaScript object to serialize as the request payload.
+* type: The type of the action, either `change` or `revision`.
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
+* label: The label to be used in UI for this action.
[source,javascript]
----
-context.post(
- {message: "..."},
- function (result) {
- // ... use result here ...
- });
+changeActions.add("change", "test")
----
-[[context_popup]]
-=== context.popup()
-
-Displays a small popup near the activation button to gather
-additional input from the user before executing the REST API RPC.
-
-The caller is always responsible for closing the popup with
-link#context_hide[`context.hide()`]. Gerrit will handle closing a
-popup if the user presses `Escape` while keyboard focus is within
-the popup.
+[[change_actions_remove]]
+=== changeActions.remove()
+Removes an action from the change actions section.
.Signature
[source,javascript]
----
-context.popup(element)
+changeActions.remove(key)
----
-* element: an HTML DOM element to display as the body of the
- popup. This is typically a `div` element but can be any valid HTML
- element. CSS can be used to style the element beyond the defaults.
+* key: The key of the action.
-A common usage is to gather more input:
+[[change_actions_addTapListener]]
+=== changeActions.addTapListener()
+Adds a tap listener to an action that will be invoked when the action
+is tapped.
+.Signature
[source,javascript]
----
-self.onAction('revision', 'start-build', function (c) {
- var l = c.checkbox();
- var m = c.checkbox();
- c.popup(c.div(
- c.div(c.label(l, 'Linux')),
- c.div(c.label(m, 'Mac OS X')),
- c.button('Build', {onclick: function() {
- c.call(
- {
- commit: c.revision.name,
- linux: l.checked,
- mac: m.checked,
- },
- function() { c.hide() });
- });
-});
+changeActions.addTapListener(key, callback)
----
-[[context_put]]
-=== context.put()
-Issues a PUT REST API call to the URL associated with the action.
+* key: The key of the action.
+
+* callback: JavaScript function to be invoked when action tapped.
-.Signature
[source,javascript]
----
-context.put(input, callback)
+changeActions.addTapListener("__key_for_my_action__", () => {
+ // do something when my action gets clicked
+})
----
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
+[[change_actions_removeTapListener]]
+=== changeActions.removeTapListener()
+Removes an existing tap listener on an action.
+.Signature
[source,javascript]
----
-context.put(
- {message: "..."},
- function (result) {
- // ... use result here ...
- });
+changeActions.removeTapListener(key, callback)
----
-[[context_refresh]]
-=== context.refresh()
-Refresh the current display. Shorthand for
-link:#Gerrit_refresh[`Gerrit.refresh()`].
-
-[[context_revision]]
-=== context.revision
-When the action is invoked on a specific revision of a change,
-a link:rest-api-changes.html#revision-info[RevisionInfo]
-object instance describing the revision. Available fields of the
-RevisionInfo may vary based on the options used by the UI when it
-loaded the change.
-
-[[context_project]]
-=== context.project
-When the action is invoked on a specific project,
-the name of the project.
-
-=== HTML Helpers
-The link:#ActionContext[action context] includes some HTML helper
-functions to make working with DOM based widgets less painful.
-
-* `br()`: new `<br>` element.
-
-* `button(label, options)`: new `<button>` with the string `label`
- wrapped inside of a `div`. The optional `options` object may
- define `onclick` as a function to be invoked upon clicking. This
- calling pattern avoids circular references between the element
- and the onclick handler.
-
-* `checkbox()`: new `<input type='checkbox'>` element.
-* `div(...)`: a new `<div>` wrapping the (optional) arguments.
-* `hr()`: new `<hr>` element.
-
-* `label(c, label)`: a new `<label>` element wrapping element `c`
- and the string `label`. Used to wrap a checkbox with its label,
- `label(checkbox(), 'Click Me')`.
-
-* `prependLabel(label, c)`: a new `<label>` element wrapping element `c`
- and the string `label`. Used to wrap an input field with its label,
- `prependLabel('Greeting message', textfield())`.
-
-* `textarea(options)`: new `<textarea>` element. The options
- object may optionally include `rows` and `cols`. The textarea
- comes with an onkeypress handler installed to play nicely with
- Gerrit's keyboard binding system.
-
-* `textfield()`: new `<input type='text'>` element. The text field
- comes with an onkeypress handler installed to play nicely with
- Gerrit's keyboard binding system.
-
-* `select(a,i)`: a new `<select>` element containing one `<option>`
- element for each entry in the provided array `a`. The option with
- the index `i` will be pre-selected in the drop-down-list.
-
-* `selected(s)`: returns the text of the `<option>` element that is
- currently selected in the provided `<select>` element `s`.
-
-* `span(...)`: a new `<span>` wrapping the (optional) arguments.
-
-* `msg(label)`: a new label.
-
-
-[[ScreenContext]]
-== Screen Context
-A new screen context is passed to the `screen` callback function
-each time the user navigates to a matching URL.
-
-[[screen_body]]
-=== screen.body
-Empty HTML `<div>` node the plugin should add its content to. The
-node is already attached to the document, but is invisible. Plugins
-must call `screen.show()` to display the DOM node. Deferred display
-allows an implementor to partially populate the DOM, make remote HTTP
-requests, finish populating when the callbacks arrive, and only then
-make the view visible to the user.
-
-[[screen_token]]
-=== screen.token
-URL token fragment that activated this screen. The value is identical
-to `screen.token_match[0]`. If the URL is `/#/x/hello/list` the token
-will be `"list"`.
-
-[[screen_token_match]]
-=== screen.token_match
-Array of matching subgroups from the pattern specified to `screen()`.
-This is identical to the result of RegExp.exec. Index 0 contains the
-entire matching expression; index 1 the first matching group, etc.
-
-[[screen_onUnload]]
-=== screen.onUnload()
-Configures an optional callback to be invoked just before the screen
-is deleted from the browser DOM. Plugins can use this callback to
-remove event listeners from DOM nodes, preventing memory leaks.
+* key: The key of the action.
+
+* callback: JavaScript function to be removed.
+
+[[change_actions_setLabel]]
+=== changeActions.setLabel()
+Sets the label for an action.
.Signature
[source,javascript]
----
-screen.onUnload(callback)
+changeActions.setLabel(key, label)
----
-* callback: JavaScript function to be invoked just before the
- `screen.body` DOM element is removed from the browser DOM.
- This event happens when the user navigates to another screen.
+* key: The key of the action.
+
+* label: The label of the action.
-[[screen.setTitle]]
-=== screen.setTitle()
-Sets the heading text to be displayed when the screen is visible.
-This is presented in a large bold font below the menus, but above the
-content in `screen.body`. Setting the title also sets the window
-title to the same string, if it has not already been set.
+[[change_actions_setTitle]]
+=== changeActions.setTitle()
+Sets the title for an action.
.Signature
[source,javascript]
----
-screen.setPageTitle(titleText)
+changeActions.setTitle(key, title)
----
-[[screen.setWindowTitle]]
-=== screen.setWindowTitle()
-Sets the text to be displayed in the browser's title bar when the
-screen is visible. Plugins should always prefer this method over
-trying to set `window.title` directly. The window title defaults to
-the title given to `setTitle`.
+* key: The key of the action.
+
+* title: The title of the action.
+
+[[change_actions_setIcon]]
+=== changeActions.setIcon()
+Sets an icon for an action.
.Signature
[source,javascript]
----
-screen.setWindowTitle(titleText)
+changeActions.setIcon(key, icon)
----
-[[screen_show]]
-=== screen.show()
-Destroy the currently visible screen and display the plugin's screen.
-This method must be called after adding content to `screen.body`.
-
-[[SettingsScreenContext]]
-== Settings Screen Context
-A new settings screen context is passed to the `settingsScreen` callback
-function each time the user navigates to a matching URL.
-
-[[settingsScreen_body]]
-=== settingsScreen.body
-Empty HTML `<div>` node the plugin should add its content to. The
-node is already attached to the document, but is invisible. Plugins
-must call `settingsScreen.show()` to display the DOM node. Deferred
-display allows an implementor to partially populate the DOM, make
-remote HTTP requests, finish populating when the callbacks arrive, and
-only then make the view visible to the user.
-
-[[settingsScreen_onUnload]]
-=== settingsScreen.onUnload()
-Configures an optional callback to be invoked just before the screen
-is deleted from the browser DOM. Plugins can use this callback to
-remove event listeners from DOM nodes, preventing memory leaks.
+* key: The key of the action.
+
+* icon: The name of the icon.
+
+[[change_actions_setEnabled]]
+=== changeActions.setEnabled()
+Sets an action to enabled or disabled.
.Signature
[source,javascript]
----
-settingsScreen.onUnload(callback)
+changeActions.setEnabled(key, enabled)
----
-* callback: JavaScript function to be invoked just before the
- `settingsScreen.body` DOM element is removed from the browser DOM.
- This event happens when the user navigates to another screen.
+* key: The key of the action.
+
+* enabled: The status of the action, true to enable.
-[[settingsScreen.setTitle]]
-=== settingsScreen.setTitle()
-Sets the heading text to be displayed when the screen is visible.
-This is presented in a large bold font below the menus, but above the
-content in `settingsScreen.body`. Setting the title also sets the
-window title to the same string, if it has not already been set.
+[[change_actions_setActionHidden]]
+=== changeActions.setActionHidden()
+Sets an action to be hidden.
.Signature
[source,javascript]
----
-settingsScreen.setPageTitle(titleText)
+changeActions.setActionHidden(type, key, hidden)
----
-[[settingsScreen.setWindowTitle]]
-=== settingsScreen.setWindowTitle()
-Sets the text to be displayed in the browser's title bar when the
-screen is visible. Plugins should always prefer this method over
-trying to set `window.title` directly. The window title defaults to
-the title given to `setTitle`.
+* type: The type of the action.
+
+* key: The key of the action.
+
+* hidden: True to hide the action, false to show the action.
+
+[[change_actions_setActionOverflow]]
+=== changeActions.setActionOverflow()
+Sets an action to show in overflow menu.
.Signature
[source,javascript]
----
-settingsScreen.setWindowTitle(titleText)
+changeActions.setActionOverflow(type, key, overflow)
----
-[[settingsScreen_show]]
-=== settingsScreen.show()
-Destroy the currently visible screen and display the plugin's screen.
-This method must be called after adding content to
-`settingsScreen.body`.
+* type: The type of the action.
+
+* key: The key of the action.
+
+* overflow: True to move the action to overflow menu, false to move
+ the action out of the overflow menu.
[[PanelContext]]
== Panel Context
@@ -713,10 +438,12 @@ accessed through this name.
[[Gerrit_css]]
=== Gerrit.css()
+[WARNING]
+This method is deprecated. It doesn't work with Shadow DOM and
+will be removed in the future. Please, use link:pg-plugin-dev.html#plugin-styles[plugin.styles] instead.
+
Creates a new unique CSS class and injects it into the document.
The name of the class is returned and can be used by the plugin.
-See link:#Gerrit_html[`Gerrit.html()`] for an easy way to use
-generated class names.
Classes created with this function should be created once at install
time and reused throughout the plugin. Repeatedly creating the same
@@ -732,194 +459,6 @@ Gerrit.install(function(self)) {
});
----
-[[Gerrit_delete]]
-=== Gerrit.delete()
-Issues a DELETE REST API request to the Gerrit server. For plugin
-private REST API URLs see link:#self_delete[self.delete()].
-
-.Signature
-[source,javascript]
-----
-Gerrit.delete(url, callback)
-----
-
-* url: URL relative to the Gerrit server. For example to access the
- link:rest-api-changes.html[changes REST API] use `'/changes/'`.
-
-* callback: JavaScript function to be invoked with the parsed
- JSON result of the API call. DELETE methods often return
- `204 No Content`, which is passed as null.
-
-[source,javascript]
-----
-Gerrit.delete(
- '/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
- function () {});
-----
-
-[[Gerrit_get]]
-=== Gerrit.get()
-Issues a GET REST API request to the Gerrit server. For plugin
-private REST API URLs see link:#self_get[self.get()].
-
-.Signature
-[source,javascript]
-----
-Gerrit.get(url, callback)
-----
-
-* url: URL relative to the Gerrit server. For example to access the
- link:rest-api-changes.html[changes REST API] use `'/changes/'`.
-
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-Gerrit.get('/changes/?q=status:open', function (open) {
- for (var i = 0; i < open.length; i++) {
- console.log(open[i].change_id);
- }
-});
-----
-
-[[Gerrit_getCurrentUser]]
-=== Gerrit.getCurrentUser()
-Returns the currently signed in user's AccountInfo data; empty account
-data if no user is currently signed in.
-
-[[Gerrit_getPluginName]]
-=== Gerrit.getPluginName()
-Returns the name this plugin was installed as by the server
-administrator. The plugin name is required to access REST API
-views installed by the plugin, or to access resources.
-
-Unlike link:#self_getPluginName[`self.getPluginName()`] this method
-must guess the name from the JavaScript call stack. Plugins are
-encouraged to use `self.getPluginName()` whenever possible.
-
-[[Gerrit_go]]
-=== Gerrit.go()
-Updates the web UI to display the screen identified by the supplied
-URL token. The URL token is the text after `#` in the browser URL.
-
-[source,javascript]
-----
-Gerrit.go('/admin/projects/');
-----
-
-If the URL passed matches `http://...`, `https://...`, or `//...`
-the current browser window will navigate to the non-Gerrit URL.
-The user can return to Gerrit with the back button.
-
-[[Gerrit_html]]
-=== Gerrit.html()
-Parses an HTML fragment after performing template replacements. If
-the HTML has a single root element or node that node is returned,
-otherwise it is wrapped inside a `<div>` and the div is returned.
-
-.Signature
-[source,javascript]
-----
-Gerrit.html(htmlText, options, wantElements);
-----
-
-* htmlText: string of HTML to be parsed. A new unattached `<div>` is
- created in the browser's document and the innerHTML property is
- assigned to the passed string, after performing replacements. If
- the div has exactly one child, that child will be returned instead
- of the div.
-
-* options: optional object reference supplying replacements for any
- `{name}` references in htmlText. Navigation through objects is
- supported permitting `{style.bar}` to be replaced with `"foo"` if
- options was `{style: {bar: "foo"}}`. Value replacements are HTML
- escaped before being inserted into the document fragment.
-
-* wantElements: if options is given and wantElements is also true
- an object consisting of `{root: parsedElement, elements: {...}}` is
- returned instead of the parsed element. The elements object contains
- a property for each element using `id={name}` in htmlText.
-
-.Example
-[source,javascript]
-----
-var style = {bar: Gerrit.css('background: yellow')};
-Gerrit.html(
- '<span class="{style.bar}">Hello {name}!</span>',
- {style: style, name: "World"});
-----
-
-Event handlers can be automatically attached to elements referenced
-through an attribute id. Object navigation is not supported for ids,
-and the parser strips the id attribute before returning the result.
-Handler functions must begin with `on` and be a function to be
-installed on the element. This approach is useful for onclick and
-other handlers that do not want to create circular references that
-will eventually leak browser memory.
-
-.Example
-[source,javascript]
-----
-var options = {
- link: {
- onclick: function(e) { window.close() },
- },
-};
-Gerrit.html('<a href="javascript:;" id="{link}">Close</a>', options);
-----
-
-When using options to install handlers care must be taken to not
-accidentally include the returned element into the event handler's
-closure. This is why options is built before calling `Gerrit.html()`
-and not inline as a shown above with "Hello World".
-
-DOM nodes can optionally be returned, allowing handlers to access the
-elements identified by `id={name}` at a later point in time.
-
-.Example
-[source,javascript]
-----
-var w = Gerrit.html(
- '<div>Name: <input type="text" id="{name}"></div>'
- + '<div>Age: <input type="text" id="{age}"></div>'
- + '<button id="{submit}"><div>Save</div></button>',
- {
- submit: {
- onclick: function(s) {
- var e = w.elements;
- window.alert(e.name.value + " is " + e.age.value);
- },
- },
- }, true);
-----
-
-To prevent memory leaks `w.root` and `w.elements` should be set to
-null when the elements are no longer necessary. Screens can use
-link:#screen_onUnload[screen.onUnload()] to define a callback function
-to perform this cleanup:
-
-[source,javascript]
-----
-var w = Gerrit.html(...);
-screen.body.appendElement(w.root);
-screen.onUnload(function() { w.clear() });
-----
-
-[[Gerrit_injectCss]]
-=== Gerrit.injectCss()
-Injects CSS rules into the document by appending onto the end of the
-existing rule list. CSS rules are global to the entire application
-and must be manually scoped by each plugin. For an automatic scoping
-alternative see link:#Gerrit_css[`css()`].
-
-[source,javascript]
-----
-Gerrit.injectCss('.myplugin_bg {background: #000}');
-----
-
[[Gerrit_install]]
=== Gerrit.install()
Registers a new plugin by invoking the supplied initialization
@@ -932,136 +471,6 @@ Gerrit.install(function (self) {
});
----
-[[Gerrit_post]]
-=== Gerrit.post()
-Issues a POST REST API request to the Gerrit server. For plugin
-private REST API URLs see link:#self_post[self.post()].
-
-.Signature
-[source,javascript]
-----
-Gerrit.post(url, input, callback)
-----
-
-* url: URL relative to the Gerrit server. For example to access the
- link:rest-api-changes.html[changes REST API] use `'/changes/'`.
-
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-Gerrit.post(
- '/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
- {topic: 'tests', message: 'Classify work as for testing.'},
- function (r) {});
-----
-
-[[Gerrit_put]]
-=== Gerrit.put()
-Issues a PUT REST API request to the Gerrit server. For plugin
-private REST API URLs see link:#self_put[self.put()].
-
-.Signature
-[source,javascript]
-----
-Gerrit.put(url, input, callback)
-----
-
-* url: URL relative to the Gerrit server. For example to access the
- link:rest-api-changes.html[changes REST API] use `'/changes/'`.
-
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
- result of the API call. If the API returns a string the result is
- a string, otherwise the result is a JavaScript object or array,
- as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-Gerrit.put(
- '/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
- {topic: 'tests', message: 'Classify work as for testing.'},
- function (r) {});
-----
-
-[[Gerrit_onAction]]
-=== Gerrit.onAction()
-Register a JavaScript callback to be invoked when the user clicks
-on a button associated with a server side `UiAction`.
-
-.Signature
-[source,javascript]
-----
-Gerrit.onAction(type, view_name, callback);
-----
-
-* type: `'change'`, `'edit'`, `'revision'`, `'project'` or `'branch'`
- indicating what sort of resource the `UiAction` was bound to in the server.
-
-* view_name: string appearing in URLs to name the view. This is the
- second argument of the `get()`, `post()`, `put()`, and `delete()`
- binding methods in a `RestApiModule`.
-
-* callback: JavaScript function to invoke when the user clicks. The
- function will be passed a link:#ActionContext[ActionContext].
-
-[[Gerrit_screen]]
-=== Gerrit.screen()
-Register a JavaScript callback to be invoked when the user navigates
-to an extension screen provided by the plugin. Extension screens are
-usually linked from the link:dev-plugins.html#top-menu-extensions[top menu].
-The callback can populate the DOM with the screen's contents.
-
-.Signature
-[source,javascript]
-----
-Gerrit.screen(pattern, callback);
-----
-
-* pattern: URL token pattern to identify the screen. Argument can be
- either a string (`'index'`) or a RegExp object (`/list\/(.*)/`).
- If a RegExp is used the matching groups will be available inside of
- the context as `token_match`.
-
-* callback: JavaScript function to invoke when the user navigates to
- the screen. The function will be passed link:#ScreenContext[screen context].
-
-[[Gerrit_refresh]]
-=== Gerrit.refresh()
-Redisplays the current web UI view, refreshing all information.
-
-[[Gerrit_refreshMenuBar]]
-=== Gerrit.refreshMenuBar()
-Refreshes Gerrit's menu bar.
-
-[[Gerrit_isSignedIn]]
-=== Gerrit.isSignedIn()
-Checks if user is signed in.
-
-[[Gerrit_url]]
-=== Gerrit.url()
-Returns the URL of the Gerrit Code Review server. If invoked with
-no parameter the URL of the site is returned. If passed a string
-the argument is appended to the site URL.
-
-[source,javascript]
-----
-Gerrit.url(); // "https://gerrit-review.googlesource.com/"
-Gerrit.url('/123'); // "https://gerrit-review.googlesource.com/123"
-----
-
-For a plugin specific version see link:#self_url()[`self.url()`].
-
-[[Gerrit_showError]]
-=== Gerrit.showError(message)
-Displays the given message in the Gerrit ErrorDialog.
-
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/js_licenses.txt b/Documentation/js_licenses.txt
index bb1399d9d9..c2bdfbb320 100644
--- a/Documentation/js_licenses.txt
+++ b/Documentation/js_licenses.txt
@@ -3,7 +3,6 @@
Apache2.0
* fonts:robotofonts
-* js:web-animations-js
* polymer_externs:polymer_closure
[[Apache2_0_license]]
@@ -477,33 +476,33 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----
-[[promise-polyfill]]
-promise-polyfill
+[[shadycss]]
+shadycss
-* js:promise-polyfill
+* js:shadycss
-[[promise-polyfill_license]]
+[[shadycss_license]]
----
-Copyright (c) 2014 Taylor Hakes
-Copyright (c) 2014 Forbes Lindesay
+# License
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Everything in this repo is BSD style license unless otherwise specified.
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+Copyright (c) 2015 The Polymer Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+* Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
----
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index e94f29741a..1a9a8f6afb 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -52,6 +52,7 @@ Apache2.0
* commons:pool
* commons:validator
* dropwizard:dropwizard-core
+* errorprone:annotations
* flogger:api
* fonts:robotofonts
* guice:guice
@@ -72,8 +73,6 @@ Apache2.0
* jetty:server
* jetty:servlet
* jetty:util
-* jgit/org.eclipse.jgit:javaewah
-* js:web-animations-js
* log:json-smart
* log:jsonevent-layout
* log:log4j
@@ -98,6 +97,7 @@ Apache2.0
* guava-retrying
* html-types
* j2objc
+* javaewah
* jsr305
* mime-util
* servlet-api
@@ -2428,9 +2428,9 @@ Database section 7.
[[jgit]]
jgit
-* jgit/org.eclipse.jgit.archive:jgit-archive
-* jgit/org.eclipse.jgit.http.server:jgit-servlet
-* jgit/org.eclipse.jgit:jgit
+* jgit
+* jgit-archive
+* jgit-servlet
[[jgit_license]]
----
@@ -3329,37 +3329,6 @@ party waives its rights to a jury trial in any resulting litigation.
----
-[[promise-polyfill]]
-promise-polyfill
-
-* js:promise-polyfill
-
-[[promise-polyfill_license]]
-----
-Copyright (c) 2014 Taylor Hakes
-Copyright (c) 2014 Forbes Lindesay
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-----
-
-
[[protobuf]]
protobuf
@@ -3404,6 +3373,37 @@ support library is itself covered by the above license.
----
+[[shadycss]]
+shadycss
+
+* js:shadycss
+
+[[shadycss_license]]
+----
+# License
+
+Everything in this repo is BSD style license unless otherwise specified.
+
+Copyright (c) 2015 The Polymer Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+* Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+----
+
+
[[slf4j]]
slf4j
diff --git a/Documentation/linux-quickstart.txt b/Documentation/linux-quickstart.txt
index bfebc6a2dd..643bde0570 100644
--- a/Documentation/linux-quickstart.txt
+++ b/Documentation/linux-quickstart.txt
@@ -29,10 +29,10 @@ From the Linux machine on which you want to install Gerrit:
. Download the desired Gerrit archive.
To view previous archives, see
-link:https://gerrit-releases.storage.googleapis.com/index.html[Gerrit Code Review: Releases]. The steps below install Gerrit 2.15.1:
+link:https://gerrit-releases.storage.googleapis.com/index.html[Gerrit Code Review: Releases]. The steps below install Gerrit 3.1.3:
....
-wget https://www.gerritcodereview.com/download/gerrit-2.15.1.war
+wget https://gerrit-releases.storage.googleapis.com/gerrit-3.1.3.war
....
NOTE: To build and install Gerrit from the source files, see
@@ -43,7 +43,8 @@ link:dev-readme.html[Gerrit Code Review: Developer Setup].
From the command line, enter:
....
-java -jar gerrit*.war init --batch --dev -d ~/gerrit_testsite
+export GERRIT_SITE=~/gerrit_testsite
+java -jar gerrit*.war init --batch --dev -d $GERRIT_SITE
....
This command takes two parameters:
@@ -78,7 +79,7 @@ To prevent outside connections from contacting your new Gerrit instance
`localhost`. For example:
....
-git config --file ~/gerrit_testsite/etc/gerrit.config httpd.listenUrl 'http://localhost:8080'
+git config --file $GERRIT_SITE/etc/gerrit.config httpd.listenUrl 'http://localhost:8080'
....
== Restart the Gerrit service
@@ -87,7 +88,7 @@ You must restart the Gerrit service for your authentication type and listen URL
changes to take effect:
....
-~/gerrit_testsite/bin/gerrit.sh restart
+$GERRIT_SITE/bin/gerrit.sh restart
....
== Viewing Gerrit
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index c0e23c5246..2545b5d931 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -15,10 +15,12 @@ The following metrics are reported.
=== Actions
-* `action/retry_attempt_counts`: Distribution of number of attempts made
-by RetryHelper to execute an action (1 == single attempt, no retry)
+* `action/retry_attempt_count`: Number of retry attempts made
+by RetryHelper to execute an action (0 == single attempt, no retry)
* `action/retry_timeout_count`: Number of action executions of RetryHelper
that ultimately timed out
+* `action/auto_retry_count`: Number of automatic retries with tracing
+* `action/failures_on_auto_retry_count`: Number of failures on auto retry
=== Pushes
@@ -63,6 +65,11 @@ objects needing finalization.
* `caches/disk_cached`: Disk entries used by persistent cache.
* `caches/disk_hit_ratio`: Disk hit ratio for persistent cache.
+=== Change
+
+* `change/submit_rule_evaluation`: Latency for evaluating submit rules on a change.
+* `change/submit_type_evaluation`: Latency for evaluating the submit type on a change.
+
=== HTTP
==== Jetty
@@ -179,6 +186,10 @@ excluding reindexing
* `notedb/stage_update_latency`: Latency for staging updates to NoteDb by table.
* `notedb/read_latency`: NoteDb read latency by table.
* `notedb/parse_latency`: NoteDb parse latency by table.
+* `notedb/external_id_cache_load_count`: Total number of times the external ID
+ cache loader was called.
+* `notedb/external_id_partial_read_latency`: Latency for generating a new external ID
+ cache state from a prior state.
* `notedb/external_id_update_count`: Total number of external ID updates.
* `notedb/read_all_external_ids_latency`: Latency for reading all
external ID's from NoteDb.
diff --git a/Documentation/pg-plugin-admin-api.txt b/Documentation/pg-plugin-admin-api.txt
index 084fa2c78a..1a41778363 100644
--- a/Documentation/pg-plugin-admin-api.txt
+++ b/Documentation/pg-plugin-admin-api.txt
@@ -4,14 +4,18 @@ This API is provided by link:pg-plugin-dev.html#plugin-admin[plugin.admin()]
and provides customization of the admin menu.
== addMenuLink
-`adminApi.addMenuLink(text, url, opt_external, opt_capabilities)`
+`adminApi.addMenuLink(text, url, opt_capability)`
Add a new link to the end of the admin navigation menu.
.Params
- *text* String text to appear in the link.
- *url* String of the destination URL for the link.
+- *opt_capability* String of capability required to show this link.
When adding an external link, the URL provided should be a full URL. Otherwise,
a non-external link should be relative beginning with a slash. For example, to
-create a link to open changes, use the value `/q/status:open`. \ No newline at end of file
+create a link to open changes, use the value `/q/status:open`.
+
+See more about capability from
+link:rest-api-accounts.html#list-account-capabilities[List Account Capabilities]. \ No newline at end of file
diff --git a/Documentation/pg-plugin-dev.txt b/Documentation/pg-plugin-dev.txt
index 8fb5655214..d90185147e 100644
--- a/Documentation/pg-plugin-dev.txt
+++ b/Documentation/pg-plugin-dev.txt
@@ -360,6 +360,16 @@ screen.
Deprecated. Use link:#plugin-settings[`plugin.settings()`] instead.
+[[plugin-styles]]
+=== styles
+`plugin.styles()`
+
+.Params:
+- none
+
+.Returns:
+- Instance of link:pg-plugin-styles-api.html[GrStylesApi]
+
=== changeMetadata
`plugin.changeMetadata()`
@@ -372,6 +382,7 @@ Deprecated. Use link:#plugin-settings[`plugin.settings()`] instead.
=== theme
`plugin.theme()`
+
Note: TODO
=== url
diff --git a/Documentation/pg-plugin-endpoints.txt b/Documentation/pg-plugin-endpoints.txt
index 3d66dd4aae..a8b333076a 100644
--- a/Documentation/pg-plugin-endpoints.txt
+++ b/Documentation/pg-plugin-endpoints.txt
@@ -134,6 +134,11 @@ This endpoint decorator wraps the voting buttons in the reply dialog.
=== header-title
This endpoint wraps the title-text in the application header.
+=== confirm-revert-change
+This endpoint is inside the confirm revert dialog. By default it displays a
+generic confirmation message regarding reverting the change. Plugins may add
+content to this message or replace it entirely.
+
=== confirm-submit-change
This endpoint is inside the confirm submit dialog. By default it displays a
generic confirmation message regarding submission of the change. Plugins may add
diff --git a/Documentation/pg-plugin-style-object.txt b/Documentation/pg-plugin-style-object.txt
new file mode 100644
index 0000000000..cdcfb557d7
--- /dev/null
+++ b/Documentation/pg-plugin-style-object.txt
@@ -0,0 +1,33 @@
+= Gerrit Code Review - GrStyleObject
+
+Store information about css style properties. You can't create this object
+directly. Instead you should use the link:pg-plugin-styles-api.html#css[css] method.
+This object allows to apply style correctly to elements within different shadow
+subtree.
+
+[[get-class-name]]
+== getClassName
+`styleObject.getClassName(element)`
+
+.Params
+- `element` - an HTMLElement.
+
+.Returns
+- `string` - class name. The class name is valid only within the shadow root of `element`.
+
+Creates a new unique CSS class and injects it into the appropriate place
+in DOM (it can be document or shadow root for element). This class can be later
+added to the element or to any other element in the same shadow root. It is guarantee,
+that method adds CSS class only once for each shadow root.
+
+== apply
+`styleObject.apply(element)`
+
+.Params
+- `element` - element to apply style.
+
+Create a new unique CSS class (see link:#get-class-name[getClassName]) and
+adds class to the element.
+
+
+
diff --git a/Documentation/pg-plugin-styles-api.txt b/Documentation/pg-plugin-styles-api.txt
new file mode 100644
index 0000000000..a8293251ec
--- /dev/null
+++ b/Documentation/pg-plugin-styles-api.txt
@@ -0,0 +1,29 @@
+= Gerrit Code Review - Plugin styles API
+
+This API is provided by link:pg-plugin-dev.html#plugin-styles[plugin.styles()]
+and provides a way to apply dynamically created styles to elements in a
+document.
+
+[[css]]
+== css
+`styles.css(rulesStr)`
+
+.Params
+- `*string* rulesStr` string with CSS styling declarations.
+
+Example:
+----
+const styleObject = plugin.styles().css('background: black; color: white;');
+...
+const className = styleObject.getClassName(element)
+...
+element.classList.add(className);
+...
+styleObject.apply(someOtherElement);
+----
+
+.Returns
+- Instance of link:pg-plugin-style-object.html[GrStyleObject].
+
+
+
diff --git a/Documentation/pg-plugin-styling.txt b/Documentation/pg-plugin-styling.txt
index 301da51c13..2453bad0b8 100644
--- a/Documentation/pg-plugin-styling.txt
+++ b/Documentation/pg-plugin-styling.txt
@@ -23,13 +23,15 @@ Plugins should provide Style Module, for example:
``` html
<dom-module id="some-style">
- <style>
- :root {
- --css-mixin-name: {
- property: value;
+ <template>
+ <style>
+ html {
+ --css-mixin-name: {
+ property: value;
+ }
}
- }
- </style>
+ </style>
+ </template>
</dom-module>
```
diff --git a/Documentation/pgm-daemon.txt b/Documentation/pgm-daemon.txt
index 25ca4dd706..cf6560b344 100644
--- a/Documentation/pgm-daemon.txt
+++ b/Documentation/pgm-daemon.txt
@@ -11,7 +11,7 @@ _java_ -jar gerrit.war _daemon_
[--enable-httpd | --disable-httpd]
[--enable-sshd | --disable-sshd]
[--console-log]
- [--slave]
+ [--replica]
[--headless]
[--init]
[-s]
@@ -32,15 +32,15 @@ per the local copy of link:config-gerrit.html[gerrit.config] located under
--enable-httpd::
--disable-httpd::
Enable (or disable) the internal HTTP daemon, answering
- web requests. Enabled by default when --slave is not used.
+ web requests. Enabled by default when --replica is not used.
--enable-sshd::
--disable-sshd::
Enable (or disable) the internal SSH daemon, answering SSH
clients and remotely executed commands. Enabled by default.
---slave::
- Run in slave mode, permitting only read operations
+--replica::
+ Run in replica mode, permitting only read operations
by clients. Commands which modify state such as
link:cmd-receive-pack.html[receive-pack] (creates new changes
or updates existing ones) or link:cmd-review.html[review]
@@ -81,9 +81,9 @@ is automatically rotated at 12:00 AM GMT each day, allowing an
external log cleaning service to clean up the prior logs.
== KNOWN ISSUES
-Slave daemon caches can quickly become out of date when modifications
-are made on the master. The following configuration is suggested in
-a slave to reduce the maxAge for each cache entry, so that changes
+Replica daemon caches can quickly become out of date when modifications
+are made on the primary node. The following configuration is suggested in
+a replica to reduce the maxAge for each cache entry, so that changes
are recognized in a reasonable period of time:
----
@@ -107,7 +107,7 @@ and if LDAP support was enabled, also include:
maxAge = 5 min
----
-Automatic cache coherency between master and slave systems is
+Automatic cache coherency between primary and replica systems is
planned to be implemented in a future version.
GERRIT
diff --git a/Documentation/prolog-cookbook.txt b/Documentation/prolog-cookbook.txt
index 9a23a27b36..f291920d4a 100644
--- a/Documentation/prolog-cookbook.txt
+++ b/Documentation/prolog-cookbook.txt
@@ -74,6 +74,9 @@ For interactive testing and playing with Prolog, Gerrit provides the
link:pgm-prolog-shell.html[prolog-shell] program which opens an interactive
Prolog interpreter shell.
+For batch or unit tests, see the examples in Gerrit source directory
+link:https://gerrit.googlesource.com/gerrit/+/refs/heads/master/prologtests/examples/[prologtests/examples].
+
[NOTE]
The interactive shell is just a prolog shell, it does not load
a gerrit server environment and thus is not intended for
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 1d4f6fb14a..6f0d828af9 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -58,8 +58,8 @@ generally disabled by default. Optional fields are:
[[details]]
--
-* `DETAILS`: Includes full name, preferred email, username and avatars
-for each account.
+* `DETAILS`: Includes full name, preferred email, username, avatars,
+status and state for each account.
--
[[all-emails]]
@@ -1254,13 +1254,10 @@ any account.
)]}'
{
"changes_per_page": 25,
- "show_site_header": true,
- "use_flash_clipboard": true,
"date_format": "STD",
"time_format": "HHMM_12",
"diff_view": "SIDE_BY_SIDE",
"size_bar_in_change_table": true,
- "review_category_strategy": "ABBREV",
"mute_common_path_prefixes": true,
"publish_comments_on_push": true,
"work_in_progress_by_default": true,
@@ -1309,13 +1306,10 @@ link:#preferences-input[PreferencesInput] entity.
{
"changes_per_page": 50,
- "show_site_header": true,
- "use_flash_clipboard": true,
"expand_inline_diffs": true,
"date_format": "STD",
"time_format": "HHMM_12",
"size_bar_in_change_table": true,
- "review_category_strategy": "NAME",
"diff_view": "SIDE_BY_SIDE",
"mute_common_path_prefixes": true,
"my": [
@@ -1359,13 +1353,10 @@ link:#preferences-info[PreferencesInfo] entity.
)]}'
{
"changes_per_page": 50,
- "show_site_header": true,
- "use_flash_clipboard": true,
"expand_inline_diffs": true,
"date_format": "STD",
"time_format": "HHMM_12",
"size_bar_in_change_table": true,
- "review_category_strategy": "NAME",
"diff_view": "SIDE_BY_SIDE",
"publish_comments_on_push": true,
"work_in_progress_by_default": true,
@@ -2201,7 +2192,7 @@ The `AccountDetailInfo` entity contains detailed information about an
account.
`AccountDetailInfo` has the same fields as link:#account-info[
-AccountInfo]. In addition `AccountDetailInfo` has the following fields:
+AccountInfo]. In addition `AccountDetailInfo` has the following field:
[options="header",cols="1,^1,5"]
|=================================
@@ -2209,8 +2200,6 @@ AccountInfo]. In addition `AccountDetailInfo` has the following fields:
|`registered_on` ||
The link:rest-api.html#timestamp[timestamp] of when the account was
registered.
-|`inactive` |not set if `false`|
-Whether the account is inactive.
|=================================
[[account-external-id-info]]
@@ -2261,9 +2250,14 @@ Only set if detailed account information is requested. +
See option link:rest-api-changes.html#detailed-accounts[
DETAILED_ACCOUNTS] for change queries +
and option link:#details[DETAILS] for account queries.
+|`avatars` |optional|List of link:#avatar-info[AvatarInfo] +
+entities that provide information about avatar images of the account.
|`_more_accounts` |optional, not set if `false`|
Whether the query would deliver more results if not limited. +
Only set on the last account that is returned.
+|`status` |optional|Status message of the account.
+|`inactive` |not set if `false`|
+Whether the account is inactive.
|===============================
[[account-input]]
@@ -2309,6 +2303,19 @@ for an account.
If not set or if set to an empty string, the account status is deleted.
|=============================
+[[avatar-info]]
+=== AvatarInfo
+The `AccountInfo` entity contains information about an avatar image of
+an account.
+
+[options="header",cols="1,6"]
+|======================
+|Field Name|Description
+|`url` |The URL to the avatar image.
+|`height` |The height of the avatar image in pixels.
+|`width` |The width of the avatar image in pixels.
+|======================
+
[[capability-info]]
=== CapabilityInfo
The `CapabilityInfo` entity contains information about the global
@@ -2705,10 +2712,6 @@ The `PreferencesInfo` entity contains information about a user's preferences.
|`changes_per_page` ||
The number of changes to show on each page.
Allowed values are `10`, `25`, `50`, `100`.
-|`show_site_header` |not set if `false`|
-Whether the site header should be shown.
-|`use_flash_clipboard` |not set if `false`|
-Whether to use the flash clipboard widget.
|`expand_inline_diffs` |not set if `false`|
Whether to expand diffs inline instead of opening as separate page
(PolyGerrit only).
@@ -2731,9 +2734,6 @@ Allowed values are `SIDE_BY_SIDE`, `UNIFIED_DIFF`.
Whether to show the change sizes as colored bars in the change table.
|`legacycid_in_change_table` |not set if `false`|
Whether to show change number in the change table.
-|`review_category_strategy` ||
-The strategy used to displayed info in the review category column.
-Allowed values are `NONE`, `NAME`, `EMAIL`, `USERNAME`, `ABBREV`.
|`mute_common_path_prefixes` |not set if `false`|
Whether to mute common path prefixes in file names in the file table.
|`signed_off_by` |not set if `false`|
@@ -2774,10 +2774,6 @@ user preferences. Fields which are not set will not be updated.
|`changes_per_page` |optional|
The number of changes to show on each page.
Allowed values are `10`, `25`, `50`, `100`.
-|`show_site_header` |optional|
-Whether the site header should be shown.
-|`use_flash_clipboard` |optional|
-Whether to use the flash clipboard widget.
|`expand_inline_diffs` |not set if `false`|
Whether to expand diffs inline instead of opening as separate page
(PolyGerrit only).
@@ -2798,9 +2794,6 @@ Allowed values are `SIDE_BY_SIDE`, `UNIFIED_DIFF`.
Whether to show the change sizes as colored bars in the change table.
|`legacycid_in_change_table` |optional|
Whether to show change number in the change table.
-|`review_category_strategy` |optional|
-The strategy used to displayed info in the review category column.
-Allowed values are `NONE`, `NAME`, `EMAIL`, `USERNAME`, `ABBREV`.
|`mute_common_path_prefixes` |optional|
Whether to mute common path prefixes in file names in the file table.
|`signed_off_by` |optional|
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 199c13d58a..3cb40f6a0d 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -318,6 +318,11 @@ effect.
A change's mergeability can be requested separately by calling the
link:#get-mergeable[get-mergeable] endpoint.
--
+[[skip_diffstat]]
+--
+* `SKIP_DIFFSTAT`: skip the 'insertions' and 'deletions' field in link:#change-info[ChangeInfo].
+ For large trees, their computation may be expensive.
+--
[[submittable]]
--
@@ -844,8 +849,10 @@ returned that describes the resulting change.
Creates a new patch set with a new commit message.
The new commit message must be provided in the request body inside a
-link:#commit-message-input[CommitMessageInput] entity and contain the change ID footer if
-link:project-configuration.html#require-change-id[Require Change-Id] was specified.
+link:#commit-message-input[CommitMessageInput] entity. If a Change-Id
+footer is specified, it must match the current Change-Id footer. If
+the Change-Id footer is absent, the current Change-Id is added to the
+message.
.Request
----
@@ -2628,6 +2635,14 @@ content isn't provided, it is wiped out for that file. As response
HTTP/1.1 204 No Content
----
+When the change edit is a no-op, for example when providing the same file
+content that the file already has, '409 no changes were made' is returned.
+
+.Response
+----
+ HTTP/1.1 409 no changes were made
+----
+
[[post-edit]]
=== Restore file content or rename files in Change Edit
--
@@ -2974,7 +2989,20 @@ As result a list of link:#reviewer-info[ReviewerInfo] entries is returned.
Suggest the reviewers for a given query `q` and result limit `n`. If result
limit is not passed, then the default 10 is used.
-Groups can be excluded from the results by specifying 'e=f'.
+This REST endpoint only suggests accounts that
+
+* are active
+* can see the change
+* are visible to the calling user
+* are not already reviewer on the change
+* don't own the change
+
+Groups can be excluded from the results by specifying the 'exclude-groups'
+request parameter:
+
+--
+'GET /changes/link:#change-id[\{change-id\}]/suggest_reviewers?q=J&n=5&exclude-groups'
+--
As result a list of link:#suggested-reviewer-info[SuggestedReviewerInfo] entries is returned.
@@ -3009,6 +3037,14 @@ As result a list of link:#suggested-reviewer-info[SuggestedReviewerInfo] entries
]
----
+To suggest CCs `reviewer-state=CC` can be specified as additional URL
+parameter. This includes existing reviewers in the result, but excludes
+existing CCs.
+
+--
+'GET /changes/link:#change-id[\{change-id\}]/suggest_reviewers?q=J&reviewer-state=CC'
+--
+
[[get-reviewer]]
=== Get Reviewer
--
@@ -3054,6 +3090,12 @@ Adds one user or all members of one group as reviewer to the change.
The reviewer to be added to the change must be provided in the request
body as a link:#reviewer-input[ReviewerInput] entity.
+Users can be moved from reviewer to CC and vice versa. This means if a
+user is added as CC that is already a reviewer on the change, the
+reviewer state of that user is updated to CC. If a user that is already
+a CC on the change is added as reviewer, the reviewer state of that
+user is updated to reviewer.
+
.Request
----
POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/reviewers HTTP/1.0
@@ -3097,6 +3139,12 @@ members are added as reviewer.
If a group with many members is added as reviewer a confirmation may be
required.
+If a group is added as CC and members of this group are already
+reviewers on the change, these members stay reviewers on the change
+(they are not downgraded to CC). However if a group is added as
+reviewer, all group members become reviewer of the change, even if they
+have been added as CC before.
+
.Request
----
POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/reviewers HTTP/1.0
@@ -4331,8 +4379,10 @@ a project-specific rule.
R = label('Any-Label-Name', reject(_)).
----
-The response is a list of link:#submit-record[SubmitRecord] entries
-describing the permutations that satisfy the tested submit rule.
+The response is a link:#submit-record[SubmitRecord] describing the
+permutations that satisfy the tested submit rule.
+
+If the submit rule was a no-op, the response is "`204 No Content`".
.Response
----
@@ -4341,14 +4391,12 @@ describing the permutations that satisfy the tested submit rule.
Content-Type: application/json; charset=UTF-8
)]}'
- [
- {
- "status": "NOT_READY",
- "reject": {
- "Any-Label-Name": {}
- }
+ {
+ "status": "NOT_READY",
+ "reject": {
+ "Any-Label-Name": {}
}
- ]
+ }
----
When testing with the `curl` command line client the
@@ -4905,7 +4953,8 @@ should make two requests.
For merge commits only, the integer-valued request parameter `parent`
changes the response to return a map of the files which are different
in this commit compared to the given parent commit. The value is the
-1-based index of the parent's position in the commit object. If not
+1-based index of the parent's position in the commit object,
+with the first parent always belonging to the target branch. If not
specified, the response contains a map of the files different in the
auto merge result.
@@ -5966,7 +6015,9 @@ The `CherryPickInput` entity contains information for cherry-picking a change to
[options="header",cols="1,^1,5"]
|===========================
|Field Name ||Description
-|`message` ||Commit message for the cherry-pick change
+|`message` |optional|
+Commit message for the cherry-pick change. If not set, the commit message of
+the cherry-picked commit is used.
|`destination` ||Destination branch
|`base` |optional|
40-hex digit SHA-1 of the commit which will be the parent commit of the newly created change.
@@ -6246,10 +6297,12 @@ in a file.
|`a` |optional|Content only in the file on side A (deleted in B).
|`b` |optional|Content only in the file on side B (added in B).
|`ab` |optional|Content in the file on both sides (unchanged).
-|`edit_a` |only present during a replace, i.e. both `a` and `b` are present|
+|`edit_a` |only present when the `intraline` parameter is set and the
+DiffContent is a replace, i.e. both `a` and `b` are present|
Text sections deleted from side A as a
link:#diff-intraline-info[DiffIntralineInfo] entity.
-|`edit_b` |only present during a replace, i.e. both `a` and `b` are present|
+|`edit_b` |only present when the `intraline` parameter is set and the
+DiffContent is a replace, i.e. both `a` and `b` are present|
Text sections inserted in side B as a
link:#diff-intraline-info[DiffIntralineInfo] entity.
|`due_to_rebase`|not set if `false`|Indicates whether this entry was introduced by a
@@ -6312,11 +6365,12 @@ link:rest-api-changes.html#diff-web-link-info[DiffWebLinkInfo] entries.
The `DiffIntralineInfo` entity contains information about intraline edits in a
file.
-The information consists of a list of `<skip length, mark length>` pairs, where
+The information consists of a list of `<skip length, edit length>` pairs, where
the skip length is the number of characters between the end of the previous edit
-and the start of this edit, and the mark length is the number of edited characters
+and the start of this edit, and the edit length is the number of edited characters
following the skip. The start of the edits is from the beginning of the related
-diff content lines.
+diff content lines. If the list is empty, the entire DiffContent should be considered
+as unedited.
Note that the implied newline character at the end of each line is included in
the length calculation, and thus it is possible for the edits to span newlines.
@@ -6863,6 +6917,9 @@ If not set, the default is `ALL`.
|`notify_details`|optional|
Additional information about whom to notify about the revert as a map
of recipient type to link:#notify-info[NotifyInfo] entity.
+|`topic` |optional|
+Name of the topic for the revert change. If not set, the default is the topic
+of the change being reverted.
|=============================
[[review-info]]
@@ -7071,7 +7128,7 @@ entities.
|`reviewed` |optional|
Indicates whether the caller is authenticated and has commented on the
current revision. Only set if link:#reviewed[REVIEWED] option is requested.
-|`messageWithFooter` |optional|
+|`commit_with_footers` |optional|
If the link:#commit-footers[COMMIT_FOOTERS] option is requested and
this is the current patch set, contains the full commit message with
Gerrit-specific commit footers, as if this revision were submitted
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 93591120e7..063e54d915 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1054,14 +1054,11 @@ PreferencesInfo] is returned.
)]}'
{
"changes_per_page": 25,
- "show_site_header": true,
- "use_flash_clipboard": true,
"download_command": "CHECKOUT",
"date_format": "STD",
"time_format": "HHMM_12",
"diff_view": "SIDE_BY_SIDE",
"size_bar_in_change_table": true,
- "review_category_strategy": "NONE",
"mute_common_path_prefixes": true,
"publish_comments_on_push": true,
"my": [
@@ -1133,14 +1130,11 @@ PreferencesInfo] is returned.
)]}'
{
"changes_per_page": 50,
- "show_site_header": true,
- "use_flash_clipboard": true,
"download_command": "CHECKOUT",
"date_format": "STD",
"time_format": "HHMM_12",
"diff_view": "SIDE_BY_SIDE",
"size_bar_in_change_table": true,
- "review_category_strategy": "NONE",
"mute_common_path_prefixes": true,
"publish_comments_on_push": true,
"my": [
@@ -1567,11 +1561,15 @@ button].
|`update_delay` ||
link:config-gerrit.html#change.updateDelay[How often in seconds the web
interface should poll for updates to the currently open change].
-|`submit_whole_topic` ||
+|`submit_whole_topic` |not set if `false`|
link:config-gerrit.html#change.submitWholeTopic[A configuration if
the whole topic is submitted].
|`disable_private_changes` |not set if `false`|
Returns true if private changes are disabled.
+|`exclude_mergeable_in_change_info` |not set if `false`|
+Value of the link:config-gerrit.html#change.api.excludeMergeableInChangeInfo[
+configuration parameter] that controls whether the mergeability bit in
+link:rest-api-changes.html#change-info[ChangeInfo] will never be set.
|=============================
[[check-account-external-ids-input]]
diff --git a/Documentation/rest-api-groups.txt b/Documentation/rest-api-groups.txt
index 85f73295aa..c4ba97347b 100644
--- a/Documentation/rest-api-groups.txt
+++ b/Documentation/rest-api-groups.txt
@@ -508,8 +508,9 @@ describes the created group.
}
----
-If the group creation fails because the name is already in use the
-response is "`409 Conflict`".
+If the group creation fails because the name is already in use, or the
+UUID was specified and the UUID is already in use, the response is
+"`409 Conflict`".
[[get-group-detail]]
=== Get Group Detail
@@ -1596,6 +1597,7 @@ a new internal group.
|Field Name ||Description
|`name` |optional|The name of the group (not encoded). +
If set, must match the group name in the URL.
+|`uuid` |optional|The UUID of the group.
|`description` |optional|The description of the group.
|`visible_to_all`|optional|
Whether the group is visible to all registered users. +
diff --git a/Documentation/rest-api-plugins.txt b/Documentation/rest-api-plugins.txt
index 92b692fa33..ce262808ff 100644
--- a/Documentation/rest-api-plugins.txt
+++ b/Documentation/rest-api-plugins.txt
@@ -391,6 +391,24 @@ describes the plugin.
}
----
+Disabling of a link:config-gerrit.html#plugins.mandatory[mandatory plugin]
+is rejected:
+
+.Request
+----
+ DELETE /plugins/replication HTTP/1.0
+----
+
+.Response
+----
+ HTTP/1.1 405 Method Not Allowed
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ Plugin replication is mandatory
+----
+
[[reload-plugin]]
=== Reload Plugin
--
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 76151b40e0..c1349aa4ae 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -2787,17 +2787,18 @@ dashboard-id.
}
----
-[[set-dashboard]]
-=== Set Dashboard
+[[create-dashboard]]
+=== Create Dashboard
--
'PUT /projects/link:#project-name[\{project-name\}]/dashboards/link:#dashboard-id[\{dashboard-id\}]'
--
-Updates/Creates a project dashboard.
+Creates a project dashboard, if a project dashboard with the given
+dashboard ID doesn't exist yet.
Currently only supported for the `default` dashboard.
-The creation/update information for the dashboard must be provided in
+The creation information for the dashboard must be provided in
the request body as a link:#dashboard-input[DashboardInput] entity.
.Request
@@ -2811,7 +2812,63 @@ the request body as a link:#dashboard-input[DashboardInput] entity.
}
----
-As response the new/updated dashboard is returned as a
+As response the new dashboard is returned as a link:#dashboard-info[
+DashboardInfo] entity.
+
+.Response
+----
+ HTTP/1.1 201 Created
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "id": "main:closed",
+ "ref": "main",
+ "path": "closed",
+ "description": "Merged and abandoned changes in last 7 weeks",
+ "url": "/dashboard/?title\u003dClosed+changes\u0026Merged\u003dstatus:merged+age:7w\u0026Abandoned\u003dstatus:abandoned+age:7w",
+ "is_default": true,
+ "title": "Closed changes",
+ "sections": [
+ {
+ "name": "Merged",
+ "query": "status:merged age:7w"
+ },
+ {
+ "name": "Abandoned",
+ "query": "status:abandoned age:7w"
+ }
+ ]
+ }
+----
+
+[[update-dashboard]]
+=== Update Dashboard
+--
+'PUT /projects/link:#project-name[\{project-name\}]/dashboards/link:#dashboard-id[\{dashboard-id\}]'
+--
+
+Updates a project dashboard, if a project dashboard with the given
+dashboard ID already exists.
+
+Currently only supported for the `default` dashboard.
+
+The update information for the dashboard must be provided in
+the request body as a link:#dashboard-input[DashboardInput] entity.
+
+.Request
+----
+ PUT /projects/work%2Fmy-project/dashboards/default HTTP/1.0
+ Content-Type: application/json; charset=UTF-8
+
+ {
+ "id": "main:closed",
+ "commit_message": "Update the default dashboard"
+ }
+----
+
+As response the updated dashboard is returned as a
link:#dashboard-info[DashboardInfo] entity.
.Response
diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt
index 28f01e98aa..05765ee51d 100644
--- a/Documentation/user-inline-edit.txt
+++ b/Documentation/user-inline-edit.txt
@@ -72,7 +72,7 @@ base.
To add a file to the change:
-. In the top left corner of the change, click Edit.
+. In the top right corner of the change, click Edit.
. Next to Files, click Open:
+
diff --git a/Documentation/user-request-tracing.txt b/Documentation/user-request-tracing.txt
index 8430e97ad1..b26f4c1e7c 100644
--- a/Documentation/user-request-tracing.txt
+++ b/Documentation/user-request-tracing.txt
@@ -1,5 +1,8 @@
= Request Tracing
+[[on-demand]]
+== On-demand Request Tracing
+
Gerrit supports on-demand tracing of single requests that results in
additional logs with debug information that are written to the
`error_log`. The logs that correspond to a traced request are
@@ -19,17 +22,24 @@ request type:
`--trace` option. More information about this can be found in
the link:cmd-index.html#trace[Trace] section of the
link:cmd-index.html[SSH command documentation].
-* Git: For Git pushes tracing can be enabled by setting the
- `trace` push option, the trace ID is returned in the command output.
- More information about this can be found in
- the link:user-upload.html#trace[Trace] section of the
- link:user-upload.html[upload documentation]. Tracing for Git requests
- other than Git push is not supported.
+* Git Push (requires usage of git protocol v2): For Git pushes tracing
+ can be enabled by setting the `trace` push option, the trace ID is
+ returned in the command output. More information about this can be
+ found in the link:user-upload.html#trace[Trace] section of the
+ link:user-upload.html[upload documentation].
+* Git Clone/Fetch/Ls-Remote (requires usage of git protocol v2): For
+ Git clone/fetch/ls-remote tracing can be enabled by setting the
+ `trace` server option. Use '-o trace=<TRACE-ID>' for `git fetch` and
+ `git ls-remote`, and '--server-option trace=<TRACE-ID>' for
+ `git clone`. If the `trace` server option is set without a value
+ (without trace ID) a trace ID is generated but the generated trace ID
+ is not returned to the client (hence a trace ID should always be
+ set).
When request tracing is enabled it is possible to provide an ID that
should be used as trace ID. If a trace ID is not provided a trace ID is
automatically generated. The trace ID must be provided to the support
-team so that they can find the trace.
+team (administrator of the server) so that they can find the trace.
When doing traces it is recommended to specify the ID of the issue
that is being investigated as trace ID so that the traces of the issue
@@ -41,6 +51,71 @@ be enabled for single requests if there is a concrete need for
debugging. In particular bots should never enable tracing for all their
requests by default.
+[[auto-retry]]
+== Automatic Request Tracing
+
+Gerrit can be link:config-gerrit.html#retry.retryWithTraceOnFailure[
+configured] to automatically retry requests on non-recoverable failures
+with tracing enabled. This allows to automatically captures traces of
+these failures for further analysis by the Gerrit administrators.
+
+The auto-retry on failure behaves the same way as if the calling user
+would retry the failed operation with tracing enabled.
+
+It is expected that the auto-retry fails with the same exception that
+triggered the auto-retry, however this is not guaranteed:
+
+* Not all Gerrit operations are fully atomic and it can happen that
+ some parts of the operation have been successfully performed before
+ the failure happened. In this case the auto-retry may fail with a
+ different exception.
+* Some exceptions may mistakenly be considered as non-recoverable and
+ the auto-retry actually succeeds.
+
+[[auto-retry-succeeded]]
+If an auto-retry succeeds you may consider filing this as
+link:https://bugs.chromium.org/p/gerrit/issues/entry?template=GoogleSource+Issue[
+Gerrit issue] so that the Gerrit developers can fix this and treat this
+exception as recoverable.
+
+The trace IDs for auto-retries are generated and start with
+`retry-on-failure-`. For REST requests they are returned to the client
+as `X-Gerrit-Trace` header.
+
+The best way to search for auto-retries in logs is to do a grep by
+`AutoRetry`. For each auto-retry that happened this should match 1 or 2
+log entries:
+
+* one `ERROR` log entry with the exception that triggered the
+ auto-retry
+* one `FINE` log entry with the exception that happened on auto-retry
+ (if this log entry is not present the operation succeeded on
+ auto-retry)
+
+To inspect single auto-retry occurrences in detail you can do a
+link:#find-trace[grep by the trace ID]. The trace ID is part of the log
+entries which have been found by the previous grep (watch out for
+something like: `retry-on-failure-1534166888910-3985dfba`).
+
+[TIP]
+Auto-retrying on failures is only supported by some of the REST
+endpoints (change REST endpoints that perform updates).
+
+[[auto-retry-metrics]]
+=== Metrics
+
+If auto-retry is link:config-gerrit.html#retry.retryWithTraceOnFailure[
+enabled] the following metrics are reported:
+
+* `action/auto_retry_count`: Number of automatic retries with tracing
+* `action/failures_on_auto_retry_count`: Number of failures on auto retry
+
+By comparing the values of these counters one can see how often the
+auto-retry succeeds. As explained link:#auto-retry-succeeded[above] if
+auto-retries succeed that's an issue with Gerrit that you may want to
+report.
+
+[[find-trace]]
== Find log entries for a trace ID
If tracing is enabled all log messages that correspond to the traced
@@ -55,6 +130,9 @@ request have a `TRACE_ID` tag set, e.g.:
By doing a grep with the trace ID over the error log the log entries
that correspond to the request can be found.
+[TIP]
+Usually only server administrators have access to the logs.
+
== Which information is captured in a trace?
* request details
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 6c4b78bfa8..0845956929 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -53,7 +53,7 @@ age:'AGE'::
+
Amount of time that has expired since the change was last updated
with a review comment or new patch set. The age must be specified
-to include a unit suffix, for example `age:2d`:
+to include a unit suffix, for example `-age:2d`:
+
* s, sec, second, seconds
* m, min, minute, minutes
@@ -63,6 +63,10 @@ to include a unit suffix, for example `age:2d`:
* mon, month, months (`1 month` is treated as `30 days`)
* y, year, years (`1 year` is treated as `365 days`)
+`age` can be used both forward and backward looking: `age:2d`
+means 'everything older than 2 days' while `-age:2d` means
+'everything with an age of at most 2 days'.
+
[[assignee]]
assignee:'USER'::
+
@@ -330,7 +334,7 @@ If 'DIR' starts with `^` it matches directories and directory segments by
regular expression. The link:http://www.brics.dk/automaton/[dk.brics.automaton
library] is used for evaluation of such patterns.
-[[footer]]
+[[footer-operator]]
footer:'FOOTER'::
+
Matches any change that has 'FOOTER' as footer in the commit message of the
@@ -412,7 +416,7 @@ is:cc::
True on any change where the current user is in CC.
Same as `cc:self`.
-is:open, is:pending::
+is:open, is:pending, is:new::
+
True if the change is open.
@@ -464,7 +468,7 @@ is:wip::
True if the change is Work In Progress.
[[status]]
-status:open, status:pending::
+status:open, status:pending, status:new::
+
True if the change state is 'review in progress'.
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index 56602e27be..6cf5587096 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -465,74 +465,8 @@ If Change-Id lines are not present in the commit messages, consider
amending the message and copying the line from the change's page
on the web, and then using `git push` as described above.
-If Change-Id lines are not available, then the user must use the
-manual mapping technique described below.
-
For more about Change-Ids, see link:user-changeid.html[Change-Id Lines].
-[[manual_replacement_mapping]]
-==== Manual Replacement Mapping
-
-[NOTE]
---
-The remainder of this section describes a manual method of replacing
-changes by matching each commit name to an existing change number.
-End-users should instead prefer to use Change-Id lines in their
-commit messages, as the process is then fully automated by Gerrit
-during normal uploads.
-
-See above for the preferred technique of replacing changes.
-
-Pushing directly to `refs/changes/` is deprecated. If you see the error
-message 'upload to refs/changes not allowed', it means that pushing directly
-to `refs/changes` is disabled on the Gerrit server and the below section does
-not apply to you.
---
-
-To add an additional patch set to a change, replacing it with an
-updated version of the same logical modification, send the new
-commit to the change's ref. For example, to add the commit whose
-SHA-1 starts with `c0ffee` as a new patch set for change number
-`1979`, use the push refspec `c0ffee:refs/changes/1979` as below:
-
-----
- git push ssh://sshusername@hostname:29418/projectname c0ffee:refs/changes/1979
-----
-
-This form can be combined together with `refs/for/'branchname'`
-(above) to simultaneously create new changes and replace changes
-during one network transaction.
-
-For example, consider the following sequence of events:
-
-----
- $ git commit -m A ; # create 3 commits
- $ git commit -m B
- $ git commit -m C
-
- $ git push ... HEAD:refs/for/master ; # upload for review
- ... A is 1500 ...
- ... B is 1501 ...
- ... C is 1502 ...
-
- $ git rebase -i HEAD~3 ; # edit "A", insert D before B
- ; # now series is A'-D-B'-C'
- $ git push ...
- HEAD:refs/for/master
- HEAD~3:refs/changes/1500
- HEAD~1:refs/changes/1501
- HEAD~0:refs/changes/1502 ; # upload replacements
-----
-
-At the final step during the push Gerrit will attach A' as a new
-patch set on change 1500; B' as a new patch set on change 1501; C'
-as a new patch set on 1502; and D will be created as a new change.
-
-Ensuring D is created as a new change requires passing the refspec
-`HEAD:refs/for/branchname`, otherwise Gerrit will ignore D and
-won't do anything with it. For this reason it is a good idea to
-always include the create change refspec when uploading replacements.
-
[[bypass_review]]
=== Bypass Review
@@ -716,10 +650,6 @@ If Change-Id lines are not present in the commit messages, consider
amending the message and copying the line from the change's page
on the web.
-If Change-Id lines are not available, then the user must use the much
-more <<manual_replacement_mapping,manual mapping technique>> offered
-by using `git push` to a specific `refs/changes/change#` reference.
-
For more about Change-Ids, see link:user-changeid.html[Change-Id Lines].
diff --git a/WORKSPACE b/WORKSPACE
index efc3c73be4..64dd721d33 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -92,6 +92,12 @@ go_repository(
importpath = "github.com/howeyc/fsnotify",
)
+# JGit external repository consumed from git submodule
+local_repository(
+ name = "jgit",
+ path = "modules/jgit",
+)
+
ANTLR_VERS = "3.5.2"
maven_jar(
@@ -161,9 +167,18 @@ maven_jar(
sha1 = "021a212688ec94fe77aff74ab34cc74f6f940e60",
)
-load("//lib/jgit:jgit.bzl", "jgit_repos")
+# JGit's transitive dependencies
+maven_jar(
+ name = "hamcrest-library",
+ artifact = "org.hamcrest:hamcrest-library:1.3",
+ sha1 = "4785a3c21320980282f9f33d0d1264a69040538f",
+)
-jgit_repos()
+maven_jar(
+ name = "jzlib",
+ artifact = "com.jcraft:jzlib:1.1.1",
+ sha1 = "a1551373315ffc2f96130a0e5704f74e151777ba",
+)
maven_jar(
name = "javaewah",
@@ -173,6 +188,12 @@ maven_jar(
)
maven_jar(
+ name = "error-prone-annotations",
+ artifact = "com.google.errorprone:error_prone_annotations:2.3.3",
+ sha1 = "42aa5155a54a87d70af32d4b0d06bf43779de0e2",
+)
+
+maven_jar(
name = "gson",
artifact = "com.google.code.gson:gson:2.8.5",
sha1 = "f645ed69d595b24d4cf8b3fbb64cc505bede8829",
@@ -291,8 +312,8 @@ maven_jar(
# When upgrading commons-compress, also upgrade tukaani-xz
maven_jar(
name = "commons-compress",
- artifact = "org.apache.commons:commons-compress:1.15",
- sha1 = "b686cd04abaef1ea7bc5e143c080563668eec17e",
+ artifact = "org.apache.commons:commons-compress:1.18",
+ sha1 = "1191f9f2bc0c47a8cce69193feb1ff0a8bcb37d5",
)
maven_jar(
@@ -599,18 +620,18 @@ maven_jar(
sha1 = "a3ae34e57fa8a4040e28247291d0cc3d6b8c7bcf",
)
-AUTO_VALUE_VERSION = "1.6.5"
+AUTO_VALUE_VERSION = "1.7"
maven_jar(
name = "auto-value",
artifact = "com.google.auto.value:auto-value:" + AUTO_VALUE_VERSION,
- sha1 = "816872c85048f36a67a276ef7a49cc2e4595711c",
+ sha1 = "fe8387764ed19460eda4f106849c664f51c07121",
)
maven_jar(
name = "auto-value-annotations",
artifact = "com.google.auto.value:auto-value-annotations:" + AUTO_VALUE_VERSION,
- sha1 = "c3dad10377f0e2242c9a4b88e9704eaf79103679",
+ sha1 = "5be124948ebdc7807df68207f35a0f23ce427f29",
)
declare_nongoogle_deps()
@@ -702,7 +723,7 @@ maven_jar(
sha1 = "f7be08ec23c21485b9b5a1cf1654c2ec8c58168d",
)
-GITILES_VERS = "0.2-12"
+GITILES_VERS = "0.3-7"
GITILES_REPO = GERRIT
@@ -711,14 +732,14 @@ maven_jar(
artifact = "com.google.gitiles:blame-cache:" + GITILES_VERS,
attach_source = False,
repository = GITILES_REPO,
- sha1 = "e175e4366d83f20378905ca58a505ba8adac291d",
+ sha1 = "af6212a62363906c63d367f8276ae1645f83bf93",
)
maven_jar(
name = "gitiles-servlet",
artifact = "com.google.gitiles:gitiles-servlet:" + GITILES_VERS,
repository = GITILES_REPO,
- sha1 = "53f654f79ec65b9af7fbe645c99bf7888cd1ac48",
+ sha1 = "6a53f722f8572a2f1bcb7d86e5692168844bab76",
)
# prettify must match the version used in Gitiles
@@ -731,8 +752,8 @@ maven_jar(
# Keep this version of Soy synchronized with the version used in Gitiles.
maven_jar(
name = "soy",
- artifact = "com.google.template:soy:2019-03-11",
- sha1 = "119ac4b3eb0e2c638526ca99374013965c727097",
+ artifact = "com.google.template:soy:2020-08-24",
+ sha1 = "e774bf5cc95923d2685292883fe219e231346e50",
)
maven_jar(
@@ -748,24 +769,24 @@ maven_jar(
)
# When updating Bouncy Castle, also update it in bazlets.
-BC_VERS = "1.60"
+BC_VERS = "1.61"
maven_jar(
name = "bcprov",
artifact = "org.bouncycastle:bcprov-jdk15on:" + BC_VERS,
- sha1 = "bd47ad3bd14b8e82595c7adaa143501e60842a84",
+ sha1 = "00df4b474e71be02c1349c3292d98886f888d1f7",
)
maven_jar(
name = "bcpg",
artifact = "org.bouncycastle:bcpg-jdk15on:" + BC_VERS,
- sha1 = "13c7a199c484127daad298996e95818478431a2c",
+ sha1 = "422656435514ab8a28752b117d5d2646660a0ace",
)
maven_jar(
name = "bcpkix",
artifact = "org.bouncycastle:bcpkix-jdk15on:" + BC_VERS,
- sha1 = "d0c46320fbc07be3a24eb13a56cee4e3d38e0c75",
+ sha1 = "89bb3aa5b98b48e584eee2a7401b7682a46779b4",
)
maven_jar(
@@ -776,7 +797,6 @@ maven_jar(
# Note that all of the following org.apache.httpcomponents have newer versions,
# but 4.4.1 is the only version that is available for all of them.
-# TODO: Check what combination of new versions are compatible.
HTTPCOMP_VERS = "4.4.1"
maven_jar(
@@ -817,30 +837,30 @@ maven_jar(
sha1 = "42a25dc3219429f0e5d060061f71acb49bf010a0",
)
-TRUTH_VERS = "0.44"
+TRUTH_VERS = "1.0"
maven_jar(
name = "truth",
artifact = "com.google.truth:truth:" + TRUTH_VERS,
- sha1 = "11eff954c0c14da7d43276d7b3bcf71463105368",
+ sha1 = "998e5fb3fa31df716574b4c9e8d374855e800451",
)
maven_jar(
name = "truth-java8-extension",
artifact = "com.google.truth.extensions:truth-java8-extension:" + TRUTH_VERS,
- sha1 = "2081a0721d3101e1cf559f013e59c6129b4b10b0",
+ sha1 = "d85fbc1daf0510821f552f2aa71d9605e97aa438",
)
maven_jar(
name = "truth-liteproto-extension",
artifact = "com.google.truth.extensions:truth-liteproto-extension:" + TRUTH_VERS,
- sha1 = "64f47e4e3f79b0a582573098b9c3c6b73599f7c6",
+ sha1 = "7a279c50a0f93da15533cef4993b45606cf67d72",
)
maven_jar(
name = "truth-proto-extension",
artifact = "com.google.truth.extensions:truth-proto-extension:" + TRUTH_VERS,
- sha1 = "c03fbc16087d8cb3bf0f3265a04566d4beb88a6d",
+ sha1 = "8c0c2ea61750f02d0d5ce9c653106b6a5dc82d12",
)
maven_jar(
@@ -849,13 +869,6 @@ maven_jar(
sha1 = "7e060dd5b19431e6d198e91ff670644372f60fbd",
)
-# When bumping the easymock version number, make sure to also move powermock to a compatible version
-maven_jar(
- name = "easymock",
- artifact = "org.easymock:easymock:3.1",
- sha1 = "3e127311a86fc2e8f550ef8ee4abe094bbcf7e7e",
-)
-
JETTY_VERS = "9.4.32.v20200930"
maven_jar(
@@ -925,6 +938,12 @@ maven_jar(
)
maven_jar(
+ name = "javax-annotation",
+ artifact = "javax.annotation:javax.annotation-api:1.3.2",
+ sha1 = "934c04d3cfef185a8008e7bf34331b79730a9d43",
+)
+
+maven_jar(
name = "mockito",
artifact = "org.mockito:mockito-core:2.24.0",
sha1 = "969a7bcb6f16e076904336ebc7ca171d412cc1f9",
@@ -933,13 +952,13 @@ maven_jar(
BYTE_BUDDY_VERSION = "1.9.7"
maven_jar(
- name = "byte-buddy",
+ name = "bytebuddy",
artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
sha1 = "8fea78fea6449e1738b675cb155ce8422661e237",
)
maven_jar(
- name = "byte-buddy-agent",
+ name = "bytebuddy-agent",
artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
sha1 = "8e7d1b599f4943851ffea125fd9780e572727fc0",
)
@@ -974,8 +993,8 @@ npm_binary(
bower_archive(
name = "iron-autogrow-textarea",
package = "polymerelements/iron-autogrow-textarea",
- sha1 = "68f0ece9b1e56ac26f8ce31d9938c504f6951bca",
- version = "2.1.0",
+ sha1 = "2f04c7e2a72d462de36093ab2b4889db20f699f6",
+ version = "2.2.0",
)
bower_archive(
@@ -995,64 +1014,64 @@ bower_archive(
bower_archive(
name = "iron-dropdown",
package = "polymerelements/iron-dropdown",
- sha1 = "ac96fe31cdf203a63426fa75131b43c98c0597d3",
- version = "1.5.5",
+ sha1 = "3902ba164552b1bfc59e6fa692efa4a1fd8dd4ea",
+ version = "2.2.1",
)
bower_archive(
name = "iron-input",
package = "polymerelements/iron-input",
- sha1 = "9bc0c8e81de2527125383cbcf74dd9f27e7fa9ac",
- version = "1.0.10",
+ sha1 = "f79952ff4f6f103c0a2cbd3dacf25935257ff392",
+ version = "2.1.3",
)
bower_archive(
name = "iron-overlay-behavior",
package = "polymerelements/iron-overlay-behavior",
- sha1 = "74cda9d7bf98e7a5e5004bc7ebdb6d208d49e11e",
- version = "2.0.0",
+ sha1 = "c2d2eac1b162420d9475ade2f16d5db8959b93fc",
+ version = "2.3.4",
)
bower_archive(
name = "iron-selector",
package = "polymerelements/iron-selector",
- sha1 = "e0ee46c28523bf17730318c3b481a8ed4331c3b2",
- version = "2.0.0",
+ sha1 = "3f3fcb55f6bd606ea493f99eab9daae21f7a6139",
+ version = "2.1.0",
)
bower_archive(
name = "paper-button",
package = "polymerelements/paper-button",
- sha1 = "3b01774f58a8085d3c903fc5a32944b26ab7be72",
- version = "2.0.0",
+ sha1 = "bcb783d74e1177c1d0836340e7c0280699d1438c",
+ version = "2.1.3",
)
bower_archive(
name = "paper-input",
package = "polymerelements/paper-input",
- sha1 = "6c934805e80ab201e143406edc73ea0ef35abf80",
- version = "1.1.18",
+ sha1 = "c1a81a4173d22e72e8ab609eb3715a75273396b3",
+ version = "2.2.3",
)
bower_archive(
name = "paper-tabs",
package = "polymerelements/paper-tabs",
- sha1 = "b6dd2fbd7ee887534334057a29eb545b940fc5cf",
- version = "2.0.0",
+ sha1 = "589b8e6efa0f171c93233137c8ea013dcea0ffc7",
+ version = "2.1.1",
)
bower_archive(
name = "iron-icon",
package = "polymerelements/iron-icon",
- sha1 = "7da49a0d33cd56017740e0dbcf41d2b71532023f",
- version = "2.0.0",
+ sha1 = "d21e7d4f1bdc6de881390f888e28d53155eeb551",
+ version = "2.1.0",
)
bower_archive(
name = "iron-iconset-svg",
package = "polymerelements/iron-iconset-svg",
- sha1 = "4d0c406239cad2ff2975c6dd95fa189de0fe6b50",
- version = "2.1.0",
+ sha1 = "07c0ce02ce6479856758893416a3709009db7f22",
+ version = "2.2.1",
)
bower_archive(
@@ -1065,36 +1084,36 @@ bower_archive(
bower_archive(
name = "page",
package = "visionmedia/page.js",
- sha1 = "51a05428dd4f68fae1df5f12d0e2b61ba67f7757",
- version = "1.7.1",
+ sha1 = "4a31889cd75cc5e7f68a4c7f256eecaf27102eee",
+ version = "1.11.4",
)
bower_archive(
name = "paper-item",
package = "polymerelements/paper-item",
- sha1 = "803273ceb9ffebec8ecc9373ea638af4cd34af58",
- version = "1.1.4",
+ sha1 = "c3bad022cf182d2bf1c8a44374c7fcb1409afbfa",
+ version = "2.1.1",
)
bower_archive(
name = "paper-listbox",
package = "polymerelements/paper-listbox",
- sha1 = "ccc1a90ab0a96878c7bf7c9c4cfe47c85b09c8e3",
- version = "2.0.0",
+ sha1 = "78247cc32bb776f204efef17cff3095878036a40",
+ version = "2.1.1",
)
bower_archive(
name = "paper-toggle-button",
package = "polymerelements/paper-toggle-button",
- sha1 = "4a2edbdb52c4531d39fe091f12de650bccda270f",
- version = "1.2.0",
+ sha1 = "9927960afb0062726ec1b585ef3e32764c3bbac9",
+ version = "2.1.1",
)
bower_archive(
name = "polymer",
package = "polymer/polymer",
- sha1 = "158443ab05ade5e2cdc24ebc01f1deef9aebac1b",
- version = "1.11.3",
+ sha1 = "d06e17a1d8dc6187ee5aa8c5b3501da10901c82f",
+ version = "2.7.2",
)
bower_archive(
@@ -1105,13 +1124,6 @@ bower_archive(
)
bower_archive(
- name = "promise-polyfill",
- package = "polymerlabs/promise-polyfill",
- sha1 = "a3b598c06cbd7f441402e666ff748326030905d6",
- version = "1.0.0",
-)
-
-bower_archive(
name = "resemblejs",
package = "rsmbl/Resemble.js",
sha1 = "49d5f022417c389b630d6f7ee667aa9540075c42",
@@ -1130,15 +1142,15 @@ bower_archive(
bower_archive(
name = "iron-test-helpers",
package = "polymerelements/iron-test-helpers",
- sha1 = "433b03b106f5ff32049b84150cd70938e18b67ac",
- version = "1.2.5",
+ sha1 = "882be2d4c8714b39299b5f7bf25253c4e8a40761",
+ version = "2.0.1",
)
bower_archive(
name = "test-fixture",
package = "polymerelements/test-fixture",
- sha1 = "e373bd21c069163c3a754e234d52c07c77b20d3c",
- version = "1.1.1",
+ sha1 = "7d72ddfebf555a2dd1fc60a85427d9026b509723",
+ version = "3.0.0",
)
bower_archive(
diff --git a/contrib/abandon_stale.py b/contrib/abandon_stale.py
deleted file mode 100755
index 2e01131d5c..0000000000
--- a/contrib/abandon_stale.py
+++ /dev/null
@@ -1,225 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# The MIT License
-#
-# Copyright 2014 Sony Mobile Communications. All rights reserved.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-""" Script to abandon stale changes from the review server.
-
-Fetches a list of open changes that have not been updated since a given age in
-days, months or years (default 6 months), and then abandons them.
-
-Requires the user's credentials for the Gerrit server to be declared in the
-.netrc file. Supports either basic or digest authentication.
-
-Example to abandon changes that have not been updated for 3 months:
-
- ./abandon_stale --gerrit-url http://review.example.com/ --age 3months
-
-Supports dry-run mode to only list the stale changes, but not actually
-abandon them.
-
-See the --help output for more information about options.
-
-Requires pygerrit2 (https://github.com/dpursehouse/pygerrit2) to be installed
-and available for import.
-
-"""
-
-import logging
-import optparse
-import re
-import sys
-
-from pygerrit2.rest import GerritRestAPI
-from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc, HTTPDigestAuthFromNetrc
-
-
-def _main():
- parser = optparse.OptionParser()
- parser.add_option('-g', '--gerrit-url', dest='gerrit_url',
- metavar='URL',
- default=None,
- help='gerrit server URL')
- parser.add_option('-b', '--basic-auth', dest='basic_auth',
- action='store_true',
- help='(deprecated) use HTTP basic authentication instead'
- ' of digest')
- parser.add_option('-d', '--digest-auth', dest='digest_auth',
- action='store_true',
- help='use HTTP digest authentication instead of basic')
- parser.add_option('-n', '--dry-run', dest='dry_run',
- action='store_true',
- help='enable dry-run mode: show stale changes but do '
- 'not abandon them')
- parser.add_option('-t', '--test', dest='testmode', action='store_true',
- help='test mode: query changes with the `test-abandon` '
- 'topic and ignore age option')
- parser.add_option('-a', '--age', dest='age',
- metavar='AGE',
- default="6months",
- help='age of change since last update in days, months'
- ' or years (default: %default)')
- parser.add_option('-m', '--message', dest='message',
- metavar='STRING', default=None,
- help='custom message to append to abandon message')
- parser.add_option('--branch', dest='branches', metavar='BRANCH_NAME',
- default=[], action='append',
- help='abandon changes only on the given branch')
- parser.add_option('--exclude-branch', dest='exclude_branches',
- metavar='BRANCH_NAME',
- default=[],
- action='append',
- help='do not abandon changes on given branch')
- parser.add_option('--project', dest='projects', metavar='PROJECT_NAME',
- default=[], action='append',
- help='abandon changes only on the given project')
- parser.add_option('--exclude-project', dest='exclude_projects',
- metavar='PROJECT_NAME',
- default=[],
- action='append',
- help='do not abandon changes on given project')
- parser.add_option('--owner', dest='owner',
- metavar='USERNAME',
- default=None,
- action='store',
- help='only abandon changes owned by the given user')
- parser.add_option('--exclude-wip', dest='exclude_wip',
- action='store_true',
- help='Exclude changes that are Work-in-Progress')
- parser.add_option('-v', '--verbose', dest='verbose',
- action='store_true',
- help='enable verbose (debug) logging')
-
- (options, _args) = parser.parse_args()
-
- level = logging.DEBUG if options.verbose else logging.INFO
- logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
- level=level)
-
- if not options.gerrit_url:
- logging.error("Gerrit URL is required")
- return 1
-
- if options.testmode:
- message = "Abandoning in test mode"
- else:
- pattern = re.compile(r"^([\d]+)(month[s]?|year[s]?|week[s]?)")
- match = pattern.match(options.age)
- if not match:
- logging.error("Invalid age: %s", options.age)
- return 1
- message = "Abandoning after %s %s or more of inactivity." % \
- (match.group(1), match.group(2))
-
- if options.digest_auth:
- auth_type = HTTPDigestAuthFromNetrc
- else:
- auth_type = HTTPBasicAuthFromNetrc
-
- try:
- auth = auth_type(url=options.gerrit_url)
- gerrit = GerritRestAPI(url=options.gerrit_url, auth=auth)
- except Exception as e:
- logging.error(e)
- return 1
-
- logging.info(message)
- try:
- stale_changes = []
- offset = 0
- step = 500
- if options.testmode:
- query_terms = ["status:new", "owner:self", "topic:test-abandon"]
- else:
- query_terms = ["status:new", "age:%s" % options.age]
- if options.exclude_wip:
- query_terms += ["-is:wip"]
- if options.branches:
- query_terms += ["branch:%s" % b for b in options.branches]
- elif options.exclude_branches:
- query_terms += ["-branch:%s" % b for b in options.exclude_branches]
- if options.projects:
- query_terms += ["project:%s" % p for p in options.projects]
- elif options.exclude_projects:
- query_terms = ["-project:%s" % p for p in options.exclude_projects]
- if options.owner and not options.testmode:
- query_terms += ["owner:%s" % options.owner]
- query = "%20".join(query_terms)
- while True:
- q = query + "&o=DETAILED_ACCOUNTS&n=%d&S=%d" % (step, offset)
- logging.debug("Query: %s", q)
- url = "/changes/?q=" + q
- result = gerrit.get(url)
- logging.debug("%d changes", len(result))
- if not result:
- break
- stale_changes += result
- last = result[-1]
- if "_more_changes" in last:
- logging.debug("More...")
- offset += step
- else:
- break
- except Exception as e:
- logging.error(e)
- return 1
-
- abandoned = 0
- errors = 0
- abandon_message = message
- if options.message:
- abandon_message += "\n\n" + options.message
- for change in stale_changes:
- number = change["_number"]
- project = ""
- if len(options.projects) != 1:
- project = "%s: " % change["project"]
- owner = ""
- if options.verbose:
- try:
- o = change["owner"]["name"]
- except KeyError:
- o = "Unknown"
- owner = " (%s)" % o
- subject = change["subject"]
- if len(subject) > 70:
- subject = subject[:65] + " [...]"
- change_id = change["id"]
- logging.info("%s%s: %s%s", number, owner, project, subject)
- if options.dry_run:
- continue
-
- try:
- gerrit.post("/changes/" + change_id + "/abandon",
- json={"message": "%s" % abandon_message})
- abandoned += 1
- except Exception as e:
- errors += 1
- logging.error(e)
- logging.info("Total %d stale open changes", len(stale_changes))
- if not options.dry_run:
- logging.info("Abandoned %d changes. %d errors.", abandoned, errors)
-
-
-if __name__ == "__main__":
- sys.exit(_main())
diff --git a/contrib/benchmark-createchange.go b/contrib/benchmark-createchange.go
new file mode 100644
index 0000000000..dc320d6ae1
--- /dev/null
+++ b/contrib/benchmark-createchange.go
@@ -0,0 +1,103 @@
+// Copyright (C) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Program to benchmark Gerrit. Creates pending changes in a loop,
+// which tests performance of BatchRefUpdate and Lucene indexing
+package main
+
+import (
+ "bytes"
+ "encoding/base64"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "net/url"
+ "os"
+ "sort"
+ "time"
+)
+
+func main() {
+ user := flag.String("user", "admin", "username for basic auth")
+ pw := flag.String("password", "secret", "HTTP password for basic auth")
+ project := flag.String("project", "", "project to create changes in")
+ gerritURL := flag.String("url", "http://localhost:8080/", "URL to gerrit instance")
+ numChanges := flag.Int("n", 100, "number of changes to create")
+ flag.Parse()
+ if *gerritURL == "" {
+ log.Fatal("provide --url")
+ }
+ if *project == "" {
+ log.Fatal("provide --project")
+ }
+
+ u, err := url.Parse(*gerritURL)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ basicAuth := fmt.Sprintf("%s:%s", *user, *pw)
+ authHeader := base64.StdEncoding.EncodeToString([]byte(basicAuth))
+
+ client := &http.Client{}
+
+ var dts []time.Duration
+ startAll := time.Now()
+ var lastSec int
+ for i := 0; i < *numChanges; i++ {
+ body := fmt.Sprintf(`{
+ "project" : "%s",
+ "subject" : "change %d",
+ "branch" : "master",
+ "status" : "NEW"
+ }`, *project, i)
+ start := time.Now()
+
+ thisSec := int(start.Sub(startAll) / time.Second)
+ if thisSec != lastSec {
+ log.Printf("change %d", i)
+ }
+ lastSec = thisSec
+
+ u.Path = "/a/changes/"
+ req, err := http.NewRequest("POST", u.String(), bytes.NewBufferString(body))
+ if err != nil {
+ log.Fatal(err)
+ }
+ req.Header.Add("Authorization", "Basic "+authHeader)
+ req.Header.Add("Content-Type", "application/json; charset=UTF-8")
+ resp, err := client.Do(req)
+ if err != nil {
+ log.Fatal(err)
+ }
+ dt := time.Now().Sub(start)
+ dts = append(dts, dt)
+
+ if resp.StatusCode/100 == 2 {
+ continue
+ }
+ log.Println("code", resp.StatusCode)
+ io.Copy(os.Stdout, resp.Body)
+ }
+
+ sort.Slice(dts, func(i, j int) bool { return dts[i] < dts[j] })
+
+ var total time.Duration
+ for _, dt := range dts {
+ total += dt
+ }
+ log.Printf("min %v max %v median %v avg %v", dts[0], dts[len(dts)-1], dts[len(dts)/2], total/time.Duration(len(dts)))
+}
diff --git a/contrib/hooks/post-receive-move-tmp-refs b/contrib/hooks/post-receive-move-tmp-refs
new file mode 100755
index 0000000000..fa0684f5df
--- /dev/null
+++ b/contrib/hooks/post-receive-move-tmp-refs
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# --------------------------------------------------------
+# Install this hook script as post-receive hook in replicated repositories
+# hosted by a gerrit replica which are updated by push replication from the
+# corresponding gerrit primary node.
+#
+# In the gerrit primary node configure the replication plugin to push changes from
+# refs/changes/ to refs/tmp/changes/
+# remote.NAME.push = +refs/changes/*:refs/tmp/changes/*
+# remote.NAME.push = +refs/heads/*:refs/heads/*
+# remote.NAME.push = +refs/tags/*:refs/tags/*
+# And if it's a Gerrit mirror:
+# remote.NAME.push = +refs/meta/*:refs/meta/*
+#
+# In the replicated repository in the gerrit replica configure
+# receive.hideRefs = refs/changes/
+# in order to not advertise the big number of refs in this namespace when
+# the gerrit primary's replication plugin is pushing a change
+#
+# Whenever a ref under refs/tmp/changes/ is arriving this hook will move it
+# to refs/changes/. This helps to avoid the large overhead of advertising all
+# refs/changes/ refs to the gerrit primary when it replicates changes to the
+# replica.
+#
+# Make this script executable then link to it in the repository you would like
+# to use it in.
+# cd /path/to/your/repository.git
+# ln -sf <shared hooks directory>/post-receive-move-tmp-refs hooks/post-receive
+#
+# If you want to use this by default for repositories on the Gerrit replica you
+# can set up a git template directory $TEMPLATE_DIR/hooks/post-receive and
+# configure init.templateDir in the ~/.gitconfig of the user that receives the
+# replication on the mirror host. That way when a new repository is created on
+# the primary and hence on the mirror (if configured that way) it will
+# automatically have the "tmp-refs" commit hook installed.
+# See https://git-scm.com/docs/git-init#_template_directory for details.
+
+# move new changes arriving under refs/tmp/changes/ to refs/changes/
+mv_tmp_refs()
+{
+ oldrev=$1
+ newrev=$2
+ refname=$3
+ case "$refname" in refs/tmp/changes/*)
+ short_refname=${refname##refs/tmp/changes/}
+ $(git update-ref refs/changes/$short_refname $newrev 2>/dev/null)
+ $(git update-ref -d $refname $newrev 2>/dev/null)
+ echo "moved \"$refname\" to \"refs/changes/$short_refname\""
+ ;;
+ esac
+ return 0
+}
+
+GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
+if [ -z "$GIT_DIR" ]; then
+ echo >&2 "fatal: post-receive: GIT_DIR not set"
+ exit 1
+fi
+
+# read ref updates passed to post-receive hook
+while read oldrev newrev refname
+do
+ mv_tmp_refs $oldrev $newrev $refname || continue
+done
diff --git a/contrib/refresh_plugin_in_testsite.sh b/contrib/refresh_plugin_in_testsite.sh
new file mode 100755
index 0000000000..bb42ce821a
--- /dev/null
+++ b/contrib/refresh_plugin_in_testsite.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script compiles a Gerrit plugin whose name is passed as first parameter
+# and copies it over to the plugin folder of the testsite. The path to the
+# testsite needs to be provided by the variable GERRIT_TESTSITE or as second
+# parameter.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+if [ "$#" -lt 1 ]
+then
+ echo "No plugin name provided as first argument. Stopping."
+ exit 1
+else
+ PLUGIN_NAME="$1"
+fi
+
+
+if [ "$#" -lt 2 ]
+then
+ if [ -z ${GERRIT_TESTSITE+x} ]
+ then
+ echo "Path to local testsite is neiter set as GERRIT_TESTSITE nor passed as second argument. Stopping."
+ exit 1
+ fi
+else
+ GERRIT_TESTSITE="$2"
+fi
+
+if [ ! -d "$GERRIT_TESTSITE" ]
+then
+ echo "Testsite directory $GERRIT_TESTSITE does not exist. Stopping."
+ exit 1
+fi
+
+bazel build //plugins/"$PLUGIN_NAME"/...
+if [ $? -ne 0 ]
+then
+ echo "Building the $PLUGIN_NAME plugin failed"
+ exit 1
+fi
+
+yes | cp -f "$GERRIT_CODE_DIR/bazel-genfiles/plugins/$PLUGIN_NAME/$PLUGIN_NAME.jar" "$GERRIT_TESTSITE/plugins/"
+if [ $? -eq 0 ]
+then
+ echo "Plugin $PLUGIN_NAME copied successfully to testsite."
+fi
diff --git a/contrib/show_new_gerrit_doc_in_chrome.sh b/contrib/show_new_gerrit_doc_in_chrome.sh
new file mode 100755
index 0000000000..d57bc8af31
--- /dev/null
+++ b/contrib/show_new_gerrit_doc_in_chrome.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script builds Gerrit's documentation and shows the current state in
+# Chrome. Specific pages (e.g. rest-api-changes.txt) including anchors can be
+# passed as parameter to jump directly to them.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+bazel build Documentation:searchfree
+if [ $? -ne 0 ]
+then
+ echo "Building the documentation failed. Stopping."
+ exit 1
+fi
+
+TMP_DOCS_DIR=/tmp/gerrit_docs
+rm -rf "$TMP_DOCS_DIR"
+unzip bazel-bin/Documentation/searchfree.zip -d "$TMP_DOCS_DIR" </dev/null >/dev/null 2>&1 & disown
+if [ $? -ne 0 ]
+then
+ echo "Unzipping the documentation to $TMP_DOCS_DIR failed. Stopping."
+ exit 1
+fi
+
+if [ "$#" -lt 1 ]
+then
+ FILE_NAME="index.html"
+else
+ FILE_NAME="$1"
+fi
+DOC_FILE_NAME="${FILE_NAME/.txt/.html}"
+google-chrome "file:///$TMP_DOCS_DIR/Documentation/$DOC_FILE_NAME" </dev/null >/dev/null 2>&1 & disown
diff --git a/contrib/start_testsite.sh b/contrib/start_testsite.sh
new file mode 100755
index 0000000000..014eba9575
--- /dev/null
+++ b/contrib/start_testsite.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script starts the local testsite in debug mode. If the flag "-u" is
+# passed, Gerrit is built from the current state of the repository and the
+# testsite is refreshed. The path to the testsite needs to be provided by
+# the variable GERRIT_TESTSITE or as parameter (after any used flags).
+# The testsite can be stopped by interrupting this script.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+UPDATE=false
+while getopts ':u' flag; do
+ case "${flag}" in
+ u) UPDATE=true ;;
+ esac
+done
+shift $(($OPTIND-1))
+
+if [ "$#" -lt 1 ]
+then
+ if [ -z ${GERRIT_TESTSITE+x} ]
+ then
+ echo "Path to local testsite is neither set as GERRIT_TESTSITE nor passed as first argument. Stopping."
+ exit 1
+ fi
+else
+ GERRIT_TESTSITE="$1"
+fi
+
+if [ "$UPDATE" = true ]
+then
+ echo "Refreshing testsite"
+ bazel build gerrit
+ if [ $? -ne 0 ]
+ then
+ echo "Build failed. Stopping."
+ exit 1
+ fi
+ $(bazel info output_base)/external/local_jdk/bin/java -jar bazel-bin/gerrit.war init --batch -d "$GERRIT_TESTSITE"
+ if [ $? -ne 0 ]
+ then
+ echo "Patching the testsite failed. Stopping."
+ exit 1
+ fi
+fi
+
+$(bazel info output_base)/external/local_jdk/bin/java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 bazel-bin/gerrit.war daemon -d "$GERRIT_TESTSITE" --console-log
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index f05c598b07..8b7f9a0551 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -15,17 +15,20 @@
package com.google.gerrit.acceptance;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.truth.OptionalSubject.optionals;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.entities.Patch.COMMIT_MSG;
+import static com.google.gerrit.entities.Patch.MERGE_LIST;
import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.NON_VISIBLE_CHANGES;
-import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
-import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.project.testing.Util.category;
-import static com.google.gerrit.server.project.testing.Util.value;
+import static com.google.gerrit.server.project.testing.TestLabels.label;
+import static com.google.gerrit.server.project.testing.TestLabels.value;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
@@ -51,9 +54,16 @@ import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.RevisionApi;
@@ -78,14 +88,6 @@ import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.mail.Address;
import com.google.gerrit.mail.EmailHeader;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
@@ -123,21 +125,23 @@ import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
import com.google.gerrit.server.plugins.TestServerPlugin;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.restapi.change.Revisions;
import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.util.git.DelegateSystemReader;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.FakeEmailSender;
import com.google.gerrit.testing.FakeEmailSender.Message;
import com.google.gerrit.testing.SshMode;
+import com.google.gerrit.testing.TestTimeUtil;
import com.google.gson.Gson;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.jcraft.jsch.KeyPair;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -147,6 +151,8 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.sql.Timestamp;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -159,8 +165,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
@@ -173,17 +177,19 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.TransportBundleStream;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
-import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -200,8 +206,6 @@ public abstract class AbstractDaemonTest {
@ConfigSuite.Parameter public Config baseConfig;
@ConfigSuite.Name private String configName;
- @Rule public ExpectedException exception = ExpectedException.none();
-
@Rule
public TestRule testRunner =
new TestRule() {
@@ -216,7 +220,8 @@ public abstract class AbstractDaemonTest {
beforeTest(description);
ProjectResetter.Config input = requireNonNull(resetProjects());
- try (ProjectResetter resetter = projectResetter.builder().build(input)) {
+ try (ProjectResetter resetter =
+ projectResetter != null ? projectResetter.builder().build(input) : null) {
AbstractDaemonTest.this.resetter = resetter;
base.evaluate();
} finally {
@@ -288,21 +293,27 @@ public abstract class AbstractDaemonTest {
@Inject private PluginGuiceEnvironment pluginGuiceEnvironment;
@Inject private PluginUser.Factory pluginUserFactory;
@Inject private ProjectIndexCollection projectIndexes;
- @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Inject private SitePaths sitePaths;
+ @Inject private ProjectOperations projectOperations;
private ProjectResetter resetter;
private List<Repository> toClose;
+ private String systemTimeZone;
+ private SystemReader oldSystemReader;
@Before
public void clearSender() {
- sender.clear();
+ if (sender != null) {
+ sender.clear();
+ }
}
@Before
public void startEventRecorder() {
- eventRecorder = eventRecorderFactory.create(admin);
+ if (eventRecorderFactory != null) {
+ eventRecorder = eventRecorderFactory.create(admin);
+ }
}
@Before
@@ -317,7 +328,9 @@ public abstract class AbstractDaemonTest {
@After
public void closeEventRecorder() {
- eventRecorder.close();
+ if (eventRecorder != null) {
+ eventRecorder.close();
+ }
}
@AfterClass
@@ -393,6 +406,10 @@ public abstract class AbstractDaemonTest {
}
protected void beforeTest(Description description) throws Exception {
+ // SystemReader must be overridden before creating any repos, since they read the user/system
+ // configs at initialization time, and are then stored in the RepositoryCache forever.
+ oldSystemReader = setFakeSystemReader(temporaryFolder.getRoot());
+
this.description = description;
GerritServer.Description classDesc =
GerritServer.Description.forTestClass(description, configName);
@@ -404,6 +421,9 @@ public abstract class AbstractDaemonTest {
baseConfig.setString("sshd", null, "listenAddress", "off");
}
+ baseConfig.unset("gerrit", null, "canonicalWebUrl");
+ baseConfig.unset("httpd", null, "listenUrl");
+
baseConfig.setInt("index", null, "batchThreads", -1);
baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
@@ -446,10 +466,63 @@ public abstract class AbstractDaemonTest {
atrScope.set(ctx);
ProjectInput in = projectInput(description);
gApi.projects().create(in);
- project = new Project.NameKey(in.name);
+ project = Project.nameKey(in.name);
if (!classDesc.skipProjectClone()) {
testRepo = cloneProject(project, getCloneAsAccount(description));
}
+
+ // Set the clock step last, so that the test setup isn't consuming any timestamps after the
+ // clock has been set.
+ setTimeSettings(classDesc.useSystemTime(), classDesc.useClockStep(), classDesc.useTimezone());
+ setTimeSettings(
+ methodDesc.useSystemTime(), methodDesc.useClockStep(), methodDesc.useTimezone());
+ }
+
+ private static SystemReader setFakeSystemReader(File tempDir) {
+ SystemReader oldSystemReader = SystemReader.getInstance();
+ SystemReader.setInstance(
+ new DelegateSystemReader(oldSystemReader) {
+ @Override
+ public FileBasedConfig openJGitConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "jgit.config"), FS.detect());
+ }
+
+ @Override
+ public FileBasedConfig openUserConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "user.config"), FS.detect());
+ }
+
+ @Override
+ public FileBasedConfig openSystemConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "system.config"), FS.detect());
+ }
+ });
+ return oldSystemReader;
+ }
+
+ private void setTimeSettings(
+ boolean useSystemTime,
+ @Nullable UseClockStep useClockStep,
+ @Nullable UseTimezone useTimezone) {
+ if (useSystemTime) {
+ TestTimeUtil.useSystemTime();
+ } else if (useClockStep != null) {
+ TestTimeUtil.resetWithClockStep(useClockStep.clockStep(), useClockStep.clockStepUnit());
+ if (useClockStep.startAtEpoch()) {
+ TestTimeUtil.setClock(Timestamp.from(Instant.EPOCH));
+ }
+ }
+ if (useTimezone != null) {
+ systemTimeZone = System.setProperty("user.timezone", useTimezone.timezone());
+ }
+ }
+
+ private void resetTimeSettings() {
+ TestTimeUtil.useSystemTime();
+ if (systemTimeZone != null) {
+ System.setProperty("user.timezone", systemTimeZone);
+ systemTimeZone = null;
+ }
}
/** Override to bind an additional Guice module */
@@ -550,7 +623,7 @@ public abstract class AbstractDaemonTest {
in.submitType = submitType;
in.createEmptyCommit = createEmptyCommit;
gApi.projects().create(in);
- return new Project.NameKey(in.name);
+ return Project.nameKey(in.name);
}
protected TestRepository<InMemoryRepository> cloneProject(Project.NameKey p) throws Exception {
@@ -582,10 +655,13 @@ public abstract class AbstractDaemonTest {
repo.close();
}
closeSsh();
+ resetTimeSettings();
if (server != commonServer) {
server.close();
server = null;
}
+ SystemReader.setInstance(oldSystemReader);
+ oldSystemReader = null;
}
protected void closeSsh() {
@@ -708,18 +784,18 @@ public abstract class AbstractDaemonTest {
return push.to("refs/for/" + branch + "%topic=" + name(topic));
}
- protected BranchApi createBranch(Branch.NameKey branch) throws Exception {
+ protected BranchApi createBranch(BranchNameKey branch) throws Exception {
return gApi.projects()
- .name(branch.getParentKey().get())
- .branch(branch.get())
+ .name(branch.project().get())
+ .branch(branch.branch())
.create(new BranchInput());
}
- protected BranchApi createBranchWithRevision(Branch.NameKey branch, String revision)
+ protected BranchApi createBranchWithRevision(BranchNameKey branch, String revision)
throws Exception {
BranchInput in = new BranchInput();
in.revision = revision;
- return gApi.projects().name(branch.getParentKey().get()).branch(branch.get()).create(in);
+ return gApi.projects().name(branch.project().get()).branch(branch.branch()).create(in);
}
private static final List<Character> RANDOM =
@@ -789,12 +865,15 @@ public abstract class AbstractDaemonTest {
}
protected Account getAccount(Account.Id accountId) {
- return getAccountState(accountId).getAccount();
+ return getAccountState(accountId).account();
}
protected AccountState getAccountState(Account.Id accountId) {
Optional<AccountState> accountState = accountCache.get(accountId);
- assertThat(accountState).named("account %s", accountId.get()).isPresent();
+ assertWithMessage("account %s", accountId.get())
+ .about(optionals())
+ .that(accountState)
+ .isPresent();
return accountState.get();
}
@@ -896,59 +975,6 @@ public abstract class AbstractDaemonTest {
return gApi.changes().id(r.getChangeId()).current();
}
- protected void allow(String ref, String permission, AccountGroup.UUID id) throws Exception {
- allow(project, ref, permission, id);
- }
-
- protected void allow(Project.NameKey p, String ref, String permission, AccountGroup.UUID id)
- throws Exception {
- try (ProjectConfigUpdate u = updateProject(p)) {
- Util.allow(u.getConfig(), permission, id, ref);
- u.save();
- }
- }
-
- protected void allowGlobalCapabilities(
- AccountGroup.UUID id, int min, int max, String... capabilityNames) throws Exception {
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- for (String capabilityName : capabilityNames) {
- Util.allow(
- u.getConfig(), capabilityName, id, new PermissionRange(capabilityName, min, max));
- }
- u.save();
- }
- }
-
- protected void allowGlobalCapabilities(AccountGroup.UUID id, String... capabilityNames)
- throws Exception {
- allowGlobalCapabilities(id, Arrays.asList(capabilityNames));
- }
-
- protected void allowGlobalCapabilities(AccountGroup.UUID id, Iterable<String> capabilityNames)
- throws Exception {
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- for (String capabilityName : capabilityNames) {
- Util.allow(u.getConfig(), capabilityName, id);
- }
- u.save();
- }
- }
-
- protected void removeGlobalCapabilities(AccountGroup.UUID id, String... capabilityNames)
- throws Exception {
- removeGlobalCapabilities(id, Arrays.asList(capabilityNames));
- }
-
- protected void removeGlobalCapabilities(AccountGroup.UUID id, Iterable<String> capabilityNames)
- throws Exception {
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- for (String capabilityName : capabilityNames) {
- Util.remove(u.getConfig(), capabilityName, id);
- }
- u.save();
- }
- }
-
protected void setUseSignedOffBy(InheritableBoolean value) throws Exception {
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
ProjectConfig config = projectConfigFactory.read(md);
@@ -967,125 +993,14 @@ public abstract class AbstractDaemonTest {
}
}
- protected void deny(String ref, String permission, AccountGroup.UUID id) throws Exception {
- deny(project, ref, permission, id);
- }
-
- protected void deny(Project.NameKey p, String ref, String permission, AccountGroup.UUID id)
- throws Exception {
- try (ProjectConfigUpdate u = updateProject(p)) {
- Util.deny(u.getConfig(), permission, id, ref);
- u.save();
- }
- }
-
- protected PermissionRule block(String ref, String permission, AccountGroup.UUID id)
- throws Exception {
- return block(project, ref, permission, id);
- }
-
- protected PermissionRule block(
- Project.NameKey project, String ref, String permission, AccountGroup.UUID id)
- throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- PermissionRule rule = Util.block(u.getConfig(), permission, id, ref);
- u.save();
- return rule;
- }
- }
-
- protected void blockLabel(
- String label, int min, int max, AccountGroup.UUID id, String ref, Project.NameKey project)
- throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.block(u.getConfig(), Permission.LABEL + label, min, max, id, ref);
- u.save();
- }
- }
-
- protected void grant(Project.NameKey project, String ref, String permission)
- throws RepositoryNotFoundException, IOException, ConfigInvalidException {
- grant(project, ref, permission, false);
- }
-
- protected void grant(Project.NameKey project, String ref, String permission, boolean force)
- throws RepositoryNotFoundException, IOException, ConfigInvalidException {
- grant(project, ref, permission, force, adminGroupUuid());
- }
-
- protected void grant(
- Project.NameKey project,
- String ref,
- String permission,
- boolean force,
- AccountGroup.UUID groupUUID)
- throws RepositoryNotFoundException, IOException, ConfigInvalidException {
- try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
- md.setMessage(String.format("Grant %s on %s", permission, ref));
- ProjectConfig config = projectConfigFactory.read(md);
- AccessSection s = config.getAccessSection(ref, true);
- Permission p = s.getPermission(permission, true);
- PermissionRule rule = Util.newRule(config, groupUUID);
- rule.setForce(force);
- p.add(rule);
- config.commit(md);
- projectCache.evict(config.getProject());
- }
- }
-
- protected void grantLabel(
- String label,
- int min,
- int max,
- Project.NameKey project,
- String ref,
- boolean force,
- AccountGroup.UUID groupUUID,
- boolean exclusive)
- throws RepositoryNotFoundException, IOException, ConfigInvalidException {
- String permission = Permission.LABEL + label;
- try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
- md.setMessage(String.format("Grant %s on %s", permission, ref));
- ProjectConfig config = projectConfigFactory.read(md);
- AccessSection s = config.getAccessSection(ref, true);
- Permission p = s.getPermission(permission, true);
- p.setExclusiveGroup(exclusive);
- PermissionRule rule = Util.newRule(config, groupUUID);
- rule.setForce(force);
- rule.setMin(min);
- rule.setMax(max);
- p.add(rule);
- config.commit(md);
- projectCache.evict(config.getProject());
- }
- }
-
- protected void removePermission(Project.NameKey project, String ref, String permission)
- throws IOException, ConfigInvalidException {
- try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
- md.setMessage(String.format("Remove %s on %s", permission, ref));
- ProjectConfig config = projectConfigFactory.read(md);
- AccessSection s = config.getAccessSection(ref, true);
- Permission p = s.getPermission(permission, true);
- p.clearRules();
- config.commit(md);
- projectCache.evict(config.getProject());
- }
- }
-
- protected void blockRead(String ref) throws Exception {
- block(ref, Permission.READ, REGISTERED_USERS);
- }
-
protected void blockAnonymousRead() throws Exception {
- AccountGroup.UUID anonymous = systemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID();
- AccountGroup.UUID registered = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
String allRefs = RefNames.REFS + "*";
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.block(u.getConfig(), Permission.READ, anonymous, allRefs);
- Util.allow(u.getConfig(), Permission.READ, registered, allRefs);
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref(allRefs).group(ANONYMOUS_USERS))
+ .add(allow(Permission.READ).ref(allRefs).group(REGISTERED_USERS))
+ .update();
}
protected PushOneCommit.Result pushTo(String ref) throws Exception {
@@ -1094,11 +1009,11 @@ public abstract class AbstractDaemonTest {
}
protected void approve(String id) throws Exception {
- gApi.changes().id(id).revision("current").review(ReviewInput.approve());
+ gApi.changes().id(id).current().review(ReviewInput.approve());
}
protected void recommend(String id) throws Exception {
- gApi.changes().id(id).revision("current").review(ReviewInput.recommend());
+ gApi.changes().id(id).current().review(ReviewInput.recommend());
}
protected void assertSubmittedTogether(String chId, String... expected) throws Exception {
@@ -1116,7 +1031,7 @@ public abstract class AbstractDaemonTest {
}
protected PatchSet getPatchSet(PatchSet.Id psId) {
- return changeDataFactory.create(project, psId.getParentKey()).patchSet(psId);
+ return changeDataFactory.create(project, psId.changeId()).patchSet(psId);
}
protected IdentifiedUser user(TestAccount testAccount) {
@@ -1136,7 +1051,7 @@ public abstract class AbstractDaemonTest {
protected RevisionResource parseRevisionResource(PushOneCommit.Result r) throws Exception {
PatchSet.Id psId = r.getPatchSetId();
- return parseRevisionResource(psId.getParentKey().toString(), psId.get());
+ return parseRevisionResource(psId.changeId().toString(), psId.get());
}
protected ChangeResource parseChangeResource(String changeId) throws Exception {
@@ -1152,22 +1067,13 @@ public abstract class AbstractDaemonTest {
}
}
- // TODO(hanwen): push this down.
- protected RevCommit getRemoteHead(Project.NameKey project, String branch) throws Exception {
- return projectOperations.project(project).getHead(branch);
- }
-
- protected RevCommit getRemoteHead() throws Exception {
- return getRemoteHead(project, "master");
- }
-
protected void assertMailReplyTo(Message message, String email) throws Exception {
assertThat(message.headers()).containsKey("Reply-To");
EmailHeader.String replyTo = (EmailHeader.String) message.headers().get("Reply-To");
assertThat(replyTo.getString()).contains(email);
}
- protected Map<Branch.NameKey, ObjectId> fetchFromSubmitPreview(String changeId) throws Exception {
+ protected Map<BranchNameKey, ObjectId> fetchFromSubmitPreview(String changeId) throws Exception {
try (BinaryResult result = gApi.changes().id(changeId).current().submitPreview()) {
return fetchFromBundles(result);
}
@@ -1179,7 +1085,7 @@ public abstract class AbstractDaemonTest {
*
* <p>Omits NoteDb meta refs.
*/
- protected Map<Branch.NameKey, ObjectId> fetchFromBundles(BinaryResult bundles) throws Exception {
+ protected Map<BranchNameKey, ObjectId> fetchFromBundles(BinaryResult bundles) throws Exception {
assertThat(bundles.getContentType()).isEqualTo("application/x-zip");
FileSystem fs = Jimfs.newFileSystem();
@@ -1187,8 +1093,8 @@ public abstract class AbstractDaemonTest {
try (OutputStream out = Files.newOutputStream(previewPath)) {
bundles.writeTo(out);
}
- Map<Branch.NameKey, ObjectId> ret = new HashMap<>();
- try (FileSystem zipFs = FileSystems.newFileSystem(previewPath, null);
+ Map<BranchNameKey, ObjectId> ret = new HashMap<>();
+ try (FileSystem zipFs = FileSystems.newFileSystem(previewPath, (ClassLoader) null);
DirectoryStream<Path> dirStream =
Files.newDirectoryStream(Iterables.getOnlyElement(zipFs.getRootDirectories()))) {
for (Path p : dirStream) {
@@ -1199,7 +1105,7 @@ public abstract class AbstractDaemonTest {
int len = bundleName.length();
assertThat(bundleName).endsWith(".git");
String repoName = bundleName.substring(0, len - 4);
- Project.NameKey proj = new Project.NameKey(repoName);
+ Project.NameKey proj = Project.nameKey(repoName);
TestRepository<?> localRepo = cloneProject(proj);
try (InputStream bundleStream = Files.newInputStream(p);
@@ -1216,7 +1122,7 @@ public abstract class AbstractDaemonTest {
continue;
}
RevCommit c = localRepo.getRevWalk().parseCommit(r.getObjectId());
- ret.put(new Branch.NameKey(proj, refName), c.getTree().copy());
+ ret.put(BranchNameKey.create(proj, refName), c.getTree().copy());
}
}
}
@@ -1226,18 +1132,18 @@ public abstract class AbstractDaemonTest {
}
/** Assert that the given branches have the given tree ids. */
- protected void assertTrees(Project.NameKey proj, Map<Branch.NameKey, ObjectId> trees)
+ protected void assertTrees(Project.NameKey proj, Map<BranchNameKey, ObjectId> trees)
throws Exception {
TestRepository<?> localRepo = cloneProject(proj);
GitUtil.fetch(localRepo, "refs/*:refs/*");
- Map<Branch.NameKey, RevTree> refValues = new HashMap<>();
+ Map<BranchNameKey, RevTree> refValues = new HashMap<>();
- for (Branch.NameKey b : trees.keySet()) {
- if (!b.getParentKey().equals(proj)) {
+ for (BranchNameKey b : trees.keySet()) {
+ if (!b.project().equals(proj)) {
continue;
}
- Ref r = localRepo.getRepository().exactRef(b.get());
+ Ref r = localRepo.getRepository().exactRef(b.branch());
assertThat(r).isNotNull();
RevWalk rw = localRepo.getRevWalk();
RevCommit c = rw.parseCommit(r.getObjectId());
@@ -1341,7 +1247,7 @@ public abstract class AbstractDaemonTest {
protected InternalGroup group(AccountGroup.UUID groupUuid) {
InternalGroup group = groupCache.get(groupUuid).orElse(null);
- assertThat(group).named(groupUuid.get()).isNotNull();
+ assertWithMessage(groupUuid.get()).that(group).isNotNull();
return group;
}
@@ -1351,13 +1257,13 @@ public abstract class AbstractDaemonTest {
}
protected InternalGroup group(String groupName) {
- InternalGroup group = groupCache.get(new AccountGroup.NameKey(groupName)).orElse(null);
- assertThat(group).named(groupName).isNotNull();
+ InternalGroup group = groupCache.get(AccountGroup.nameKey(groupName)).orElse(null);
+ assertWithMessage(groupName).that(group).isNotNull();
return group;
}
protected GroupReference groupRef(String groupName) {
- InternalGroup group = groupCache.get(new AccountGroup.NameKey(groupName)).orElse(null);
+ InternalGroup group = groupCache.get(AccountGroup.nameKey(groupName)).orElse(null);
assertThat(group).isNotNull();
return new GroupReference(group.getGroupUUID(), group.getName());
}
@@ -1379,8 +1285,8 @@ public abstract class AbstractDaemonTest {
}
protected void assertGroupDoesNotExist(String groupName) {
- InternalGroup group = groupCache.get(new AccountGroup.NameKey(groupName)).orElse(null);
- assertThat(group).named(groupName).isNull();
+ InternalGroup group = groupCache.get(AccountGroup.nameKey(groupName)).orElse(null);
+ assertWithMessage(groupName).that(group).isNull();
}
protected void assertNotifyTo(TestAccount expected) {
@@ -1532,7 +1438,7 @@ public abstract class AbstractDaemonTest {
LabelValue... value)
throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
- LabelType labelType = category(label, value);
+ LabelType labelType = label(label, value);
labelType.setFunction(func);
labelType.setRefPatterns(refPatterns);
u.getConfig().getLabelSections().put(labelType.getName(), labelType);
@@ -1540,10 +1446,6 @@ public abstract class AbstractDaemonTest {
}
}
- protected void fail(@Nullable String format, Object... args) {
- assert_().fail(format, args);
- }
-
protected void enableCreateNewChangeForAllNotInTarget() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig()
diff --git a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
index fa501e6200..a372089444 100644
--- a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
@@ -73,7 +73,11 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
}
protected static FakeEmailSenderSubject assertThat(FakeEmailSender sender) {
- return assertAbout(FakeEmailSenderSubject::new).that(sender);
+ return assertAbout(fakeEmailSenders()).that(sender);
+ }
+
+ protected static Subject.Factory<FakeEmailSenderSubject, FakeEmailSender> fakeEmailSenders() {
+ return FakeEmailSenderSubject::new;
}
protected void setEmailStrategy(TestAccount account, EmailStrategy strategy) throws Exception {
@@ -91,8 +95,8 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
gApi.accounts().self().setPreferences(prefs);
}
- protected static class FakeEmailSenderSubject
- extends Subject<FakeEmailSenderSubject, FakeEmailSender> {
+ protected static class FakeEmailSenderSubject extends Subject {
+ private final FakeEmailSender fakeEmailSender;
private Message message;
private StagedUsers users;
private Map<RecipientType, List<String>> recipients = new HashMap<>();
@@ -100,10 +104,11 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
FakeEmailSenderSubject(FailureMetadata failureMetadata, FakeEmailSender target) {
super(failureMetadata, target);
+ fakeEmailSender = target;
}
public FakeEmailSenderSubject didNotSend() {
- Message message = actual().peekMessage();
+ Message message = fakeEmailSender.peekMessage();
if (message != null) {
failWithoutActual(fact("expected no message", message));
}
@@ -111,7 +116,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
}
public FakeEmailSenderSubject sent(String messageType, StagedUsers users) {
- message = actual().nextMessage();
+ message = fakeEmailSender.nextMessage();
if (message == null) {
failWithoutActual(fact("expected message", "not sent"));
}
@@ -140,9 +145,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
: header));
}
- // Return a named subject that displays a human-readable table of
- // recipients.
- return named(recipientMapToString(recipients, users::emailToName));
+ return this;
}
private static String recipientMapToString(
@@ -203,8 +206,9 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
if (recipients.get(type).contains(email) != expected) {
failWithoutActual(
fact(
- expected ? "should notify" : "shouldn't notify",
- type + ": " + users.emailToName(email)));
+ expected ? "expected to notify" : "expected not to notify",
+ type + ": " + users.emailToName(email)),
+ fact("but notified", recipientMapToString(recipients, users::emailToName)));
}
if (expected) {
accountedFor.add(email);
@@ -429,7 +433,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
.reviewer(REVIEWER_BY_EMAIL)
.reviewer(ccer.email(), ReviewerState.CC, false)
.reviewer(CC_BY_EMAIL, ReviewerState.CC, false);
- ReviewResult result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
+ ReviewResult result = gApi.changes().id(r.getChangeId()).current().review(in);
supportReviewersByEmail = true;
if (result.reviewers.values().stream().anyMatch(v -> v.error != null)) {
supportReviewersByEmail = false;
@@ -437,7 +441,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
ReviewInput.noScore()
.reviewer(reviewer.email())
.reviewer(ccer.email(), ReviewerState.CC, false);
- result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
+ result = gApi.changes().id(r.getChangeId()).current().review(in);
}
Truth.assertThat(result.reviewers.values().stream().allMatch(v -> v.error == null)).isTrue();
}
diff --git a/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java b/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
index ccd30ab5a4..020602b227 100644
--- a/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
@@ -22,11 +22,11 @@ import static java.util.Objects.requireNonNull;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableListMultimap;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.PluginDefinedInfo;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.DynamicOptions.DynamicBean;
import com.google.gerrit.server.change.ChangeAttributeFactory;
import com.google.gerrit.server.restapi.change.GetChange;
diff --git a/java/com/google/gerrit/acceptance/AccountCreator.java b/java/com/google/gerrit/acceptance/AccountCreator.java
index aeae2c2cfd..75d0d2f867 100644
--- a/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -20,9 +20,9 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.GroupCache;
@@ -76,7 +76,7 @@ public class AccountCreator {
if (account != null) {
return account;
}
- Account.Id id = new Account.Id(sequences.nextAccountId());
+ Account.Id id = Account.id(sequences.nextAccountId());
List<ExternalId> extIds = new ArrayList<>(2);
String httpPass = null;
@@ -98,7 +98,7 @@ public class AccountCreator {
if (groupNames != null) {
for (String n : groupNames) {
- AccountGroup.NameKey k = new AccountGroup.NameKey(n);
+ AccountGroup.NameKey k = AccountGroup.nameKey(n);
Optional<InternalGroup> group = groupCache.get(k);
if (!group.isPresent()) {
throw new NoSuchGroupException(n);
diff --git a/java/com/google/gerrit/acceptance/AccountIndexedCounter.java b/java/com/google/gerrit/acceptance/AccountIndexedCounter.java
new file mode 100644
index 0000000000..88b97c70a5
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/AccountIndexedCounter.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.util.concurrent.AtomicLongMap;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.events.AccountIndexedListener;
+
+/** Checks if an account is indexed the correct number of times. */
+public class AccountIndexedCounter implements AccountIndexedListener {
+ private final AtomicLongMap<Integer> countsByAccount = AtomicLongMap.create();
+
+ @Override
+ public void onAccountIndexed(int id) {
+ countsByAccount.incrementAndGet(id);
+ }
+
+ public void clear() {
+ countsByAccount.clear();
+ }
+
+ public void assertReindexOf(TestAccount testAccount) {
+ assertReindexOf(testAccount, 1);
+ }
+
+ public void assertReindexOf(AccountInfo accountInfo) {
+ assertReindexOf(Account.id(accountInfo._accountId), 1);
+ }
+
+ public void assertReindexOf(TestAccount testAccount, long expectedCount) {
+ assertThat(countsByAccount.asMap()).containsExactly(testAccount.id().get(), expectedCount);
+ clear();
+ }
+
+ public void assertReindexOf(Account.Id accountId, long expectedCount) {
+ assertThat(countsByAccount.asMap()).containsEntry(accountId.get(), expectedCount);
+ countsByAccount.remove(accountId.get());
+ }
+
+ public void assertNoReindex() {
+ assertThat(countsByAccount.asMap()).isEmpty();
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index 25d8df1ae7..646d8f0765 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -2,6 +2,82 @@ load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("//tools/bzl:java.bzl", "java_library2")
load("//tools/bzl:javadoc.bzl", "java_doc")
+FUNCTION_SRCS = [
+ "testsuite/ThrowingConsumer.java",
+ "testsuite/ThrowingFunction.java",
+]
+
+DEPLOY_ENV = [
+ "//java/com/google/gerrit/exceptions",
+ "//java/com/google/gerrit/gpg",
+ "//java/com/google/gerrit/git",
+ "//java/com/google/gerrit/index:query_exception",
+ "//java/com/google/gerrit/launcher",
+ "//java/com/google/gerrit/lifecycle",
+ "//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
+ "//java/com/google/gerrit/extensions:api",
+ "//java/com/google/gerrit/httpd",
+ "//java/com/google/gerrit/index",
+ "//java/com/google/gerrit/index/project",
+ "//java/com/google/gerrit/json",
+ "//java/com/google/gerrit/lucene",
+ "//java/com/google/gerrit/mail",
+ "//java/com/google/gerrit/metrics",
+ "//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/audit",
+ "//java/com/google/gerrit/server/git/receive",
+ "//java/com/google/gerrit/server/logging",
+ "//java/com/google/gerrit/server/restapi",
+ "//java/com/google/gerrit/server/schema",
+ "//java/com/google/gerrit/server/util/git",
+ "//java/com/google/gerrit/server/util/time",
+ "//java/com/google/gerrit/sshd",
+ "//lib/auto:auto-value",
+ "//lib/auto:auto-value-annotations",
+ "//lib:args4j",
+ "//lib:gson",
+ "//lib:guava-retrying",
+ "//lib:jgit",
+ "//lib:jsch",
+ "//lib/commons:compress",
+ "//lib/commons:lang",
+ "//lib/flogger:api",
+ "//lib/guice",
+ "//lib/guice:guice-assistedinject",
+ "//lib/guice:guice-servlet",
+ "//lib/mail",
+ "//lib/mina:sshd",
+ "//lib:guava",
+ "//lib/bouncycastle:bcpg",
+ "//lib/bouncycastle:bcprov",
+ "//prolog:gerrit-prolog-common",
+]
+
+TEST_DEPS = [
+ "//java/com/google/gerrit/httpd/auth/openid",
+ "//java/com/google/gerrit/pgm",
+ "//java/com/google/gerrit/pgm/http/jetty",
+ "//java/com/google/gerrit/pgm/util",
+ "//java/com/google/gerrit/truth",
+ "//java/com/google/gerrit/acceptance/testsuite/project",
+ "//java/com/google/gerrit/server/group/testing",
+ "//java/com/google/gerrit/server/project/testing:project-test-util",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
+ "//java/com/google/gerrit/extensions/common/testing:common-test-util",
+ "//java/com/google/gerrit/extensions/restapi/testing:restapi-test-util",
+ "//java/com/google/gerrit/gpg/testing:gpg-test-util",
+ "//java/com/google/gerrit/git/testing",
+]
+
+PGM_DEPLOY_ENV = [
+ "//lib:caffeine",
+ "//lib:caffeine-guava",
+ "//lib/jackson:jackson-core",
+ "//lib/prolog:cafeteria",
+]
+
java_library(
name = "lib",
testonly = True,
@@ -10,125 +86,53 @@ java_library(
visibility = ["//visibility:public"],
exports = [
":framework-lib",
- "//java/com/google/gerrit/common:annotations",
- "//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/extensions/common/testing:common-test-util",
- "//java/com/google/gerrit/extensions/restapi/testing:restapi-test-util",
- "//java/com/google/gerrit/git/testing",
- "//java/com/google/gerrit/gpg/testing:gpg-test-util",
- "//java/com/google/gerrit/httpd",
- "//java/com/google/gerrit/index",
- "//java/com/google/gerrit/json",
- "//java/com/google/gerrit/launcher",
- "//java/com/google/gerrit/lucene",
- "//java/com/google/gerrit/mail",
- "//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/pgm",
- "//java/com/google/gerrit/pgm/init",
- "//java/com/google/gerrit/pgm/util",
- "//java/com/google/gerrit/reviewdb:server",
- "//java/com/google/gerrit/server",
- "//java/com/google/gerrit/server/audit",
- "//java/com/google/gerrit/server/git/receive",
- "//java/com/google/gerrit/server/project/testing:project-test-util",
- "//java/com/google/gerrit/server/restapi",
- "//java/com/google/gerrit/sshd",
- "//java/com/google/gerrit/testing:gerrit-test-util",
- "//lib:args4j",
- "//lib:gson",
- "//lib:guava-retrying",
- "//lib:h2",
- "//lib:jimfs",
- "//lib:jsch",
- "//lib:servlet-api-without-neverlink",
- "//lib/bouncycastle:bcpg",
- "//lib/bouncycastle:bcprov",
- "//lib/commons:compress",
- "//lib/flogger:api",
- "//lib/guice",
- "//lib/guice:guice-assistedinject",
- "//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/mina:sshd",
- "//prolog:gerrit-prolog-common",
- ],
+ ] + DEPLOY_ENV + TEST_DEPS,
)
java_binary(
name = "framework",
testonly = True,
+ deploy_env = [":framework-deploy-env"],
main_class = "Dummy",
visibility = ["//visibility:public"],
runtime_deps = [":framework-lib"],
)
+java_binary(
+ name = "framework-deploy-env",
+ testonly = True,
+ main_class = "Dummy",
+ runtime_deps = DEPLOY_ENV + PGM_DEPLOY_ENV,
+)
+
java_library2(
name = "framework-lib",
testonly = True,
- srcs = glob(["**/*.java"]),
+ srcs = glob(
+ ["**/*.java"],
+ exclude = FUNCTION_SRCS,
+ ),
exported_deps = [
- "//java/com/google/gerrit/exceptions",
- "//java/com/google/gerrit/gpg",
- "//java/com/google/gerrit/httpd/auth/openid",
- "//java/com/google/gerrit/index:query_exception",
- "//java/com/google/gerrit/launcher",
- "//java/com/google/gerrit/lifecycle",
- "//java/com/google/gerrit/pgm:daemon",
- "//java/com/google/gerrit/pgm/http/jetty",
- "//java/com/google/gerrit/pgm/util",
- "//java/com/google/gerrit/server/group/testing",
- "//java/com/google/gerrit/server/project/testing:project-test-util",
- "//java/com/google/gerrit/testing:gerrit-test-util",
- "//lib:guava",
+ ":function",
+ "//lib:jgit-junit",
"//lib:jimfs",
- "//lib/auto:auto-value",
- "//lib/auto:auto-value-annotations",
- "//lib/flogger:api",
+ "//lib:servlet-api",
"//lib/httpcomponents:fluent-hc",
"//lib/httpcomponents:httpclient",
"//lib/httpcomponents:httpcore",
- "//lib/jetty:servlet",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/mockito",
"//lib/truth",
"//lib/truth:truth-java8-extension",
- "//prolog:gerrit-prolog-common",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//java/com/google/gerrit/common:annotations",
- "//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/httpd",
- "//java/com/google/gerrit/index",
- "//java/com/google/gerrit/index/project",
- "//java/com/google/gerrit/json",
- "//java/com/google/gerrit/lucene",
- "//java/com/google/gerrit/mail",
- "//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/reviewdb:server",
- "//java/com/google/gerrit/server",
- "//java/com/google/gerrit/server/audit",
- "//java/com/google/gerrit/server/git/receive",
- "//java/com/google/gerrit/server/restapi",
- "//java/com/google/gerrit/server/schema",
- "//java/com/google/gerrit/server/util/time",
- "//java/com/google/gerrit/sshd",
- "//lib:args4j",
- "//lib:gson",
- "//lib:guava-retrying",
- "//lib:jsch",
- "//lib:servlet-api",
- "//lib/commons:lang",
"//lib/greenmail",
- "//lib/guice",
- "//lib/guice:guice-assistedinject",
- "//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/mail",
- "//lib/mina:sshd",
- ],
+ ] + TEST_DEPS,
+ visibility = ["//visibility:public"],
+ deps = DEPLOY_ENV,
+)
+
+java_library(
+ name = "function",
+ srcs = FUNCTION_SRCS,
+ visibility = ["//visibility:public"],
)
java_doc(
diff --git a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
index 27ed603cae..1ff7d0eb8a 100644
--- a/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
+++ b/java/com/google/gerrit/acceptance/ChangeIndexedCounter.java
@@ -45,9 +45,8 @@ public class ChangeIndexedCounter implements ChangeIndexedListener {
assertReindexOf(info, 1);
}
- public void assertReindexOf(ChangeInfo info, int expectedCount) {
- assertThat(getCount(info)).isEqualTo(expectedCount);
- assertThat(countsByChange).hasSize(1);
+ public void assertReindexOf(ChangeInfo info, long expectedCount) {
+ assertThat(countsByChange.asMap()).containsExactly(info._number, expectedCount);
clear();
}
}
diff --git a/java/com/google/gerrit/acceptance/DisabledAccountIndex.java b/java/com/google/gerrit/acceptance/DisabledAccountIndex.java
index 91baafbc83..271d15c176 100644
--- a/java/com/google/gerrit/acceptance/DisabledAccountIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledAccountIndex.java
@@ -14,11 +14,11 @@
package com.google.gerrit.acceptance;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.index.account.AccountIndex;
diff --git a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
index a32c6d13d4..34f72f5ccf 100644
--- a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
@@ -14,12 +14,12 @@
package com.google.gerrit.acceptance;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.query.change.ChangeData;
import java.util.Optional;
diff --git a/java/com/google/gerrit/acceptance/DisabledProjectIndex.java b/java/com/google/gerrit/acceptance/DisabledProjectIndex.java
index 2524a76181..ed119ff3bc 100644
--- a/java/com/google/gerrit/acceptance/DisabledProjectIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledProjectIndex.java
@@ -14,13 +14,13 @@
package com.google.gerrit.acceptance;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Project;
/**
* This class wraps an index and assumes the search index can't handle any queries. However, it does
diff --git a/java/com/google/gerrit/acceptance/ExtensionRegistry.java b/java/com/google/gerrit/acceptance/ExtensionRegistry.java
new file mode 100644
index 0000000000..eaf03b332c
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ExtensionRegistry.java
@@ -0,0 +1,253 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import com.google.gerrit.extensions.api.changes.ActionVisitor;
+import com.google.gerrit.extensions.config.DownloadScheme;
+import com.google.gerrit.extensions.events.AccountActivationListener;
+import com.google.gerrit.extensions.events.AccountIndexedListener;
+import com.google.gerrit.extensions.events.ChangeIndexedListener;
+import com.google.gerrit.extensions.events.CommentAddedListener;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.events.GroupIndexedListener;
+import com.google.gerrit.extensions.events.ProjectIndexedListener;
+import com.google.gerrit.extensions.events.RevisionCreatedListener;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.extensions.webui.FileHistoryWebLink;
+import com.google.gerrit.extensions.webui.PatchSetWebLink;
+import com.google.gerrit.server.ExceptionHook;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.change.ChangeETagComputation;
+import com.google.gerrit.server.git.ChangeMessageModifier;
+import com.google.gerrit.server.git.validators.CommitValidationListener;
+import com.google.gerrit.server.git.validators.OnSubmitValidationListener;
+import com.google.gerrit.server.git.validators.RefOperationValidationListener;
+import com.google.gerrit.server.logging.PerformanceLogger;
+import com.google.gerrit.server.rules.SubmitRule;
+import com.google.gerrit.server.validators.AccountActivationValidationListener;
+import com.google.gerrit.server.validators.ProjectCreationValidationListener;
+import com.google.inject.Inject;
+import com.google.inject.util.Providers;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ExtensionRegistry {
+ private final DynamicSet<AccountIndexedListener> accountIndexedListeners;
+ private final DynamicSet<ChangeIndexedListener> changeIndexedListeners;
+ private final DynamicSet<GroupIndexedListener> groupIndexedListeners;
+ private final DynamicSet<ProjectIndexedListener> projectIndexedListeners;
+ private final DynamicSet<CommitValidationListener> commitValidationListeners;
+ private final DynamicSet<ExceptionHook> exceptionHooks;
+ private final DynamicSet<PerformanceLogger> performanceLoggers;
+ private final DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
+ private final DynamicSet<SubmitRule> submitRules;
+ private final DynamicSet<ChangeMessageModifier> changeMessageModifiers;
+ private final DynamicSet<ChangeETagComputation> changeETagComputations;
+ private final DynamicSet<ActionVisitor> actionVisitors;
+ private final DynamicMap<DownloadScheme> downloadSchemes;
+ private final DynamicSet<RefOperationValidationListener> refOperationValidationListeners;
+ private final DynamicSet<CommentAddedListener> commentAddedListeners;
+ private final DynamicSet<GitReferenceUpdatedListener> refUpdatedListeners;
+ private final DynamicSet<FileHistoryWebLink> fileHistoryWebLinks;
+ private final DynamicSet<PatchSetWebLink> patchSetWebLinks;
+ private final DynamicSet<RevisionCreatedListener> revisionCreatedListeners;
+ private final DynamicSet<GroupBackend> groupBackends;
+ private final DynamicSet<AccountActivationValidationListener>
+ accountActivationValidationListeners;
+ private final DynamicSet<AccountActivationListener> accountActivationListeners;
+ private final DynamicSet<OnSubmitValidationListener> onSubmitValidationListeners;
+
+ @Inject
+ ExtensionRegistry(
+ DynamicSet<AccountIndexedListener> accountIndexedListeners,
+ DynamicSet<ChangeIndexedListener> changeIndexedListeners,
+ DynamicSet<GroupIndexedListener> groupIndexedListeners,
+ DynamicSet<ProjectIndexedListener> projectIndexedListeners,
+ DynamicSet<CommitValidationListener> commitValidationListeners,
+ DynamicSet<ExceptionHook> exceptionHooks,
+ DynamicSet<PerformanceLogger> performanceLoggers,
+ DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners,
+ DynamicSet<SubmitRule> submitRules,
+ DynamicSet<ChangeMessageModifier> changeMessageModifiers,
+ DynamicSet<ChangeETagComputation> changeETagComputations,
+ DynamicSet<ActionVisitor> actionVisitors,
+ DynamicMap<DownloadScheme> downloadSchemes,
+ DynamicSet<RefOperationValidationListener> refOperationValidationListeners,
+ DynamicSet<CommentAddedListener> commentAddedListeners,
+ DynamicSet<GitReferenceUpdatedListener> refUpdatedListeners,
+ DynamicSet<FileHistoryWebLink> fileHistoryWebLinks,
+ DynamicSet<PatchSetWebLink> patchSetWebLinks,
+ DynamicSet<RevisionCreatedListener> revisionCreatedListeners,
+ DynamicSet<GroupBackend> groupBackends,
+ DynamicSet<AccountActivationValidationListener> accountActivationValidationListeners,
+ DynamicSet<AccountActivationListener> accountActivationListeners,
+ DynamicSet<OnSubmitValidationListener> onSubmitValidationListeners) {
+ this.accountIndexedListeners = accountIndexedListeners;
+ this.changeIndexedListeners = changeIndexedListeners;
+ this.groupIndexedListeners = groupIndexedListeners;
+ this.projectIndexedListeners = projectIndexedListeners;
+ this.commitValidationListeners = commitValidationListeners;
+ this.exceptionHooks = exceptionHooks;
+ this.performanceLoggers = performanceLoggers;
+ this.projectCreationValidationListeners = projectCreationValidationListeners;
+ this.submitRules = submitRules;
+ this.changeMessageModifiers = changeMessageModifiers;
+ this.changeETagComputations = changeETagComputations;
+ this.actionVisitors = actionVisitors;
+ this.downloadSchemes = downloadSchemes;
+ this.refOperationValidationListeners = refOperationValidationListeners;
+ this.commentAddedListeners = commentAddedListeners;
+ this.refUpdatedListeners = refUpdatedListeners;
+ this.fileHistoryWebLinks = fileHistoryWebLinks;
+ this.patchSetWebLinks = patchSetWebLinks;
+ this.revisionCreatedListeners = revisionCreatedListeners;
+ this.groupBackends = groupBackends;
+ this.accountActivationValidationListeners = accountActivationValidationListeners;
+ this.accountActivationListeners = accountActivationListeners;
+ this.onSubmitValidationListeners = onSubmitValidationListeners;
+ }
+
+ public Registration newRegistration() {
+ return new Registration();
+ }
+
+ @SuppressWarnings("FunctionalInterfaceClash")
+ public class Registration implements AutoCloseable {
+ private final List<RegistrationHandle> registrationHandles = new ArrayList<>();
+
+ public Registration add(AccountIndexedListener accountIndexedListener) {
+ return add(accountIndexedListeners, accountIndexedListener);
+ }
+
+ public Registration add(ChangeIndexedListener changeIndexedListener) {
+ return add(changeIndexedListeners, changeIndexedListener);
+ }
+
+ public Registration add(GroupIndexedListener groupIndexedListener) {
+ return add(groupIndexedListeners, groupIndexedListener);
+ }
+
+ public Registration add(ProjectIndexedListener projectIndexedListener) {
+ return add(projectIndexedListeners, projectIndexedListener);
+ }
+
+ public Registration add(CommitValidationListener commitValidationListener) {
+ return add(commitValidationListeners, commitValidationListener);
+ }
+
+ public Registration add(ExceptionHook exceptionHook) {
+ return add(exceptionHooks, exceptionHook);
+ }
+
+ public Registration add(PerformanceLogger performanceLogger) {
+ return add(performanceLoggers, performanceLogger);
+ }
+
+ public Registration add(ProjectCreationValidationListener projectCreationListener) {
+ return add(projectCreationValidationListeners, projectCreationListener);
+ }
+
+ public Registration add(SubmitRule submitRule) {
+ return add(submitRules, submitRule);
+ }
+
+ public Registration add(ChangeMessageModifier changeMessageModifier) {
+ return add(changeMessageModifiers, changeMessageModifier);
+ }
+
+ public Registration add(ChangeMessageModifier changeMessageModifier, String exportName) {
+ return add(changeMessageModifiers, changeMessageModifier, exportName);
+ }
+
+ public Registration add(ChangeETagComputation changeETagComputation) {
+ return add(changeETagComputations, changeETagComputation);
+ }
+
+ public Registration add(ActionVisitor actionVisitor) {
+ return add(actionVisitors, actionVisitor);
+ }
+
+ public Registration add(DownloadScheme downloadScheme, String exportName) {
+ return add(downloadSchemes, downloadScheme, exportName);
+ }
+
+ public Registration add(RefOperationValidationListener refOperationValidationListener) {
+ return add(refOperationValidationListeners, refOperationValidationListener);
+ }
+
+ public Registration add(CommentAddedListener commentAddedListener) {
+ return add(commentAddedListeners, commentAddedListener);
+ }
+
+ public Registration add(GitReferenceUpdatedListener refUpdatedListener) {
+ return add(refUpdatedListeners, refUpdatedListener);
+ }
+
+ public Registration add(FileHistoryWebLink fileHistoryWebLink) {
+ return add(fileHistoryWebLinks, fileHistoryWebLink);
+ }
+
+ public Registration add(PatchSetWebLink patchSetWebLink) {
+ return add(patchSetWebLinks, patchSetWebLink);
+ }
+
+ public Registration add(RevisionCreatedListener revisionCreatedListener) {
+ return add(revisionCreatedListeners, revisionCreatedListener);
+ }
+
+ public Registration add(GroupBackend groupBackend) {
+ return add(groupBackends, groupBackend);
+ }
+
+ public Registration add(
+ AccountActivationValidationListener accountActivationValidationListener) {
+ return add(accountActivationValidationListeners, accountActivationValidationListener);
+ }
+
+ public Registration add(AccountActivationListener accountDeactivatedListener) {
+ return add(accountActivationListeners, accountDeactivatedListener);
+ }
+
+ public Registration add(OnSubmitValidationListener onSubmitValidationListener) {
+ return add(onSubmitValidationListeners, onSubmitValidationListener);
+ }
+
+ private <T> Registration add(DynamicSet<T> dynamicSet, T extension) {
+ return add(dynamicSet, extension, "gerrit");
+ }
+
+ private <T> Registration add(DynamicSet<T> dynamicSet, T extension, String exportname) {
+ RegistrationHandle registrationHandle = dynamicSet.add(exportname, extension);
+ registrationHandles.add(registrationHandle);
+ return this;
+ }
+
+ private <T> Registration add(DynamicMap<T> dynamicMap, T extension, String exportName) {
+ RegistrationHandle registrationHandle =
+ ((PrivateInternals_DynamicMapImpl<T>) dynamicMap)
+ .put("myPlugin", exportName, Providers.of(extension));
+ registrationHandles.add(registrationHandle);
+ return this;
+ }
+
+ @Override
+ public void close() {
+ registrationHandles.forEach(h -> h.remove());
+ }
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/GcAssert.java b/java/com/google/gerrit/acceptance/GcAssert.java
index b9ef629df1..bef33238ca 100644
--- a/java/com/google/gerrit/acceptance/GcAssert.java
+++ b/java/com/google/gerrit/acceptance/GcAssert.java
@@ -16,7 +16,7 @@ package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertWithMessage;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import java.io.File;
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index bb828afece..be37dd746d 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -42,6 +42,7 @@ import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
import com.google.gerrit.server.ssh.NoSshModule;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.gerrit.server.util.SocketUtil;
import com.google.gerrit.server.util.SystemLog;
import com.google.gerrit.testing.FakeEmailSender;
@@ -104,6 +105,9 @@ public class GerritServer implements AutoCloseable {
has(Sandboxed.class, testDesc.getTestClass()),
has(SkipProjectClone.class, testDesc.getTestClass()),
has(UseSsh.class, testDesc.getTestClass()),
+ false, // @UseSystemTime is only valid on methods.
+ get(UseClockStep.class, testDesc.getTestClass()),
+ get(UseTimezone.class, testDesc.getTestClass()),
null, // @GerritConfig is only valid on methods.
null, // @GerritConfigs is only valid on methods.
null, // @GlobalPluginConfig is only valid on methods.
@@ -112,6 +116,15 @@ public class GerritServer implements AutoCloseable {
public static Description forTestMethod(
org.junit.runner.Description testDesc, String configName) {
+ UseClockStep useClockStep = testDesc.getAnnotation(UseClockStep.class);
+ if (testDesc.getAnnotation(UseSystemTime.class) == null && useClockStep == null) {
+ // Only read the UseClockStep from the class if on method level neither @UseSystemTime nor
+ // @UseClockStep have been used.
+ // If the method defines @UseSystemTime or @UseClockStep it should overwrite @UseClockStep
+ // on class level.
+ useClockStep = get(UseClockStep.class, testDesc.getTestClass());
+ }
+
return new AutoValue_GerritServer_Description(
testDesc,
configName,
@@ -126,6 +139,11 @@ public class GerritServer implements AutoCloseable {
|| has(SkipProjectClone.class, testDesc.getTestClass()),
testDesc.getAnnotation(UseSsh.class) != null
|| has(UseSsh.class, testDesc.getTestClass()),
+ testDesc.getAnnotation(UseSystemTime.class) != null,
+ useClockStep,
+ testDesc.getAnnotation(UseTimezone.class) != null
+ ? testDesc.getAnnotation(UseTimezone.class)
+ : get(UseTimezone.class, testDesc.getTestClass()),
testDesc.getAnnotation(GerritConfig.class),
testDesc.getAnnotation(GerritConfigs.class),
testDesc.getAnnotation(GlobalPluginConfig.class),
@@ -141,6 +159,16 @@ public class GerritServer implements AutoCloseable {
return false;
}
+ @Nullable
+ private static <T extends Annotation> T get(Class<T> annotation, Class<?> clazz) {
+ for (; clazz != null; clazz = clazz.getSuperclass()) {
+ if (clazz.getAnnotation(annotation) != null) {
+ return clazz.getAnnotation(annotation);
+ }
+ }
+ return null;
+ }
+
abstract org.junit.runner.Description testDescription();
@Nullable
@@ -160,6 +188,14 @@ public class GerritServer implements AutoCloseable {
return useSshAnnotation() && SshMode.useSsh();
}
+ abstract boolean useSystemTime();
+
+ @Nullable
+ abstract UseClockStep useClockStep();
+
+ @Nullable
+ abstract UseTimezone useTimezone();
+
@Nullable
abstract GerritConfig config();
@@ -173,12 +209,15 @@ public class GerritServer implements AutoCloseable {
abstract GlobalPluginConfigs pluginConfigs();
private void checkValidAnnotations() {
+ if (useClockStep() != null && useSystemTime()) {
+ throw new IllegalStateException("Use either @UseClockStep or @UseSystemTime, not both");
+ }
if (configs() != null && config() != null) {
- throw new IllegalStateException("Use either @GerritConfigs or @GerritConfig not both");
+ throw new IllegalStateException("Use either @GerritConfigs or @GerritConfig, not both");
}
if (pluginConfigs() != null && pluginConfig() != null) {
throw new IllegalStateException(
- "Use either @GlobalPluginConfig or @GlobalPluginConfigs not both");
+ "Use either @GlobalPluginConfig or @GlobalPluginConfigs, not both");
}
if ((pluginConfigs() != null || pluginConfig() != null) && memory()) {
throw new IllegalStateException("Must use @UseLocalDisk with @GlobalPluginConfig(s)");
@@ -361,7 +400,7 @@ public class GerritServer implements AutoCloseable {
@Nullable InMemoryRepositoryManager inMemoryRepoManager)
throws Exception {
Config cfg = desc.buildConfig(baseConfig);
- daemon.setSlave(isSlave(baseConfig) || cfg.getBoolean("container", "slave", false));
+ daemon.setReplica(ReplicaUtil.isReplica(baseConfig) || ReplicaUtil.isReplica(cfg));
mergeTestConfig(cfg);
// Set the log4j configuration to an invalid one to prevent system logs
// from getting configured and creating log files.
@@ -374,7 +413,8 @@ public class GerritServer implements AutoCloseable {
cfg.setString(
"accountPatchReviewDb", null, "url", JdbcAccountPatchReviewStore.TEST_IN_MEMORY_URL);
daemon.setEnableHttpd(desc.httpd());
- daemon.setLuceneModule(LuceneIndexModule.singleVersionAllLatest(0, isSlave(baseConfig)));
+ daemon.setLuceneModule(
+ LuceneIndexModule.singleVersionAllLatest(0, ReplicaUtil.isReplica(baseConfig)));
daemon.setDatabaseForTesting(
ImmutableList.of(
new InMemoryTestingDatabaseModule(cfg, site, inMemoryRepoManager),
@@ -390,10 +430,6 @@ public class GerritServer implements AutoCloseable {
return new GerritServer(desc, null, createTestInjector(daemon), daemon, null);
}
- private static boolean isSlave(Config baseConfig) {
- return baseConfig.getBoolean("container", "slave", false);
- }
-
private static GerritServer startOnDisk(
Description desc,
Path site,
@@ -434,9 +470,13 @@ public class GerritServer implements AutoCloseable {
private static void mergeTestConfig(Config cfg) {
String forceEphemeralPort = String.format("%s:0", getLocalHost().getHostName());
String url = "http://" + forceEphemeralPort + "/";
- cfg.setString("gerrit", null, "canonicalWebUrl", url);
- cfg.setString("httpd", null, "listenUrl", url);
+ if (cfg.getString("gerrit", null, "canonicalWebUrl") == null) {
+ cfg.setString("gerrit", null, "canonicalWebUrl", url);
+ }
+ if (cfg.getString("httpd", null, "listenUrl") == null) {
+ cfg.setString("httpd", null, "listenUrl", url);
+ }
if (cfg.getString("sshd", null, "listenAddress") == null) {
cfg.setString("sshd", null, "listenAddress", forceEphemeralPort);
}
@@ -452,11 +492,13 @@ public class GerritServer implements AutoCloseable {
cfg.setInt("sshd", null, "commandStartThreads", 1);
cfg.setInt("receive", null, "threadPoolSize", 1);
cfg.setInt("index", null, "threads", 1);
- cfg.setBoolean("index", null, "reindexAfterRefUpdate", false);
+ if (cfg.getString("index", null, "reindexAfterRefUpdate") == null) {
+ cfg.setBoolean("index", null, "reindexAfterRefUpdate", false);
+ }
}
private static Injector createTestInjector(Daemon daemon) throws Exception {
- Injector sysInjector = get(daemon, "sysInjector");
+ Injector sysInjector = getInjector(daemon, "sysInjector");
Module module =
new FactoryModule() {
@Override
@@ -489,13 +531,14 @@ public class GerritServer implements AutoCloseable {
return sysInjector.createChildInjector(module);
}
- @SuppressWarnings("unchecked")
- private static <T> T get(Object obj, String field)
+ private static Injector getInjector(Object obj, String field)
throws SecurityException, NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
- return (T) f.get(obj);
+ Object v = f.get(obj);
+ checkArgument(v instanceof Injector, "not an Injector: %s", v);
+ return (Injector) f.get(obj);
}
private static InetAddress getLocalHost() {
@@ -551,7 +594,7 @@ public class GerritServer implements AutoCloseable {
Path site = server.testInjector.getInstance(Key.get(Path.class, SitePath.class));
Config cfg = server.testInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
- cfg.setBoolean("container", null, "slave", true);
+ cfg.setBoolean("container", null, "replica", true);
InMemoryRepositoryManager inMemoryRepoManager = null;
if (hasBinding(server.testInjector, InMemoryRepositoryManager.class)) {
diff --git a/java/com/google/gerrit/acceptance/GitClientVersion.java b/java/com/google/gerrit/acceptance/GitClientVersion.java
new file mode 100644
index 0000000000..4c9a32dc72
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/GitClientVersion.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import static java.util.stream.Collectors.joining;
+
+import java.util.stream.IntStream;
+
+/** Class to parse and represent version of git-core client */
+public class GitClientVersion implements Comparable<GitClientVersion> {
+ private final int v[];
+
+ /**
+ * Constructor to represent instance for minimum supported git-core version
+ *
+ * @param parts version passed as single digits
+ */
+ public GitClientVersion(int... parts) {
+ this.v = parts;
+ }
+
+ /**
+ * Parse the git-core version as returned by git version command
+ *
+ * @param version String returned by git version command
+ */
+ public GitClientVersion(String version) {
+ // "git version x.y.z", at Google "git version x.y.z.gXXXXXXXXXX-goog"
+ String parts[] = version.split(" ")[2].split("\\.");
+ int numParts = Math.min(parts.length, 3); // ignore Google-specific part of the version
+ v = new int[numParts];
+ for (int i = 0; i < numParts; i++) {
+ v[i] = Integer.valueOf(parts[i]);
+ }
+ }
+
+ @Override
+ public int compareTo(GitClientVersion o) {
+ int m = Math.max(v.length, o.v.length);
+ for (int i = 0; i < m; i++) {
+ int l = i < v.length ? v[i] : 0;
+ int r = i < o.v.length ? o.v[i] : 0;
+ if (l != r) {
+ return l < r ? -1 : 1;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return IntStream.of(v).mapToObj(String::valueOf).collect(joining("."));
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/GitUtil.java b/java/com/google/gerrit/acceptance/GitUtil.java
index cdfdae7682..ae72793397 100644
--- a/java/com/google/gerrit/acceptance/GitUtil.java
+++ b/java/com/google/gerrit/acceptance/GitUtil.java
@@ -15,13 +15,14 @@
package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
import com.google.gerrit.common.FooterConstants;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
@@ -109,19 +110,14 @@ public class GitUtil {
throws Exception {
DfsRepositoryDescription desc = new DfsRepositoryDescription("clone of " + project.get());
- FS fs = FS.detect();
-
- // Avoid leaking user state into our tests.
- fs.setUserHome(null);
-
- InMemoryRepository dest =
- new InMemoryRepository.Builder()
- .setRepositoryDescription(desc)
- // SshTransport depends on a real FS to read ~/.ssh/config, but
- // InMemoryRepository by default uses a null FS.
- // TODO(dborowitz): Remove when we no longer depend on SSH.
- .setFS(fs)
- .build();
+ InMemoryRepository.Builder b = new InMemoryRepository.Builder().setRepositoryDescription(desc);
+ if (uri.startsWith("ssh://")) {
+ // SshTransport depends on a real FS to read ~/.ssh/config, but InMemoryRepository by default
+ // uses a null FS.
+ // Avoid leaking user state into our tests.
+ b.setFS(FS.detect().setUserHome(null));
+ }
+ InMemoryRepository dest = b.build();
Config cfg = dest.getConfig();
cfg.setString("remote", "origin", "url", uri);
cfg.setString("remote", "origin", "fetch", "+refs/heads/*:refs/remotes/origin/*");
@@ -134,11 +130,6 @@ public class GitUtil {
return testRepo;
}
- public static TestRepository<InMemoryRepository> cloneProject(
- Project.NameKey project, SshSession sshSession) throws Exception {
- return cloneProject(project, sshSession.getUrl() + "/" + project.get());
- }
-
public static Ref createAnnotatedTag(TestRepository<?> testRepo, String name, PersonIdent tagger)
throws GitAPIException {
TagCommand cmd =
@@ -209,13 +200,13 @@ public class GitUtil {
public static void assertPushOk(PushResult result, String ref) {
RemoteRefUpdate rru = result.getRemoteUpdate(ref);
- assertThat(rru.getStatus()).named(rru.toString()).isEqualTo(RemoteRefUpdate.Status.OK);
+ assertWithMessage(rru.toString()).that(rru.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK);
}
public static void assertPushRejected(PushResult result, String ref, String expectedMessage) {
RemoteRefUpdate rru = result.getRemoteUpdate(ref);
- assertThat(rru.getStatus())
- .named(rru.toString())
+ assertWithMessage(rru.toString())
+ .that(rru.getStatus())
.isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
assertThat(rru.getMessage()).isEqualTo(expectedMessage);
}
diff --git a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index a704d2fc8b..a3207e28f1 100644
--- a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -55,8 +55,6 @@ class InMemoryTestingDatabaseModule extends LifecycleModule {
@Override
protected void configure() {
bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(cfg);
-
- // TODO(dborowitz): Use jimfs.
bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
if (repoManager != null) {
diff --git a/java/com/google/gerrit/acceptance/InProcessProtocol.java b/java/com/google/gerrit/acceptance/InProcessProtocol.java
index 69715a9cf8..feda6bf6b3 100644
--- a/java/com/google/gerrit/acceptance/InProcessProtocol.java
+++ b/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -21,24 +21,24 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.InProcessProtocol.Context;
import com.google.gerrit.common.data.Capable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.config.GerritRequestModule;
-import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
+import com.google.gerrit.server.git.PermissionAwareRepositoryManager;
import com.google.gerrit.server.git.ReceivePackInitializer;
import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.UploadPackInitializer;
+import com.google.gerrit.server.git.UsersSelfAdvertiseRefsHook;
import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -90,8 +90,6 @@ class InProcessProtocol extends TestProtocol<Context> {
@Provides
@RemotePeer
SocketAddress getSocketAddress() {
- // TODO(dborowitz): Could potentially fake this with thread ID or
- // something.
throw new OutOfScopeException("No remote peer in acceptance tests");
}
};
@@ -203,6 +201,7 @@ class InProcessProtocol extends TestProtocol<Context> {
private final ThreadLocalRequestContext threadContext;
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
+ private final UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook;
@Inject
Upload(
@@ -212,7 +211,8 @@ class InProcessProtocol extends TestProtocol<Context> {
UploadValidators.Factory uploadValidatorsFactory,
ThreadLocalRequestContext threadContext,
ProjectCache projectCache,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook) {
this.transferConfig = transferConfig;
this.uploadPackInitializers = uploadPackInitializers;
this.preUploadHooks = preUploadHooks;
@@ -220,6 +220,7 @@ class InProcessProtocol extends TestProtocol<Context> {
this.threadContext = threadContext;
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
+ this.usersSelfAdvertiseRefsHook = usersSelfAdvertiseRefsHook;
}
@Override
@@ -249,12 +250,17 @@ class InProcessProtocol extends TestProtocol<Context> {
if (projectState == null) {
throw new RuntimeException("can't load project state for " + req.project.get());
}
- UploadPack up = new UploadPack(repo);
+ Repository permissionAwareRepository = PermissionAwareRepositoryManager.wrap(repo, perm);
+ UploadPack up = new UploadPack(permissionAwareRepository);
up.setPackConfig(transferConfig.getPackConfig());
up.setTimeout(transferConfig.getTimeout());
- up.setAdvertiseRefsHook(new DefaultAdvertiseRefsHook(perm, RefFilterOptions.defaults()));
+ if (projectState.isAllUsers()) {
+ up.setAdvertiseRefsHook(usersSelfAdvertiseRefsHook);
+ }
List<PreUploadHook> hooks = Lists.newArrayList(preUploadHooks);
- hooks.add(uploadValidatorsFactory.create(projectState.getProject(), repo, "localhost-test"));
+ hooks.add(
+ uploadValidatorsFactory.create(
+ projectState.getProject(), permissionAwareRepository, "localhost-test"));
up.setPreUploadHook(PreUploadHookChain.newChain(hooks));
uploadPackInitializers.runEach(initializer -> initializer.init(req.project, up));
return up;
@@ -343,7 +349,7 @@ class InProcessProtocol extends TestProtocol<Context> {
ImmutableList.<PostReceiveHook>builder()
.add(
(pack, commands) -> {
- if (affectsSize(pack, commands)) {
+ if (affectsSize(pack)) {
try {
quotaBackend
.user(identifiedUser)
diff --git a/java/com/google/gerrit/acceptance/ProjectResetter.java b/java/com/google/gerrit/acceptance/ProjectResetter.java
index ea958f661b..a528974478 100644
--- a/java/com/google/gerrit/acceptance/ProjectResetter.java
+++ b/java/com/google/gerrit/acceptance/ProjectResetter.java
@@ -15,7 +15,7 @@
package com.google.gerrit.acceptance;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS;
+import static com.google.gerrit.entities.RefNames.REFS_USERS;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableList;
@@ -23,11 +23,11 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.index.RefState;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupIncludeCache;
diff --git a/java/com/google/gerrit/acceptance/PushOneCommit.java b/java/com/google/gerrit/acceptance/PushOneCommit.java
index e15dd40d57..3ccbe4d85b 100644
--- a/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static org.junit.Assert.assertEquals;
@@ -24,9 +25,9 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
@@ -395,15 +396,15 @@ public class PushOneCommit {
public void assertErrorStatus() {
RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
assertThat(refUpdate).isNotNull();
- assertThat(refUpdate.getStatus())
- .named(message(refUpdate))
+ assertWithMessage(message(refUpdate))
+ .that(refUpdate.getStatus())
.isEqualTo(Status.REJECTED_OTHER_REASON);
}
private void assertStatus(Status expectedStatus, String expectedMessage) {
RemoteRefUpdate refUpdate = result.getRemoteUpdate(ref);
assertThat(refUpdate).isNotNull();
- assertThat(refUpdate.getStatus()).named(message(refUpdate)).isEqualTo(expectedStatus);
+ assertWithMessage(message(refUpdate)).that(refUpdate.getStatus()).isEqualTo(expectedStatus);
if (expectedMessage == null) {
assertThat(refUpdate.getMessage()).isNull();
} else {
diff --git a/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java b/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
index 19910db042..e943519d19 100644
--- a/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
+++ b/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
@@ -14,12 +14,12 @@
package com.google.gerrit.acceptance;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.query.change.ChangeData;
diff --git a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
index bd8a926bf8..b985e409f0 100644
--- a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
@@ -20,6 +20,7 @@ import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.index.group.GroupIndexer;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.inject.Inject;
import com.google.inject.Scopes;
import java.io.IOException;
@@ -50,8 +51,8 @@ public class ReindexGroupsAtStartup implements LifecycleListener {
@Override
public void start() {
- // Gerrit slaves without a reindex
- if (cfg.getBoolean("container", "slave", false)
+ // Gerrit replicas without a reindex
+ if (ReplicaUtil.isReplica(cfg)
&& !cfg.getBoolean("index", "scheduledIndexer", "runOnStartup", true)) {
return;
}
diff --git a/java/com/google/gerrit/acceptance/RestResponse.java b/java/com/google/gerrit/acceptance/RestResponse.java
index e8de5c6220..a045d803db 100644
--- a/java/com/google/gerrit/acceptance/RestResponse.java
+++ b/java/com/google/gerrit/acceptance/RestResponse.java
@@ -17,13 +17,23 @@ package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.httpd.restapi.RestApiServlet.JSON_MAGIC;
+import static com.google.gerrit.httpd.restapi.RestApiServlet.SC_UNPROCESSABLE_ENTITY;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
+import static javax.servlet.http.HttpServletResponse.SC_CREATED;
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
+import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
-import org.apache.http.HttpStatus;
public class RestResponse extends HttpResponse {
@@ -47,47 +57,47 @@ public class RestResponse extends HttpResponse {
}
public void assertOK() throws Exception {
- assertStatus(HttpStatus.SC_OK);
+ assertStatus(SC_OK);
}
public void assertNotFound() throws Exception {
- assertStatus(HttpStatus.SC_NOT_FOUND);
+ assertStatus(SC_NOT_FOUND);
}
public void assertConflict() throws Exception {
- assertStatus(HttpStatus.SC_CONFLICT);
+ assertStatus(SC_CONFLICT);
}
public void assertForbidden() throws Exception {
- assertStatus(HttpStatus.SC_FORBIDDEN);
+ assertStatus(SC_FORBIDDEN);
}
public void assertNoContent() throws Exception {
- assertStatus(HttpStatus.SC_NO_CONTENT);
+ assertStatus(SC_NO_CONTENT);
}
public void assertBadRequest() throws Exception {
- assertStatus(HttpStatus.SC_BAD_REQUEST);
+ assertStatus(SC_BAD_REQUEST);
}
public void assertUnprocessableEntity() throws Exception {
- assertStatus(HttpStatus.SC_UNPROCESSABLE_ENTITY);
+ assertStatus(SC_UNPROCESSABLE_ENTITY);
}
public void assertMethodNotAllowed() throws Exception {
- assertStatus(HttpStatus.SC_METHOD_NOT_ALLOWED);
+ assertStatus(SC_METHOD_NOT_ALLOWED);
}
public void assertCreated() throws Exception {
- assertStatus(HttpStatus.SC_CREATED);
+ assertStatus(SC_CREATED);
}
public void assertPreconditionFailed() throws Exception {
- assertStatus(HttpStatus.SC_PRECONDITION_FAILED);
+ assertStatus(SC_PRECONDITION_FAILED);
}
public void assertTemporaryRedirect(String path) throws Exception {
- assertStatus(HttpStatus.SC_MOVED_TEMPORARILY);
+ assertStatus(SC_MOVED_TEMPORARILY);
assertThat(URI.create(getHeader("Location")).getPath()).isEqualTo(path);
}
}
diff --git a/java/com/google/gerrit/acceptance/SshdModule.java b/java/com/google/gerrit/acceptance/SshdModule.java
index 185d6e2a3d..873ba17707 100644
--- a/java/com/google/gerrit/acceptance/SshdModule.java
+++ b/java/com/google/gerrit/acceptance/SshdModule.java
@@ -34,7 +34,7 @@ public class SshdModule extends AbstractModule {
if (keys == null) {
keys = new SimpleGeneratorHostKeyProvider();
keys.setAlgorithm("RSA");
- keys.loadKeys();
+ keys.loadKeys(null);
}
return keys;
}
diff --git a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index d20124ae7b..43fe4ebfa0 100644
--- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -14,7 +14,7 @@
package com.google.gerrit.acceptance;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
import static org.junit.Assert.fail;
@@ -29,12 +29,12 @@ import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.launcher.GerritLauncher;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.git.DelegateSystemReader;
import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Injector;
import com.google.inject.Module;
@@ -67,10 +67,10 @@ public abstract class StandaloneSiteTest {
private ServerContext(GerritServer server) throws Exception {
this.server = server;
Injector i = server.getTestInjector();
- if (adminId == null) {
- adminId = i.getInstance(AccountCreator.class).admin().id();
+ if (admin == null) {
+ admin = i.getInstance(AccountCreator.class).admin();
}
- ctx = i.getInstance(OneOffRequestContext.class).openAs(adminId);
+ ctx = i.getInstance(OneOffRequestContext.class).openAs(admin.id());
GerritApi gApi = i.getInstance(GerritApi.class);
try {
@@ -125,7 +125,7 @@ public abstract class StandaloneSiteTest {
@Rule public RuleChain ruleChain = RuleChain.outerRule(tempSiteDir).around(testRunner);
protected SitePaths sitePaths;
- protected Account.Id adminId;
+ protected TestAccount admin;
private GerritServer.Description serverDesc;
private SystemReader oldSystemReader;
@@ -143,20 +143,10 @@ public abstract class StandaloneSiteTest {
private static SystemReader setFakeSystemReader(File tempDir) {
SystemReader oldSystemReader = SystemReader.getInstance();
SystemReader.setInstance(
- new SystemReader() {
+ new DelegateSystemReader(oldSystemReader) {
@Override
- public String getHostname() {
- return oldSystemReader.getHostname();
- }
-
- @Override
- public String getenv(String variable) {
- return oldSystemReader.getenv(variable);
- }
-
- @Override
- public String getProperty(String key) {
- return oldSystemReader.getProperty(key);
+ public FileBasedConfig openJGitConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "jgit.config"), FS.detect());
}
@Override
@@ -168,16 +158,6 @@ public abstract class StandaloneSiteTest {
public FileBasedConfig openSystemConfig(Config parent, FS fs) {
return new FileBasedConfig(parent, new File(tempDir, "system.config"), FS.detect());
}
-
- @Override
- public long getCurrentTime() {
- return oldSystemReader.getCurrentTime();
- }
-
- @Override
- public int getTimezone(long when) {
- return oldSystemReader.getTimezone(when);
- }
});
return oldSystemReader;
}
@@ -214,8 +194,8 @@ public abstract class StandaloneSiteTest {
// Use invokeProgram with the current classloader, rather than mainImpl, which would create a
// new classloader. This is necessary so that static state, particularly the SystemReader, is
// shared with the test method.
- assertThat(GerritLauncher.invokeProgram(StandaloneSiteTest.class.getClassLoader(), args))
- .named("gerrit.war " + Arrays.stream(args).collect(joining(" ")))
+ assertWithMessage("gerrit.war " + Arrays.stream(args).collect(joining(" ")))
+ .that(GerritLauncher.invokeProgram(StandaloneSiteTest.class.getClassLoader(), args))
.isEqualTo(0);
}
diff --git a/java/com/google/gerrit/acceptance/TestAccount.java b/java/com/google/gerrit/acceptance/TestAccount.java
index c937aed558..07bb739bf1 100644
--- a/java/com/google/gerrit/acceptance/TestAccount.java
+++ b/java/com/google/gerrit/acceptance/TestAccount.java
@@ -21,8 +21,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.common.net.InetAddresses;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
import java.net.InetSocketAddress;
import java.util.Arrays;
import org.apache.http.client.utils.URIBuilder;
diff --git a/java/com/google/gerrit/acceptance/UseClockStep.java b/java/com/google/gerrit/acceptance/UseClockStep.java
new file mode 100644
index 0000000000..10a93fed53
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/UseClockStep.java
@@ -0,0 +1,42 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Annotation to use a clock step for the execution of acceptance tests (the test class must inherit
+ * from {@link AbstractDaemonTest}).
+ *
+ * <p>Annotations on method level override annotations on class level.
+ */
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+public @interface UseClockStep {
+ /** Amount to increment clock by on each lookup. */
+ long clockStep() default 1L;
+
+ /** Time unit for {@link #clockStep()}. */
+ TimeUnit clockStepUnit() default TimeUnit.SECONDS;
+
+ /** Whether the clock should initially be set to {@link java.time.Instant#EPOCH}. */
+ boolean startAtEpoch() default false;
+}
diff --git a/java/com/google/gerrit/acceptance/UseSystemTime.java b/java/com/google/gerrit/acceptance/UseSystemTime.java
new file mode 100644
index 0000000000..e9cbd47369
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/UseSystemTime.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to use the system time for the execution of acceptance tests (the test class must
+ * inherit from {@link AbstractDaemonTest}).
+ *
+ * <p>Can only be applied on method level, since using system time on class level is the default if
+ * {@link UseClockStep} is not used.
+ *
+ * <p>Intended to be used to use system time for single tests when the test class is annotated with
+ * {@link UseClockStep}.
+ */
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface UseSystemTime {}
diff --git a/java/com/google/gerrit/acceptance/UseTimezone.java b/java/com/google/gerrit/acceptance/UseTimezone.java
new file mode 100644
index 0000000000..7412030b83
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/UseTimezone.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to set a timezone for the execution of acceptance tests (the test class must inherit
+ * from {@link AbstractDaemonTest}).
+ *
+ * <p>Annotations on method level override annotations on class level.
+ */
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+public @interface UseTimezone {
+ /** The timezone that should be used for the test, e.g. "US/Eastern". */
+ String timezone();
+}
diff --git a/java/com/google/gerrit/acceptance/rest/CreateTestPlugin.java b/java/com/google/gerrit/acceptance/rest/CreateTestPlugin.java
index 85a2d7b012..2a77d31c3a 100644
--- a/java/com/google/gerrit/acceptance/rest/CreateTestPlugin.java
+++ b/java/com/google/gerrit/acceptance/rest/CreateTestPlugin.java
@@ -33,7 +33,8 @@ public class CreateTestPlugin
}
@Override
- public Object apply(ConfigResource parentResource, IdString id, Input input) throws Exception {
+ public Response<?> apply(ConfigResource parentResource, IdString id, Input input)
+ throws Exception {
return Response.created(input);
}
}
diff --git a/java/com/google/gerrit/acceptance/rest/GetTestPlugin.java b/java/com/google/gerrit/acceptance/rest/GetTestPlugin.java
index a31cc151dd..3b6da85337 100644
--- a/java/com/google/gerrit/acceptance/rest/GetTestPlugin.java
+++ b/java/com/google/gerrit/acceptance/rest/GetTestPlugin.java
@@ -27,7 +27,7 @@ import com.google.inject.Singleton;
public class GetTestPlugin implements RestReadView<PluginResource> {
@Override
- public Object apply(PluginResource resource)
+ public Response<?> apply(PluginResource resource)
throws AuthException, BadRequestException, ResourceConflictException, Exception {
return Response.ok("Foo");
}
diff --git a/java/com/google/gerrit/acceptance/rest/ListTestPlugin.java b/java/com/google/gerrit/acceptance/rest/ListTestPlugin.java
index 00e071ccb5..ce037a73c9 100644
--- a/java/com/google/gerrit/acceptance/rest/ListTestPlugin.java
+++ b/java/com/google/gerrit/acceptance/rest/ListTestPlugin.java
@@ -18,14 +18,15 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.ConfigResource;
public class ListTestPlugin implements RestReadView<ConfigResource> {
@Override
- public Object apply(ConfigResource resource)
+ public Response<?> apply(ConfigResource resource)
throws AuthException, BadRequestException, ResourceConflictException, Exception {
- return ImmutableList.of();
+ return Response.ok(ImmutableList.of());
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
index 61b828ee36..efae223749 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
@@ -14,7 +14,7 @@
package com.google.gerrit.acceptance.testsuite.account;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
/**
* An aggregation of operations on accounts for test purposes.
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
index 7641e4784f..f1b840a466 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
@@ -16,7 +16,7 @@ package com.google.gerrit.acceptance.testsuite.account;
import static com.google.common.base.Preconditions.checkState;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.Accounts;
@@ -61,14 +61,14 @@ public class AccountOperationsImpl implements AccountOperations {
private Account.Id createAccount(TestAccountCreation accountCreation) throws Exception {
AccountsUpdate.AccountUpdater accountUpdater =
(account, updateBuilder) ->
- fillBuilder(updateBuilder, accountCreation, account.getAccount().getId());
+ fillBuilder(updateBuilder, accountCreation, account.account().id());
AccountState createdAccount = createAccount(accountUpdater);
- return createdAccount.getAccount().getId();
+ return createdAccount.account().id();
}
private AccountState createAccount(AccountsUpdate.AccountUpdater accountUpdater)
throws IOException, ConfigInvalidException {
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
return accountsUpdate.insert("Create Test Account", accountId, accountUpdater);
}
@@ -129,13 +129,13 @@ public class AccountOperationsImpl implements AccountOperations {
}
private TestAccount toTestAccount(AccountState accountState) {
- Account account = accountState.getAccount();
+ Account account = accountState.account();
return TestAccount.builder()
- .accountId(account.getId())
- .preferredEmail(Optional.ofNullable(account.getPreferredEmail()))
- .fullname(Optional.ofNullable(account.getFullName()))
- .username(accountState.getUserName())
- .active(accountState.getAccount().isActive())
+ .accountId(account.id())
+ .preferredEmail(Optional.ofNullable(account.preferredEmail()))
+ .fullname(Optional.ofNullable(account.fullName()))
+ .username(accountState.userName())
+ .active(accountState.account().isActive())
.build();
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestAccount.java b/java/com/google/gerrit/acceptance/testsuite/account/TestAccount.java
index e7ffeeccbc..2574d55a1b 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestAccount.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestAccount.java
@@ -15,7 +15,7 @@
package com.google.gerrit.acceptance.testsuite.account;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.util.Optional;
@AutoValue
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
index f2414e029e..983fec02da 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
@@ -16,7 +16,7 @@ package com.google.gerrit.acceptance.testsuite.account;
import com.google.auto.value.AutoValue;
import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.util.Optional;
@AutoValue
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
index 4847fdb5bc..6c95360a05 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
@@ -19,7 +19,7 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.gerrit.acceptance.SshEnabled;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
index 533d06b0fc..b9414e1a2b 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
@@ -14,7 +14,7 @@
package com.google.gerrit.acceptance.testsuite.group;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
/**
* An aggregation of operations on groups for test purposes.
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
index e0ddee56fd..fd5c003e48 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
@@ -16,9 +16,9 @@ package com.google.gerrit.acceptance.testsuite.group;
import static com.google.common.base.Preconditions.checkState;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.exceptions.NoSuchGroupException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.GroupUUID;
@@ -78,10 +78,10 @@ public class GroupOperationsImpl implements GroupOperations {
}
private InternalGroupCreation toInternalGroupCreation(TestGroupCreation groupCreation) {
- AccountGroup.Id groupId = new AccountGroup.Id(seq.nextGroupId());
+ AccountGroup.Id groupId = AccountGroup.id(seq.nextGroupId());
String groupName = groupCreation.name().orElse("group-with-id-" + groupId.get());
AccountGroup.UUID groupUuid = GroupUUID.make(groupName, serverIdent);
- AccountGroup.NameKey nameKey = new AccountGroup.NameKey(groupName);
+ AccountGroup.NameKey nameKey = AccountGroup.nameKey(groupName);
return InternalGroupCreation.builder()
.setId(groupId)
.setGroupUUID(groupUuid)
@@ -153,7 +153,7 @@ public class GroupOperationsImpl implements GroupOperations {
private InternalGroupUpdate toInternalGroupUpdate(TestGroupUpdate groupUpdate) {
InternalGroupUpdate.Builder builder = InternalGroupUpdate.builder();
- groupUpdate.name().map(AccountGroup.NameKey::new).ifPresent(builder::setName);
+ groupUpdate.name().map(AccountGroup::nameKey).ifPresent(builder::setName);
groupUpdate.description().ifPresent(builder::setDescription);
groupUpdate.ownerGroupUuid().ifPresent(builder::setOwnerGroupUUID);
groupUpdate.visibleToAll().ifPresent(builder::setVisibleToAll);
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
index b450304361..c885353320 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
@@ -16,8 +16,8 @@ package com.google.gerrit.acceptance.testsuite.group;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.sql.Timestamp;
import java.util.Optional;
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
index 612ce2a47f..8bb7b23b9a 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
@@ -18,8 +18,8 @@ import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.util.Optional;
import java.util.Set;
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
index bc9d569aa7..47c7117efc 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
@@ -18,8 +18,8 @@ import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.acceptance.testsuite.ThrowingConsumer;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/BUILD b/java/com/google/gerrit/acceptance/testsuite/project/BUILD
new file mode 100644
index 0000000000..8a3a23a5b6
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/project/BUILD
@@ -0,0 +1,22 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_testonly = 1)
+
+java_library(
+ name = "project",
+ srcs = glob(["*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//java/com/google/gerrit/acceptance:function",
+ "//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
+ "//java/com/google/gerrit/extensions:api",
+ "//java/com/google/gerrit/server",
+ "//lib:guava",
+ "//lib:jgit",
+ "//lib/auto:auto-value",
+ "//lib/auto:auto-value-annotations",
+ "//lib/commons:lang",
+ "//lib/guice",
+ ],
+)
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java
index 029d161f11..2db611baeb 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java
@@ -14,7 +14,9 @@
package com.google.gerrit.acceptance.testsuite.project;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.project.ProjectConfig;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.revwalk.RevCommit;
/**
@@ -28,6 +30,9 @@ public interface ProjectOperations {
PerProjectOperations project(Project.NameKey key);
+ /** Starts a fluent chain for updating All-Projects. */
+ TestProjectUpdate.Builder allProjectsForUpdate();
+
interface PerProjectOperations {
/**
* Returns the commit for this project. branchName can either be shortened ("HEAD", "master") or
@@ -40,5 +45,33 @@ public interface ProjectOperations {
* fully qualified refname ("refs/heads/master").
*/
boolean hasHead(String branchName);
+
+ /** Returns a fresh {@link ProjectConfig} read from the tip of {@code refs/meta/config}. */
+ ProjectConfig getProjectConfig();
+
+ /**
+ * Returns a fresh JGit {@link Config} instance read from {@code project.config} at the tip of
+ * {@code refs/meta/config}. Does not have a base config, i.e. does not respect {@code
+ * $site_path/etc/project.config}.
+ */
+ Config getConfig();
+
+ /**
+ * Starts the fluent chain to update a project. The returned builder can be used to specify how
+ * the attributes of the project should be modified. To update the project for real, the {@link
+ * TestProjectUpdate.Builder#update()} must be called.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * projectOperations
+ * .forUpdate()
+ * .add(allow(ABANDON).ref("refs/*").group(REGISTERED_USERS))
+ * .update();
+ * </pre>
+ *
+ * @return a builder to update the check.
+ */
+ TestProjectUpdate.Builder forUpdate();
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
index 28be3f3a7e..7797fe0153 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
@@ -14,30 +14,68 @@
package com.google.gerrit.acceptance.testsuite.project;
-import com.google.common.base.Preconditions;
+import static com.google.gerrit.entities.RefNames.REFS_CONFIG;
+import static com.google.gerrit.server.project.ProjectConfig.PROJECT_CONFIG;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.gerrit.acceptance.testsuite.project.TestProjectCreation.Builder;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestCapability;
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestLabelPermission;
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestPermission;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.project.CreateProjectArgs;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectCreator;
import com.google.inject.Inject;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.commons.lang.RandomStringUtils;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
public class ProjectOperationsImpl implements ProjectOperations {
- private final ProjectCreator projectCreator;
+ private final AllProjectsName allProjectsName;
private final GitRepositoryManager repoManager;
+ private final MetaDataUpdate.Server metaDataUpdateFactory;
+ private final ProjectCache projectCache;
+ private final ProjectConfig.Factory projectConfigFactory;
+ private final ProjectCreator projectCreator;
@Inject
- ProjectOperationsImpl(GitRepositoryManager repoManager, ProjectCreator projectCreator) {
+ ProjectOperationsImpl(
+ AllProjectsName allProjectsName,
+ GitRepositoryManager repoManager,
+ MetaDataUpdate.Server metaDataUpdateFactory,
+ ProjectCache projectCache,
+ ProjectConfig.Factory projectConfigFactory,
+ ProjectCreator projectCreator) {
+ this.allProjectsName = allProjectsName;
this.repoManager = repoManager;
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.projectCache = projectCache;
+ this.projectConfigFactory = projectConfigFactory;
this.projectCreator = projectCreator;
}
@@ -58,7 +96,7 @@ public class ProjectOperationsImpl implements ProjectOperations {
args.ownerIds = new ArrayList<>();
projectCreation.submitType().ifPresent(st -> args.submitType = st);
projectCreator.createProject(args);
- return new Project.NameKey(name);
+ return Project.nameKey(name);
}
@Override
@@ -66,8 +104,12 @@ public class ProjectOperationsImpl implements ProjectOperations {
return new PerProjectOperations(key);
}
- private class PerProjectOperations implements ProjectOperations.PerProjectOperations {
+ @Override
+ public TestProjectUpdate.Builder allProjectsForUpdate() {
+ return project(allProjectsName).forUpdate();
+ }
+ private class PerProjectOperations implements ProjectOperations.PerProjectOperations {
Project.NameKey nameKey;
PerProjectOperations(Project.NameKey nameKey) {
@@ -76,7 +118,7 @@ public class ProjectOperationsImpl implements ProjectOperations {
@Override
public RevCommit getHead(String branch) {
- return Preconditions.checkNotNull(headOrNull(branch));
+ return requireNonNull(headOrNull(branch));
}
@Override
@@ -84,6 +126,88 @@ public class ProjectOperationsImpl implements ProjectOperations {
return headOrNull(branch) != null;
}
+ @Override
+ public TestProjectUpdate.Builder forUpdate() {
+ return TestProjectUpdate.builder(nameKey, allProjectsName, this::updateProject);
+ }
+
+ private void updateProject(TestProjectUpdate projectUpdate)
+ throws IOException, ConfigInvalidException {
+ try (MetaDataUpdate metaDataUpdate = metaDataUpdateFactory.create(nameKey)) {
+ ProjectConfig projectConfig = projectConfigFactory.read(metaDataUpdate);
+ removePermissions(projectConfig, projectUpdate.removedPermissions());
+ addCapabilities(projectConfig, projectUpdate.addedCapabilities());
+ addPermissions(projectConfig, projectUpdate.addedPermissions());
+ addLabelPermissions(projectConfig, projectUpdate.addedLabelPermissions());
+ setExclusiveGroupPermissions(projectConfig, projectUpdate.exclusiveGroupPermissions());
+ projectConfig.commit(metaDataUpdate);
+ }
+ projectCache.evict(nameKey);
+ }
+
+ private void removePermissions(
+ ProjectConfig projectConfig,
+ ImmutableList<TestProjectUpdate.TestPermissionKey> removedPermissions) {
+ for (TestProjectUpdate.TestPermissionKey p : removedPermissions) {
+ Permission permission =
+ projectConfig.getAccessSection(p.section(), true).getPermission(p.name(), true);
+ if (p.group().isPresent()) {
+ GroupReference group = new GroupReference(p.group().get(), p.group().get().get());
+ group = projectConfig.resolve(group);
+ permission.removeRule(group);
+ } else {
+ permission.clearRules();
+ }
+ }
+ }
+
+ private void addCapabilities(
+ ProjectConfig projectConfig, ImmutableList<TestCapability> addedCapabilities) {
+ for (TestCapability c : addedCapabilities) {
+ PermissionRule rule = newRule(projectConfig, c.group());
+ rule.setRange(c.min(), c.max());
+ projectConfig
+ .getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true)
+ .getPermission(c.name(), true)
+ .add(rule);
+ }
+ }
+
+ private void addPermissions(
+ ProjectConfig projectConfig, ImmutableList<TestPermission> addedPermissions) {
+ for (TestPermission p : addedPermissions) {
+ PermissionRule rule = newRule(projectConfig, p.group());
+ rule.setAction(p.action());
+ rule.setForce(p.force());
+ projectConfig.getAccessSection(p.ref(), true).getPermission(p.name(), true).add(rule);
+ }
+ }
+
+ private void addLabelPermissions(
+ ProjectConfig projectConfig, ImmutableList<TestLabelPermission> addedLabelPermissions) {
+ for (TestLabelPermission p : addedLabelPermissions) {
+ PermissionRule rule = newRule(projectConfig, p.group());
+ rule.setAction(p.action());
+ rule.setRange(p.min(), p.max());
+ String permissionName =
+ p.impersonation() ? Permission.forLabelAs(p.name()) : Permission.forLabel(p.name());
+ Permission permission =
+ projectConfig.getAccessSection(p.ref(), true).getPermission(permissionName, true);
+ permission.add(rule);
+ }
+ }
+
+ private void setExclusiveGroupPermissions(
+ ProjectConfig projectConfig,
+ ImmutableMap<TestProjectUpdate.TestPermissionKey, Boolean> exclusiveGroupPermissions) {
+ exclusiveGroupPermissions.forEach(
+ (key, exclusive) ->
+ projectConfig
+ .getAccessSection(key.section(), true)
+ .getPermission(key.name(), true)
+ .setExclusiveGroup(exclusive));
+ }
+
private RevCommit headOrNull(String branch) {
if (!branch.startsWith(Constants.R_REFS)) {
branch = RefNames.REFS_HEADS + branch;
@@ -97,5 +221,45 @@ public class ProjectOperationsImpl implements ProjectOperations {
throw new IllegalStateException(e);
}
}
+
+ @Override
+ public ProjectConfig getProjectConfig() {
+ try (Repository repo = repoManager.openRepository(nameKey)) {
+ ProjectConfig projectConfig = projectConfigFactory.create(nameKey);
+ projectConfig.load(nameKey, repo);
+ return projectConfig;
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public Config getConfig() {
+ try (Repository repo = repoManager.openRepository(nameKey);
+ RevWalk rw = new RevWalk(repo)) {
+ Ref ref = repo.exactRef(REFS_CONFIG);
+ if (ref == null) {
+ return new Config();
+ }
+ RevTree tree = rw.parseTree(ref.getObjectId());
+ TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(), PROJECT_CONFIG, tree);
+ if (tw == null) {
+ return new Config();
+ }
+ ObjectLoader loader = rw.getObjectReader().open(tw.getObjectId(0));
+ String text = new String(loader.getCachedBytes(), UTF_8);
+ Config config = new Config();
+ config.fromText(text);
+ return config;
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+
+ private static PermissionRule newRule(ProjectConfig project, AccountGroup.UUID groupUUID) {
+ GroupReference group = new GroupReference(groupUUID, groupUUID.get());
+ group = project.resolve(group);
+ return new PermissionRule(group);
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
index 31af1d2251..99e045c693 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
@@ -16,8 +16,8 @@ package com.google.gerrit.acceptance.testsuite.project;
import com.google.auto.value.AutoValue;
import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Project;
import java.util.Optional;
@AutoValue
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java
new file mode 100644
index 0000000000..734854b041
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java
@@ -0,0 +1,436 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.testsuite.project;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.common.data.AccessSection.GLOBAL_CAPABILITIES;
+import static java.util.Objects.requireNonNull;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.acceptance.testsuite.ThrowingConsumer;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRange;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.config.AllProjectsName;
+import java.util.Optional;
+import org.eclipse.jgit.lib.Constants;
+
+@AutoValue
+public abstract class TestProjectUpdate {
+ /** Starts a builder for allowing a capability. */
+ public static TestCapability.Builder allowCapability(String name) {
+ return TestCapability.builder().name(name);
+ }
+
+ /** Records a global capability to be updated. */
+ @AutoValue
+ public abstract static class TestCapability {
+ private static Builder builder() {
+ return new AutoValue_TestProjectUpdate_TestCapability.Builder();
+ }
+
+ abstract String name();
+
+ abstract AccountGroup.UUID group();
+
+ abstract int min();
+
+ abstract int max();
+
+ /** Builder for {@link TestCapability}. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ /** Sets the name of the capability. */
+ public abstract Builder name(String name);
+
+ abstract String name();
+
+ /** Sets the group to which the capability applies. */
+ public abstract Builder group(AccountGroup.UUID group);
+
+ abstract Builder min(int min);
+
+ abstract Optional<Integer> min();
+
+ abstract Builder max(int max);
+
+ abstract Optional<Integer> max();
+
+ /** Sets the minimum and maximum values for the capability. */
+ public Builder range(int min, int max) {
+ checkNonInvertedRange(min, max);
+ return min(min).max(max);
+ }
+
+ /** Builds the {@link TestCapability}. */
+ abstract TestCapability autoBuild();
+
+ public TestCapability build() {
+ PermissionRange.WithDefaults withDefaults = GlobalCapability.getRange(name());
+ if (withDefaults != null) {
+ int min = min().orElse(withDefaults.getDefaultMin());
+ int max = max().orElse(withDefaults.getDefaultMax());
+ range(min, max);
+ // Don't enforce range is nonempty; this is allowed for e.g. batchChangesLimit.
+ } else {
+ checkArgument(
+ !min().isPresent() && !max().isPresent(),
+ "capability %s does not support ranges",
+ name());
+ range(0, 0);
+ }
+
+ return autoBuild();
+ }
+ }
+ }
+
+ /** Starts a builder for allowing a permission. */
+ public static TestPermission.Builder allow(String name) {
+ return TestPermission.builder().name(name).action(PermissionRule.Action.ALLOW);
+ }
+
+ /** Starts a builder for denying a permission. */
+ public static TestPermission.Builder deny(String name) {
+ return TestPermission.builder().name(name).action(PermissionRule.Action.DENY);
+ }
+
+ /** Starts a builder for blocking a permission. */
+ public static TestPermission.Builder block(String name) {
+ return TestPermission.builder().name(name).action(PermissionRule.Action.BLOCK);
+ }
+
+ /**
+ * Records a permission to be updated.
+ *
+ * <p>Not used for permissions that have ranges (label permissions) or global capabilities.
+ */
+ @AutoValue
+ public abstract static class TestPermission {
+ private static Builder builder() {
+ return new AutoValue_TestProjectUpdate_TestPermission.Builder().force(false);
+ }
+
+ abstract String name();
+
+ abstract String ref();
+
+ abstract AccountGroup.UUID group();
+
+ abstract PermissionRule.Action action();
+
+ abstract boolean force();
+
+ /** Builder for {@link TestPermission}. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ abstract Builder name(String name);
+
+ /** Sets the ref pattern used on the permission. */
+ public abstract Builder ref(String ref);
+
+ /** Sets the group to which the permission applies. */
+ public abstract Builder group(AccountGroup.UUID groupUuid);
+
+ abstract Builder action(PermissionRule.Action action);
+
+ /** Sets whether the permission is a force permission. */
+ public abstract Builder force(boolean force);
+
+ /** Builds the {@link TestPermission}. */
+ public abstract TestPermission build();
+ }
+ }
+
+ /** Starts a builder for allowing a label permission. */
+ public static TestLabelPermission.Builder allowLabel(String name) {
+ return TestLabelPermission.builder().name(name).action(PermissionRule.Action.ALLOW);
+ }
+
+ /** Starts a builder for denying a label permission. */
+ public static TestLabelPermission.Builder blockLabel(String name) {
+ return TestLabelPermission.builder().name(name).action(PermissionRule.Action.BLOCK);
+ }
+
+ /** Records a label permission to be updated. */
+ @AutoValue
+ public abstract static class TestLabelPermission {
+ private static Builder builder() {
+ return new AutoValue_TestProjectUpdate_TestLabelPermission.Builder().impersonation(false);
+ }
+
+ abstract String name();
+
+ abstract String ref();
+
+ abstract AccountGroup.UUID group();
+
+ abstract PermissionRule.Action action();
+
+ abstract int min();
+
+ abstract int max();
+
+ abstract boolean impersonation();
+
+ /** Builder for {@link TestLabelPermission}. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ abstract Builder name(String name);
+
+ /** Sets the ref pattern used on the permission. */
+ public abstract Builder ref(String ref);
+
+ /** Sets the group to which the permission applies. */
+ public abstract Builder group(AccountGroup.UUID group);
+
+ abstract Builder action(PermissionRule.Action action);
+
+ abstract Builder min(int min);
+
+ abstract Builder max(int max);
+
+ /** Sets the minimum and maximum values for the permission. */
+ public Builder range(int min, int max) {
+ checkArgument(min != 0 || max != 0, "empty range");
+ checkNonInvertedRange(min, max);
+ return min(min).max(max);
+ }
+
+ /** Sets whether this permission should be for impersonating another user's votes. */
+ public abstract Builder impersonation(boolean impersonation);
+
+ abstract TestLabelPermission autoBuild();
+
+ /** Builds the {@link TestPermission}. */
+ public TestLabelPermission build() {
+ TestLabelPermission result = autoBuild();
+ checkLabelName(result.name());
+ return result;
+ }
+ }
+ }
+
+ /**
+ * Starts a builder for describing a permission key for deletion. Not for label permissions or
+ * global capabilities.
+ */
+ public static TestPermissionKey.Builder permissionKey(String name) {
+ return TestPermissionKey.builder().name(name);
+ }
+
+ /** Starts a builder for describing a label permission key for deletion. */
+ public static TestPermissionKey.Builder labelPermissionKey(String name) {
+ checkLabelName(name);
+ return TestPermissionKey.builder().name(Permission.forLabel(name));
+ }
+
+ /** Starts a builder for describing a capability key for deletion. */
+ public static TestPermissionKey.Builder capabilityKey(String name) {
+ return TestPermissionKey.builder().name(name).section(GLOBAL_CAPABILITIES);
+ }
+
+ /** Records the key of a permission (of any type) for deletion. */
+ @AutoValue
+ public abstract static class TestPermissionKey {
+ private static Builder builder() {
+ return new AutoValue_TestProjectUpdate_TestPermissionKey.Builder();
+ }
+
+ abstract String section();
+
+ abstract String name();
+
+ abstract Optional<AccountGroup.UUID> group();
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ abstract Builder section(String section);
+
+ abstract Optional<String> section();
+
+ /** Sets the ref pattern used on the permission. Not for global capabilities. */
+ public Builder ref(String ref) {
+ requireNonNull(ref);
+ checkArgument(ref.startsWith(Constants.R_REFS), "must be a ref: %s", ref);
+ checkArgument(
+ !section().isPresent() || !section().get().equals(GLOBAL_CAPABILITIES),
+ "can't set ref on global capability");
+ return section(ref);
+ }
+
+ abstract Builder name(String name);
+
+ /** Sets the group to which the permission applies. */
+ public abstract Builder group(AccountGroup.UUID group);
+
+ /** Builds the {@link TestPermissionKey}. */
+ public abstract TestPermissionKey build();
+ }
+ }
+
+ static Builder builder(
+ Project.NameKey nameKey,
+ AllProjectsName allProjectsName,
+ ThrowingConsumer<TestProjectUpdate> projectUpdater) {
+ return new AutoValue_TestProjectUpdate.Builder()
+ .nameKey(nameKey)
+ .allProjectsName(allProjectsName)
+ .projectUpdater(projectUpdater);
+ }
+
+ /** Builder for {@link TestProjectUpdate}. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ abstract Builder nameKey(Project.NameKey project);
+
+ abstract Builder allProjectsName(AllProjectsName allProjects);
+
+ abstract ImmutableList.Builder<TestPermission> addedPermissionsBuilder();
+
+ abstract ImmutableList.Builder<TestLabelPermission> addedLabelPermissionsBuilder();
+
+ abstract ImmutableList.Builder<TestCapability> addedCapabilitiesBuilder();
+
+ abstract ImmutableList.Builder<TestPermissionKey> removedPermissionsBuilder();
+
+ abstract ImmutableMap.Builder<TestPermissionKey, Boolean> exclusiveGroupPermissionsBuilder();
+
+ /** Adds a permission to be included in this update. */
+ public Builder add(TestPermission testPermission) {
+ addedPermissionsBuilder().add(testPermission);
+ return this;
+ }
+
+ /** Adds a permission to be included in this update. */
+ public Builder add(TestPermission.Builder testPermissionBuilder) {
+ return add(testPermissionBuilder.build());
+ }
+
+ /** Adds a label permission to be included in this update. */
+ public Builder add(TestLabelPermission testLabelPermission) {
+ addedLabelPermissionsBuilder().add(testLabelPermission);
+ return this;
+ }
+
+ /** Adds a label permission to be included in this update. */
+ public Builder add(TestLabelPermission.Builder testLabelPermissionBuilder) {
+ return add(testLabelPermissionBuilder.build());
+ }
+
+ /** Adds a capability to be included in this update. */
+ public Builder add(TestCapability testCapability) {
+ addedCapabilitiesBuilder().add(testCapability);
+ return this;
+ }
+
+ /** Adds a capability to be included in this update. */
+ public Builder add(TestCapability.Builder testCapabilityBuilder) {
+ return add(testCapabilityBuilder.build());
+ }
+
+ /** Removes a permission, label permission, or capability as part of this update. */
+ public Builder remove(TestPermissionKey testPermissionKey) {
+ removedPermissionsBuilder().add(testPermissionKey);
+ return this;
+ }
+
+ /** Removes a permission, label permission, or capability as part of this update. */
+ public Builder remove(TestPermissionKey.Builder testPermissionKeyBuilder) {
+ return remove(testPermissionKeyBuilder.build());
+ }
+
+ /** Sets the exclusive bit bit for the given permission key. */
+ public Builder setExclusiveGroup(
+ TestPermissionKey.Builder testPermissionKeyBuilder, boolean exclusive) {
+ return setExclusiveGroup(testPermissionKeyBuilder.build(), exclusive);
+ }
+
+ /** Sets the exclusive bit bit for the given permission key. */
+ public Builder setExclusiveGroup(TestPermissionKey testPermissionKey, boolean exclusive) {
+ checkArgument(
+ !testPermissionKey.group().isPresent(),
+ "do not specify group for setExclusiveGroup: %s",
+ testPermissionKey);
+ checkArgument(
+ !testPermissionKey.section().equals(GLOBAL_CAPABILITIES),
+ "setExclusiveGroup not valid for global capabilities: %s",
+ testPermissionKey);
+ exclusiveGroupPermissionsBuilder().put(testPermissionKey, exclusive);
+ return this;
+ }
+
+ abstract Builder projectUpdater(ThrowingConsumer<TestProjectUpdate> projectUpdater);
+
+ abstract TestProjectUpdate autoBuild();
+
+ TestProjectUpdate build() {
+ TestProjectUpdate projectUpdate = autoBuild();
+ if (projectUpdate.hasCapabilityUpdates()) {
+ checkArgument(
+ projectUpdate.nameKey().equals(projectUpdate.allProjectsName()),
+ "cannot update global capabilities on %s, only %s: %s",
+ projectUpdate.nameKey(),
+ projectUpdate.allProjectsName(),
+ projectUpdate);
+ }
+ return projectUpdate;
+ }
+
+ /** Executes the update, updating the underlying project. */
+ public void update() {
+ TestProjectUpdate projectUpdate = build();
+ projectUpdate.projectUpdater().acceptAndThrowSilently(projectUpdate);
+ }
+ }
+
+ abstract Project.NameKey nameKey();
+
+ abstract AllProjectsName allProjectsName();
+
+ abstract ImmutableList<TestPermission> addedPermissions();
+
+ abstract ImmutableList<TestLabelPermission> addedLabelPermissions();
+
+ abstract ImmutableList<TestCapability> addedCapabilities();
+
+ abstract ImmutableList<TestPermissionKey> removedPermissions();
+
+ abstract ImmutableMap<TestPermissionKey, Boolean> exclusiveGroupPermissions();
+
+ abstract ThrowingConsumer<TestProjectUpdate> projectUpdater();
+
+ boolean hasCapabilityUpdates() {
+ return !addedCapabilities().isEmpty()
+ || removedPermissions().stream().anyMatch(k -> k.section().equals(GLOBAL_CAPABILITIES));
+ }
+
+ private static void checkLabelName(String name) {
+ // "label-Code-Review" is technically a valid label name, and we don't prevent users from
+ // using it in production, but specifying it in a test is programmer error.
+ checkArgument(!Permission.isLabel(name), "expected label name, got permission name: %s", name);
+ LabelType.checkName(name);
+ }
+
+ private static void checkNonInvertedRange(int min, int max) {
+ checkArgument(min <= max, "inverted range: %s > %s", min, max);
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.java b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.java
index 17d9294583..a9914b3d74 100644
--- a/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.java
+++ b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.java
@@ -16,7 +16,7 @@ package com.google.gerrit.acceptance.testsuite.request;
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.testsuite.account.TestAccount;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
/**
* An aggregation of operations on Guice request scopes for test purposes.
diff --git a/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java
index 554642266a..db730a6c3a 100644
--- a/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java
@@ -24,7 +24,7 @@ import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/common/BUILD b/java/com/google/gerrit/common/BUILD
index 3b3d9c65a2..1099919898 100644
--- a/java/com/google/gerrit/common/BUILD
+++ b/java/com/google/gerrit/common/BUILD
@@ -21,16 +21,15 @@ java_library(
visibility = ["//visibility:public"],
deps = [
":annotations",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/prettify:server",
- "//java/com/google/gerrit/reviewdb:server",
- "//java/com/google/gwtorm",
"//lib:guava",
+ "//lib:jgit",
"//lib:servlet-api",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/flogger:api",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/common/FileUtil.java b/java/com/google/gerrit/common/FileUtil.java
index 04288bc71b..5b0925e70c 100644
--- a/java/com/google/gerrit/common/FileUtil.java
+++ b/java/com/google/gerrit/common/FileUtil.java
@@ -44,7 +44,6 @@ public class FileUtil {
}
public static void chmod(int mode, Path path) {
- // TODO(dborowitz): Is there a portable way to do this with NIO?
chmod(mode, path.toFile());
}
diff --git a/java/com/google/gerrit/common/PageLinks.java b/java/com/google/gerrit/common/PageLinks.java
index 2243eab1bb..38de5b15a1 100644
--- a/java/com/google/gerrit/common/PageLinks.java
+++ b/java/com/google/gerrit/common/PageLinks.java
@@ -14,12 +14,12 @@
package com.google.gerrit.common;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Status;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.client.KeyUtil;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Change.Status;
+import com.google.gerrit.entities.KeyUtil;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
public class PageLinks {
public static final String PROJECT_CHANGE_DELIMITER = "/+/";
@@ -83,7 +83,7 @@ public class PageLinks {
}
public static String toChange(@Nullable Project.NameKey project, PatchSet.Id ps) {
- return toChange(project, ps.getParentKey()) + ps.getId();
+ return toChange(project, ps.changeId()) + ps.getId();
}
public static String toProject(Project.NameKey p) {
diff --git a/java/com/google/gerrit/common/UsedAt.java b/java/com/google/gerrit/common/UsedAt.java
index 44ed92bc47..9f8b2551bd 100644
--- a/java/com/google/gerrit/common/UsedAt.java
+++ b/java/com/google/gerrit/common/UsedAt.java
@@ -14,6 +14,7 @@
package com.google.gerrit.common;
+import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -22,13 +23,13 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- * A marker for a method that is public solely because it is called from inside a project or an
- * organisation using Gerrit.
+ * A marker to say a method/type/field is added or is increased to public solely because it is
+ * called from inside a project or an organisation using Gerrit.
*/
-@Target({METHOD, TYPE})
+@Target({METHOD, TYPE, FIELD})
@Retention(RUNTIME)
public @interface UsedAt {
- /** Enumeration of projects that call a method that would otherwise be private. */
+ /** Enumeration of projects that call a method/type/field. */
enum Project {
GOOGLE,
COLLABNET,
diff --git a/java/com/google/gerrit/common/data/AccessSection.java b/java/com/google/gerrit/common/data/AccessSection.java
index 3670e961a6..0c9663b90c 100644
--- a/java/com/google/gerrit/common/data/AccessSection.java
+++ b/java/com/google/gerrit/common/data/AccessSection.java
@@ -18,7 +18,7 @@ import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
diff --git a/java/com/google/gerrit/common/data/CommentDetail.java b/java/com/google/gerrit/common/data/CommentDetail.java
index 1ae246f94c..d69f0bba50 100644
--- a/java/com/google/gerrit/common/data/CommentDetail.java
+++ b/java/com/google/gerrit/common/data/CommentDetail.java
@@ -14,9 +14,9 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -42,7 +42,7 @@ public class CommentDetail {
protected CommentDetail() {}
public void include(Change.Id changeId, Comment p) {
- PatchSet.Id psId = new PatchSet.Id(changeId, p.key.patchSetId);
+ PatchSet.Id psId = PatchSet.id(changeId, p.key.patchSetId);
if (p.side == 0) {
if (idA == null && idB.equals(psId)) {
a.add(p);
diff --git a/java/com/google/gerrit/common/data/ContributorAgreement.java b/java/com/google/gerrit/common/data/ContributorAgreement.java
index a6e8cdd33f..bc106f0229 100644
--- a/java/com/google/gerrit/common/data/ContributorAgreement.java
+++ b/java/com/google/gerrit/common/data/ContributorAgreement.java
@@ -14,7 +14,7 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import java.util.ArrayList;
import java.util.List;
diff --git a/java/com/google/gerrit/common/data/FilenameComparator.java b/java/com/google/gerrit/common/data/FilenameComparator.java
index e0a6569897..ebf423c583 100644
--- a/java/com/google/gerrit/common/data/FilenameComparator.java
+++ b/java/com/google/gerrit/common/data/FilenameComparator.java
@@ -14,7 +14,7 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.entities.Patch;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
diff --git a/java/com/google/gerrit/common/data/GarbageCollectionResult.java b/java/com/google/gerrit/common/data/GarbageCollectionResult.java
index a6c534c431..5ed01585a4 100644
--- a/java/com/google/gerrit/common/data/GarbageCollectionResult.java
+++ b/java/com/google/gerrit/common/data/GarbageCollectionResult.java
@@ -14,7 +14,7 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import java.util.ArrayList;
import java.util.List;
diff --git a/java/com/google/gerrit/common/data/GroupDescription.java b/java/com/google/gerrit/common/data/GroupDescription.java
index d22b94b09b..ed8b39d0a2 100644
--- a/java/com/google/gerrit/common/data/GroupDescription.java
+++ b/java/com/google/gerrit/common/data/GroupDescription.java
@@ -15,8 +15,8 @@
package com.google.gerrit.common.data;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.sql.Timestamp;
import java.util.Set;
diff --git a/java/com/google/gerrit/common/data/GroupReference.java b/java/com/google/gerrit/common/data/GroupReference.java
index e5b0965293..0af088ede9 100644
--- a/java/com/google/gerrit/common/data/GroupReference.java
+++ b/java/com/google/gerrit/common/data/GroupReference.java
@@ -14,8 +14,10 @@
package com.google.gerrit.common.data;
+import static java.util.Objects.requireNonNull;
+
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
/** Describes a group within a projects {@link AccessSection}s. */
public class GroupReference implements Comparable<GroupReference> {
@@ -46,17 +48,27 @@ public class GroupReference implements Comparable<GroupReference> {
/**
* Create a group reference.
*
- * @param uuid UUID of the group, may be {@code null} if the group name couldn't be resolved
+ * @param uuid UUID of the group, must not be {@code null}
+ * @param name the group name, must not be {@code null}
+ */
+ public GroupReference(AccountGroup.UUID uuid, String name) {
+ setUUID(requireNonNull(uuid));
+ setName(name);
+ }
+
+ /**
+ * Create a group reference where the group's name couldn't be resolved.
+ *
* @param name the group name, must not be {@code null}
*/
- public GroupReference(@Nullable AccountGroup.UUID uuid, String name) {
- setUUID(uuid);
+ public GroupReference(String name) {
+ setUUID(null);
setName(name);
}
@Nullable
public AccountGroup.UUID getUUID() {
- return uuid != null ? new AccountGroup.UUID(uuid) : null;
+ return uuid != null ? AccountGroup.uuid(uuid) : null;
}
public void setUUID(@Nullable AccountGroup.UUID newUUID) {
diff --git a/java/com/google/gerrit/common/data/LabelFunction.java b/java/com/google/gerrit/common/data/LabelFunction.java
index 7d13c7086f..6af675bd35 100644
--- a/java/com/google/gerrit/common/data/LabelFunction.java
+++ b/java/com/google/gerrit/common/data/LabelFunction.java
@@ -15,7 +15,7 @@
package com.google.gerrit.common.data;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.entities.PatchSetApproval;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -98,18 +98,18 @@ public enum LabelFunction {
}
for (PatchSetApproval a : approvals) {
- if (a.getValue() == 0) {
+ if (a.value() == 0) {
continue;
}
if (isBlock && labelType.isMaxNegative(a)) {
- submitRecordLabel.appliedBy = a.getAccountId();
+ submitRecordLabel.appliedBy = a.accountId();
submitRecordLabel.status = SubmitRecord.Label.Status.REJECT;
return submitRecordLabel;
}
if (labelType.isMaxPositive(a) || !requiresMaxValue) {
- submitRecordLabel.appliedBy = a.getAccountId();
+ submitRecordLabel.appliedBy = a.accountId();
submitRecordLabel.status = SubmitRecord.Label.Status.MAY;
if (isRequired) {
diff --git a/java/com/google/gerrit/common/data/LabelType.java b/java/com/google/gerrit/common/data/LabelType.java
index 42945c452d..90b0930386 100644
--- a/java/com/google/gerrit/common/data/LabelType.java
+++ b/java/com/google/gerrit/common/data/LabelType.java
@@ -19,8 +19,8 @@ import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSetApproval;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -34,6 +34,7 @@ public class LabelType {
public static final boolean DEF_COPY_ALL_SCORES_IF_NO_CODE_CHANGE = false;
public static final boolean DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE = false;
public static final boolean DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE = false;
+ public static final boolean DEF_COPY_ANY_SCORE = false;
public static final boolean DEF_COPY_MAX_SCORE = false;
public static final boolean DEF_COPY_MIN_SCORE = false;
public static final boolean DEF_IGNORE_SELF_APPROVAL = false;
@@ -96,6 +97,7 @@ public class LabelType {
protected LabelFunction function;
+ protected boolean copyAnyScore;
protected boolean copyMinScore;
protected boolean copyMaxScore;
protected boolean copyAllScoresOnMergeFirstParentUpdate;
@@ -139,6 +141,7 @@ public class LabelType {
setCopyAllScoresIfNoCodeChange(DEF_COPY_ALL_SCORES_IF_NO_CODE_CHANGE);
setCopyAllScoresOnTrivialRebase(DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE);
setCopyAllScoresOnMergeFirstParentUpdate(DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE);
+ setCopyAnyScore(DEF_COPY_ANY_SCORE);
setCopyMaxScore(DEF_COPY_MAX_SCORE);
setCopyMinScore(DEF_COPY_MIN_SCORE);
setAllowPostSubmit(DEF_ALLOW_POST_SUBMIT);
@@ -155,7 +158,7 @@ public class LabelType {
}
public boolean matches(PatchSetApproval psa) {
- return psa.getLabelId().get().equalsIgnoreCase(name);
+ return psa.labelId().get().equalsIgnoreCase(name);
}
public LabelFunction getFunction() {
@@ -229,6 +232,14 @@ public class LabelType {
this.defaultValue = defaultValue;
}
+ public boolean isCopyAnyScore() {
+ return copyAnyScore;
+ }
+
+ public void setCopyAnyScore(boolean copyAnyScore) {
+ this.copyAnyScore = copyAnyScore;
+ }
+
public boolean isCopyMinScore() {
return copyMinScore;
}
@@ -279,11 +290,11 @@ public class LabelType {
}
public boolean isMaxNegative(PatchSetApproval ca) {
- return maxNegative == ca.getValue();
+ return maxNegative == ca.value();
}
public boolean isMaxPositive(PatchSetApproval ca) {
- return maxPositive == ca.getValue();
+ return maxPositive == ca.value();
}
public LabelValue getValue(short value) {
@@ -291,11 +302,11 @@ public class LabelType {
}
public LabelValue getValue(PatchSetApproval ca) {
- return byValue.get(ca.getValue());
+ return byValue.get(ca.value());
}
public LabelId getLabelId() {
- return new LabelId(name);
+ return LabelId.create(name);
}
@Override
diff --git a/java/com/google/gerrit/common/data/LabelTypes.java b/java/com/google/gerrit/common/data/LabelTypes.java
index d5891d1575..1647658aad 100644
--- a/java/com/google/gerrit/common/data/LabelTypes.java
+++ b/java/com/google/gerrit/common/data/LabelTypes.java
@@ -14,7 +14,7 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.LabelId;
+import com.google.gerrit.entities.LabelId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
diff --git a/java/com/google/gerrit/common/data/PatchScript.java b/java/com/google/gerrit/common/data/PatchScript.java
index 3428580a55..c177e356d6 100644
--- a/java/com/google/gerrit/common/data/PatchScript.java
+++ b/java/com/google/gerrit/common/data/PatchScript.java
@@ -14,12 +14,12 @@
package com.google.gerrit.common.data;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Patch.ChangeType;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.prettify.common.SparseFileContent;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.diff.Edit;
@@ -56,7 +56,6 @@ public class PatchScript {
private CommentDetail comments;
private List<Patch> history;
private boolean hugeFile;
- private boolean intralineDifference;
private boolean intralineFailure;
private boolean intralineTimeout;
private boolean binary;
@@ -83,7 +82,6 @@ public class PatchScript {
CommentDetail cd,
List<Patch> hist,
boolean hf,
- boolean id,
boolean idf,
boolean idt,
boolean bin,
@@ -108,7 +106,6 @@ public class PatchScript {
comments = cd;
history = hist;
hugeFile = hf;
- intralineDifference = id;
intralineFailure = idf;
intralineTimeout = idt;
binary = bin;
@@ -178,10 +175,6 @@ public class PatchScript {
return diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE;
}
- public boolean hasIntralineDifference() {
- return intralineDifference;
- }
-
public boolean hasIntralineFailure() {
return intralineFailure;
}
diff --git a/java/com/google/gerrit/common/data/PermissionRange.java b/java/com/google/gerrit/common/data/PermissionRange.java
index 2f05854ba1..97c37315d1 100644
--- a/java/com/google/gerrit/common/data/PermissionRange.java
+++ b/java/com/google/gerrit/common/data/PermissionRange.java
@@ -17,6 +17,10 @@ package com.google.gerrit.common.data;
import java.util.ArrayList;
import java.util.List;
+/**
+ * Represents a closed interval [min, max] with a name. The special value [0, 0] is understood to be
+ * the empty range.
+ */
public class PermissionRange implements Comparable<PermissionRange> {
public static class WithDefaults extends PermissionRange {
protected int defaultMin;
diff --git a/java/com/google/gerrit/common/data/SubmitRecord.java b/java/com/google/gerrit/common/data/SubmitRecord.java
index 22861b24b1..fe5843ad41 100644
--- a/java/com/google/gerrit/common/data/SubmitRecord.java
+++ b/java/com/google/gerrit/common/data/SubmitRecord.java
@@ -14,7 +14,7 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
diff --git a/java/com/google/gerrit/common/data/SubscribeSection.java b/java/com/google/gerrit/common/data/SubscribeSection.java
index aaf0798428..6ac4695bce 100644
--- a/java/com/google/gerrit/common/data/SubscribeSection.java
+++ b/java/com/google/gerrit/common/data/SubscribeSection.java
@@ -14,8 +14,8 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -60,14 +60,14 @@ public class SubscribeSection {
* @param branch the branch to check
* @return if the branch could trigger a superproject update
*/
- public boolean appliesTo(Branch.NameKey branch) {
+ public boolean appliesTo(BranchNameKey branch) {
for (RefSpec r : matchingRefSpecs) {
- if (r.matchSource(branch.get())) {
+ if (r.matchSource(branch.branch())) {
return true;
}
}
for (RefSpec r : multiMatchRefSpecs) {
- if (r.matchSource(branch.get())) {
+ if (r.matchSource(branch.branch())) {
return true;
}
}
diff --git a/java/com/google/gerrit/common/data/testing/BUILD b/java/com/google/gerrit/common/data/testing/BUILD
index 8ab01dead8..b9ec30b8de 100644
--- a/java/com/google/gerrit/common/data/testing/BUILD
+++ b/java/com/google/gerrit/common/data/testing/BUILD
@@ -7,7 +7,7 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//lib/truth",
],
)
diff --git a/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java b/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
index 265d5901d5..d841aa6389 100644
--- a/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
+++ b/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
@@ -21,9 +21,9 @@ import com.google.common.truth.FailureMetadata;
import com.google.common.truth.StringSubject;
import com.google.common.truth.Subject;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
-public class GroupReferenceSubject extends Subject<GroupReferenceSubject, GroupReference> {
+public class GroupReferenceSubject extends Subject {
public static GroupReferenceSubject assertThat(GroupReference group) {
return assertAbout(groupReferences()).that(group);
@@ -33,19 +33,20 @@ public class GroupReferenceSubject extends Subject<GroupReferenceSubject, GroupR
return GroupReferenceSubject::new;
}
+ private final GroupReference group;
+
private GroupReferenceSubject(FailureMetadata metadata, GroupReference group) {
super(metadata, group);
+ this.group = group;
}
- public ComparableSubject<?, AccountGroup.UUID> groupUuid() {
+ public ComparableSubject<AccountGroup.UUID> groupUuid() {
isNotNull();
- GroupReference group = actual();
- return check("groupUuid()").that(group.getUUID());
+ return check("getUUID()").that(group.getUUID());
}
public StringSubject name() {
isNotNull();
- GroupReference group = actual();
- return check("name()").that(group.getName());
+ return check("getName()").that(group.getName());
}
}
diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 8ed9729ec7..0d1f9cd50a 100644
--- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -32,6 +32,7 @@ import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
import com.google.gerrit.elasticsearch.builders.QueryBuilder;
import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder;
import com.google.gerrit.elasticsearch.bulk.DeleteRequest;
+import com.google.gerrit.entities.converter.ProtoConverter;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.FieldType;
@@ -45,7 +46,6 @@ import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.ResultSet;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.converter.ProtoConverter;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.IndexUtils;
import com.google.gson.Gson;
diff --git a/java/com/google/gerrit/elasticsearch/BUILD b/java/com/google/gerrit/elasticsearch/BUILD
index a9b145bfbc..edbd82c15b 100644
--- a/java/com/google/gerrit/elasticsearch/BUILD
+++ b/java/com/google/gerrit/elasticsearch/BUILD
@@ -6,6 +6,7 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
@@ -13,10 +14,10 @@ java_library(
"//java/com/google/gerrit/index/project",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/proto",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//lib:gson",
"//lib:guava",
+ "//lib:jgit",
"//lib:protobuf",
"//lib/commons:codec",
"//lib/commons:lang",
@@ -29,6 +30,5 @@ java_library(
"//lib/httpcomponents:httpcore",
"//lib/httpcomponents:httpcore-nio",
"//lib/jackson:jackson-core",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
index f646efcb8a..374120e3e4 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
@@ -14,19 +14,17 @@
package com.google.gerrit.elasticsearch;
-import static com.google.gerrit.server.index.account.AccountField.ID;
-
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
import com.google.gerrit.elasticsearch.bulk.BulkRequest;
import com.google.gerrit.elasticsearch.bulk.IndexRequest;
import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.config.SitePaths;
@@ -83,15 +81,23 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
throw new StorageException(
String.format(
"Failed to replace account %s in index %s: %s",
- as.getAccount().getId(), indexName, statusCode));
+ as.account().id(), indexName, statusCode));
}
}
@Override
public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts)
throws QueryParseException {
- JsonArray sortArray = getSortArray(AccountField.ID.getName());
- return new ElasticQuerySource(p, opts.filterFields(IndexUtils::accountFields), type, sortArray);
+ JsonArray sortArray =
+ getSortArray(
+ schema.useLegacyNumericFields()
+ ? AccountField.ID.getName()
+ : AccountField.ID_STR.getName());
+ return new ElasticQuerySource(
+ p,
+ opts.filterFields(o -> IndexUtils.accountFields(o, schema.useLegacyNumericFields())),
+ type,
+ sortArray);
}
@Override
@@ -106,7 +112,7 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
@Override
protected String getId(AccountState as) {
- return as.getAccount().getId().toString();
+ return as.account().id().toString();
}
@Override
@@ -116,7 +122,15 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
source = json.getAsJsonObject().get("fields");
}
- Account.Id id = new Account.Id(source.getAsJsonObject().get(ID.getName()).getAsInt());
+ Account.Id id =
+ Account.id(
+ source
+ .getAsJsonObject()
+ .get(
+ schema.useLegacyNumericFields()
+ ? AccountField.ID.getName()
+ : AccountField.ID_STR.getName())
+ .getAsInt());
// Use the AccountCache rather than depending on any stored fields in the document (of which
// there shouldn't be any). The most expensive part to compute anyway is the effective group
// IDs, and we don't have a good way to reindex when those change.
diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index ca2b1a8203..a65840044e 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -30,18 +30,19 @@ import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
import com.google.gerrit.elasticsearch.bulk.BulkRequest;
import com.google.gerrit.elasticsearch.bulk.IndexRequest;
import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.converter.ChangeProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetProtoConverter;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.converter.ChangeProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetApprovalProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetProtoConverter;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.StarredChangesUtil;
@@ -87,6 +88,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
private final ChangeMapping mapping;
private final ChangeData.Factory changeDataFactory;
private final Schema<ChangeData> schema;
+ private final FieldDef<ChangeData, ?> idField;
@Inject
ElasticChangeIndex(
@@ -98,7 +100,9 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
super(cfg, sitePaths, schema, clientBuilder, CHANGES);
this.changeDataFactory = changeDataFactory;
this.schema = schema;
- mapping = new ChangeMapping(schema, client.adapter());
+ this.mapping = new ChangeMapping(schema, client.adapter());
+ this.idField =
+ this.schema.useLegacyNumericFields() ? ChangeField.LEGACY_ID : ChangeField.LEGACY_ID_STR;
}
@Override
@@ -136,7 +140,8 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
}
}
- QueryOptions filteredOpts = opts.filterFields(IndexUtils::changeFields);
+ QueryOptions filteredOpts =
+ opts.filterFields(o -> IndexUtils.changeFields(o, schema.useLegacyNumericFields()));
return new ElasticQuerySource(p, filteredOpts, getURI(indexes), getSortArray());
}
@@ -146,7 +151,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
JsonArray sortArray = new JsonArray();
addNamedElement(ChangeField.UPDATED.getName(), properties, sortArray);
- addNamedElement(ChangeField.LEGACY_ID.getName(), properties, sortArray);
+ addNamedElement(idField.getName(), properties, sortArray);
return sortArray;
}
@@ -179,10 +184,10 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
JsonElement c = source.get(ChangeField.CHANGE.getName());
if (c == null) {
- int id = source.get(ChangeField.LEGACY_ID.getName()).getAsInt();
+ int id = source.get(idField.getName()).getAsInt();
// IndexUtils#changeFields ensures either CHANGE or PROJECT is always present.
String projectName = requireNonNull(source.get(ChangeField.PROJECT.getName()).getAsString());
- return changeDataFactory.create(new Project.NameKey(projectName), new Change.Id(id));
+ return changeDataFactory.create(Project.nameKey(projectName), Change.id(id));
}
ChangeData cd =
@@ -252,7 +257,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
if (reviewedBy.size() == 1 && aId == ChangeField.NOT_REVIEWED) {
break;
}
- accounts.add(new Account.Id(aId));
+ accounts.add(Account.id(aId));
}
cd.setReviewedBy(accounts);
}
diff --git a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
index eff3d52de9..3922f89616 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
@@ -18,13 +18,13 @@ import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
import com.google.gerrit.elasticsearch.bulk.BulkRequest;
import com.google.gerrit.elasticsearch.bulk.IndexRequest;
import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.group.InternalGroup;
@@ -116,8 +116,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
}
AccountGroup.UUID uuid =
- new AccountGroup.UUID(
- source.getAsJsonObject().get(GroupField.UUID.getName()).getAsString());
+ AccountGroup.uuid(source.getAsJsonObject().get(GroupField.UUID.getName()).getAsString());
// Use the GroupCache rather than depending on any stored fields in the
// document (of which there shouldn't be any).
return groupCache.get().get(uuid).orElse(null);
diff --git a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
index a0ebb07ba3..7e45f4fc23 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
@@ -18,6 +18,7 @@ import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
import com.google.gerrit.elasticsearch.bulk.BulkRequest;
import com.google.gerrit.elasticsearch.bulk.IndexRequest;
import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
@@ -27,7 +28,6 @@ import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.IndexUtils;
import com.google.gerrit.server.project.ProjectCache;
@@ -117,8 +117,7 @@ public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, P
}
Project.NameKey nameKey =
- new Project.NameKey(
- source.getAsJsonObject().get(ProjectField.NAME.getName()).getAsString());
+ Project.nameKey(source.getAsJsonObject().get(ProjectField.NAME.getName()).getAsString());
return projectCache.get().get(nameKey).toProjectData();
}
}
diff --git a/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java b/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
index 394158dbc2..d05e91cbac 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java
@@ -145,7 +145,7 @@ public class ElasticQueryBuilder {
String name = p.getField().getName();
String value = p.getValue();
- if (value.isEmpty()) {
+ if (!p.getField().isRepeatable() && value.isEmpty()) {
return new BoolQueryBuilder().mustNot(QueryBuilders.existsQuery(name));
} else if (p instanceof RegexPredicate) {
if (value.startsWith("^")) {
diff --git a/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java b/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
index a693f6db4c..2f0bd0118e 100644
--- a/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
+++ b/java/com/google/gerrit/elasticsearch/bulk/UpdateRequest.java
@@ -40,11 +40,7 @@ public class UpdateRequest<V> extends BulkRequest {
for (Values<V> values : schema.buildFields(v)) {
String name = values.getField().getName();
if (values.getField().isRepeatable()) {
- builder.field(
- name,
- Streams.stream(values.getValues())
- .filter(e -> shouldAddElement(e))
- .collect(toList()));
+ builder.field(name, Streams.stream(values.getValues()).collect(toList()));
} else {
Object element = Iterables.getOnlyElement(values.getValues(), "");
if (shouldAddElement(element)) {
diff --git a/java/com/google/gerrit/reviewdb/client/Account.java b/java/com/google/gerrit/entities/Account.java
index fa3abedca1..809db1e429 100644
--- a/java/com/google/gerrit/reviewdb/client/Account.java
+++ b/java/com/google/gerrit/entities/Account.java
@@ -12,15 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_DRAFT_COMMENTS;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_STARRED_CHANGES;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS;
+import static com.google.gerrit.entities.RefNames.REFS_DRAFT_COMMENTS;
+import static com.google.gerrit.entities.RefNames.REFS_STARRED_CHANGES;
+import static com.google.gerrit.entities.RefNames.REFS_USERS;
+import com.google.auto.value.AutoValue;
import com.google.common.primitives.Ints;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gwtorm.client.IntKey;
import java.sql.Timestamp;
import java.util.Optional;
@@ -37,44 +38,24 @@ import java.util.Optional;
* <li>ExternalId: OpenID identities and email addresses known to be registered to this user.
* Multiple records can exist when the user has more than one public identity, such as a work
* and a personal email address.
- * <li>{@link AccountGroupMember}: membership of the user in a specific human managed {@link
- * AccountGroup}. Multiple records can exist when the user is a member of more than one group.
* <li>AccountSshKey: user's public SSH keys, for authentication through the internal SSH daemon.
* One record per SSH key uploaded by the user, keys are checked in random order until a match
* is found.
* <li>{@link DiffPreferencesInfo}: user's preferences for rendering side-to-side and unified diff
* </ul>
*/
-public final class Account {
+@AutoValue
+public abstract class Account {
public static Id id(int id) {
- return new Id(id);
+ return new AutoValue_Account_Id(id);
}
/** Key local to Gerrit to identify a user. */
- public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- protected int id;
-
- protected Id() {}
-
- public Id(int id) {
- this.id = id;
- }
-
- @Override
- public int get() {
- return id;
- }
-
- @Override
- protected void set(int newValue) {
- id = newValue;
- }
-
+ @AutoValue
+ public abstract static class Id implements Comparable<Id> {
/** Parse an Account.Id out of a string representation. */
public static Optional<Id> tryParse(String str) {
- return Optional.ofNullable(Ints.tryParse(str)).map(Id::new);
+ return Optional.ofNullable(Ints.tryParse(str)).map(Account::id);
}
public static Id fromRef(String name) {
@@ -99,12 +80,12 @@ public final class Account {
*/
public static Id fromRefPart(String name) {
Integer id = RefNames.parseShardedRefPart(name);
- return id != null ? new Account.Id(id) : null;
+ return id != null ? Account.id(id) : null;
}
public static Id parseAfterShardedRefPart(String name) {
Integer id = RefNames.parseAfterShardedRefPart(name);
- return id != null ? new Account.Id(id) : null;
+ return id != null ? Account.id(id) : null;
}
/**
@@ -119,34 +100,52 @@ public final class Account {
*/
public static Id fromRefSuffix(String name) {
Integer id = RefNames.parseRefSuffix(name);
- return id != null ? new Account.Id(id) : null;
+ return id != null ? Account.id(id) : null;
+ }
+
+ abstract int id();
+
+ public int get() {
+ return id();
+ }
+
+ @Override
+ public final int compareTo(Id o) {
+ return Integer.compare(id(), o.id());
+ }
+
+ @Override
+ public final String toString() {
+ return Integer.toString(get());
}
}
- private Id accountId;
+ public abstract Id id();
/** Date and time the user registered with the review server. */
- private Timestamp registeredOn;
+ public abstract Timestamp registeredOn();
/** Full name of the user ("Given-name Surname" style). */
- private String fullName;
+ @Nullable
+ public abstract String fullName();
/** Email address the user prefers to be contacted through. */
- private String preferredEmail;
+ @Nullable
+ public abstract String preferredEmail();
/**
* Is this user inactive? This is used to avoid showing some users (eg. former employees) in
* auto-suggest.
*/
- private boolean inactive;
+ public abstract boolean inactive();
/** The user-settable status of this account (e.g. busy, OOO, available) */
- private String status;
+ @Nullable
+ public abstract String status();
/** ID of the user branch from which the account was read. */
- private String metaId;
-
- protected Account() {}
+ @Nullable
+ public abstract String metaId();
/**
* Create a new account.
@@ -154,38 +153,11 @@ public final class Account {
* @param newId unique id, see {@link com.google.gerrit.server.notedb.Sequences#nextAccountId()}.
* @param registeredOn when the account was registered.
*/
- public Account(Account.Id newId, Timestamp registeredOn) {
- this.accountId = newId;
- this.registeredOn = registeredOn;
- }
-
- /** Get local id of this account, to link with in other entities */
- public Account.Id getId() {
- return accountId;
- }
-
- /** Get the full name of the user ("Given-name Surname" style). */
- public String getFullName() {
- return fullName;
- }
-
- /** Set the full name of the user ("Given-name Surname" style). */
- public void setFullName(String name) {
- if (name != null && !name.trim().isEmpty()) {
- fullName = name.trim();
- } else {
- fullName = null;
- }
- }
-
- /** Email address the user prefers to be contacted through. */
- public String getPreferredEmail() {
- return preferredEmail;
- }
-
- /** Set the email address the user prefers to be contacted through. */
- public void setPreferredEmail(String addr) {
- preferredEmail = addr;
+ public static Account.Builder builder(Account.Id newId, Timestamp registeredOn) {
+ return new AutoValue_Account.Builder()
+ .setInactive(false)
+ .setId(newId)
+ .setRegisteredOn(registeredOn);
}
/**
@@ -201,13 +173,13 @@ public final class Account {
* generic string containing the accountId.
*/
public String getName() {
- if (fullName != null) {
- return fullName;
+ if (fullName() != null) {
+ return fullName();
}
- if (preferredEmail != null) {
- return preferredEmail;
+ if (preferredEmail() != null) {
+ return preferredEmail();
}
- return getName(accountId);
+ return getName(id());
}
public static String getName(Account.Id accountId) {
@@ -227,62 +199,70 @@ public final class Account {
* </ul>
*/
public String getNameEmail(String anonymousCowardName) {
- String name = fullName != null ? fullName : anonymousCowardName;
+ String name = fullName() != null ? fullName() : anonymousCowardName;
StringBuilder b = new StringBuilder();
b.append(name);
- if (preferredEmail != null) {
+ if (preferredEmail() != null) {
b.append(" <");
- b.append(preferredEmail);
+ b.append(preferredEmail());
b.append(">");
} else {
b.append(" (");
- b.append(accountId.get());
+ b.append(id().get());
b.append(")");
}
return b.toString();
}
- /** Get the date and time the user first registered. */
- public Timestamp getRegisteredOn() {
- return registeredOn;
+ public boolean isActive() {
+ return !inactive();
}
- public String getMetaId() {
- return metaId;
- }
+ public abstract Builder toBuilder();
- public void setMetaId(String metaId) {
- this.metaId = metaId;
- }
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Id id();
- public boolean isActive() {
- return !inactive;
- }
+ abstract Builder setId(Id id);
- public void setActive(boolean active) {
- inactive = !active;
- }
+ public abstract Timestamp registeredOn();
- public String getStatus() {
- return status;
- }
+ abstract Builder setRegisteredOn(Timestamp registeredOn);
- public void setStatus(String status) {
- this.status = status;
- }
+ @Nullable
+ public abstract String fullName();
- @Override
- public boolean equals(Object o) {
- return o instanceof Account && ((Account) o).getId().equals(getId());
- }
+ public abstract Builder setFullName(String fullName);
- @Override
- public int hashCode() {
- return getId().get();
+ @Nullable
+ public abstract String preferredEmail();
+
+ public abstract Builder setPreferredEmail(String preferredEmail);
+
+ public abstract boolean inactive();
+
+ public abstract Builder setInactive(boolean inactive);
+
+ public Builder setActive(boolean active) {
+ return setInactive(!active);
+ }
+
+ @Nullable
+ public abstract String status();
+
+ public abstract Builder setStatus(String status);
+
+ @Nullable
+ public abstract String metaId();
+
+ public abstract Builder setMetaId(@Nullable String metaId);
+
+ public abstract Account build();
}
@Override
- public String toString() {
+ public final String toString() {
return getName();
}
}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroup.java b/java/com/google/gerrit/entities/AccountGroup.java
index 0db7bbd83f..c10edc24a0 100644
--- a/java/com/google/gerrit/reviewdb/client/AccountGroup.java
+++ b/java/com/google/gerrit/entities/AccountGroup.java
@@ -12,11 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
+import com.google.auto.value.AutoValue;
import com.google.gerrit.common.Nullable;
-import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.client.StringKey;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.Objects;
@@ -34,63 +33,45 @@ public final class AccountGroup {
}
public static NameKey nameKey(String n) {
- return new NameKey(n);
+ return new AutoValue_AccountGroup_NameKey(n);
}
/** Group name key */
- public static class NameKey extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
+ @AutoValue
+ public abstract static class NameKey implements Comparable<NameKey> {
+ abstract String name();
- protected String name;
-
- protected NameKey() {}
-
- public NameKey(String n) {
- name = n;
+ public String get() {
+ return name();
}
@Override
- public String get() {
- return name;
+ public final int compareTo(NameKey o) {
+ return name().compareTo(o.name());
}
@Override
- protected void set(String newValue) {
- name = newValue;
+ public final String toString() {
+ return KeyUtil.encode(get());
}
}
public static UUID uuid(String n) {
- return new UUID(n);
+ return new AutoValue_AccountGroup_UUID(n);
}
/** Globally unique identifier. */
- public static class UUID extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- protected String uuid;
-
- protected UUID() {}
-
- public UUID(String n) {
- uuid = n;
- }
+ @AutoValue
+ public abstract static class UUID implements Comparable<UUID> {
+ abstract String uuid();
- @Override
public String get() {
- return uuid;
- }
-
- @Override
- protected void set(String newValue) {
- uuid = newValue;
+ return uuid();
}
/** Parse an {@link AccountGroup.UUID} out of a string representation. */
public static UUID parse(String str) {
- final UUID r = new UUID();
- r.fromString(str);
- return r;
+ return AccountGroup.uuid(KeyUtil.decode(str));
}
/** Parse an {@link AccountGroup.UUID} out of a ref-name. */
@@ -112,7 +93,17 @@ public final class AccountGroup {
*/
public static UUID fromRefPart(String refPart) {
String uuid = RefNames.parseShardedUuidFromRefPart(refPart);
- return uuid != null ? new AccountGroup.UUID(uuid) : null;
+ return uuid != null ? AccountGroup.uuid(uuid) : null;
+ }
+
+ @Override
+ public final int compareTo(UUID o) {
+ return uuid().compareTo(o.uuid());
+ }
+
+ @Override
+ public final String toString() {
+ return KeyUtil.encode(get());
}
}
@@ -122,36 +113,26 @@ public final class AccountGroup {
}
public static Id id(int id) {
- return new Id(id);
+ return new AutoValue_AccountGroup_Id(id);
}
/** Synthetic key to link to within the database */
- public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- protected int id;
-
- protected Id() {}
-
- public Id(int id) {
- this.id = id;
- }
+ @AutoValue
+ public abstract static class Id {
+ abstract int id();
- @Override
public int get() {
- return id;
- }
-
- @Override
- protected void set(int newValue) {
- id = newValue;
+ return id();
}
/** Parse an AccountGroup.Id out of a string representation. */
public static Id parse(String str) {
- final Id r = new Id();
- r.fromString(str);
- return r;
+ return AccountGroup.id(Integer.parseInt(str));
+ }
+
+ @Override
+ public final String toString() {
+ return Integer.toString(get());
}
}
diff --git a/java/com/google/gerrit/entities/AccountGroupByIdAudit.java b/java/com/google/gerrit/entities/AccountGroupByIdAudit.java
new file mode 100644
index 0000000000..17ddf518d6
--- /dev/null
+++ b/java/com/google/gerrit/entities/AccountGroupByIdAudit.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2011 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import com.google.auto.value.AutoValue;
+import java.sql.Timestamp;
+import java.util.Optional;
+
+/** Inclusion of an {@link AccountGroup} in another {@link AccountGroup}. */
+@AutoValue
+public abstract class AccountGroupByIdAudit {
+ public static Builder builder() {
+ return new AutoValue_AccountGroupByIdAudit.Builder();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder groupId(AccountGroup.Id groupId);
+
+ public abstract Builder includeUuid(AccountGroup.UUID includeUuid);
+
+ public abstract Builder addedBy(Account.Id addedBy);
+
+ public abstract Builder addedOn(Timestamp addedOn);
+
+ abstract Builder removedBy(Account.Id removedBy);
+
+ abstract Builder removedOn(Timestamp removedOn);
+
+ public Builder removed(Account.Id removedBy, Timestamp removedOn) {
+ return removedBy(removedBy).removedOn(removedOn);
+ }
+
+ public abstract AccountGroupByIdAudit build();
+ }
+
+ public abstract AccountGroup.Id groupId();
+
+ public abstract AccountGroup.UUID includeUuid();
+
+ public abstract Account.Id addedBy();
+
+ public abstract Timestamp addedOn();
+
+ public abstract Optional<Account.Id> removedBy();
+
+ public abstract Optional<Timestamp> removedOn();
+
+ public abstract Builder toBuilder();
+
+ public boolean isActive() {
+ return !removedOn().isPresent();
+ }
+}
diff --git a/java/com/google/gerrit/entities/AccountGroupMemberAudit.java b/java/com/google/gerrit/entities/AccountGroupMemberAudit.java
new file mode 100644
index 0000000000..4d191b8c36
--- /dev/null
+++ b/java/com/google/gerrit/entities/AccountGroupMemberAudit.java
@@ -0,0 +1,74 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import com.google.auto.value.AutoValue;
+import java.sql.Timestamp;
+import java.util.Optional;
+
+/** Membership of an {@link Account} in an {@link AccountGroup}. */
+@AutoValue
+public abstract class AccountGroupMemberAudit {
+ public static Builder builder() {
+ return new AutoValue_AccountGroupMemberAudit.Builder();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder groupId(AccountGroup.Id groupId);
+
+ public abstract Builder memberId(Account.Id accountId);
+
+ public abstract Builder addedBy(Account.Id addedBy);
+
+ abstract Account.Id addedBy();
+
+ public abstract Builder addedOn(Timestamp addedOn);
+
+ abstract Timestamp addedOn();
+
+ abstract Builder removedBy(Account.Id removedBy);
+
+ abstract Builder removedOn(Timestamp removedOn);
+
+ public Builder removed(Account.Id removedBy, Timestamp removedOn) {
+ return removedBy(removedBy).removedOn(removedOn);
+ }
+
+ public Builder removedLegacy() {
+ return removed(addedBy(), addedOn());
+ }
+
+ public abstract AccountGroupMemberAudit build();
+ }
+
+ public abstract AccountGroup.Id groupId();
+
+ public abstract Account.Id memberId();
+
+ public abstract Account.Id addedBy();
+
+ public abstract Timestamp addedOn();
+
+ public abstract Optional<Account.Id> removedBy();
+
+ public abstract Optional<Timestamp> removedOn();
+
+ public abstract Builder toBuilder();
+
+ public boolean isActive() {
+ return !removedOn().isPresent();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/BUILD b/java/com/google/gerrit/entities/BUILD
index 3bc6528d5d..8784bd8f30 100644
--- a/java/com/google/gerrit/reviewdb/BUILD
+++ b/java/com/google/gerrit/entities/BUILD
@@ -5,14 +5,17 @@ package(
)
java_library(
- name = "server",
+ name = "entities",
srcs = glob(["**/*.java"]),
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gwtorm",
"//lib:guava",
+ "//lib:jgit",
"//lib:protobuf",
+ "//lib/auto:auto-value",
+ "//lib/auto:auto-value-annotations",
+ "//lib/errorprone:annotations",
"//proto:entities_java_proto",
],
)
diff --git a/java/com/google/gerrit/reviewdb/client/BooleanProjectConfig.java b/java/com/google/gerrit/entities/BooleanProjectConfig.java
index a70d254607..5201f6dc26 100644
--- a/java/com/google/gerrit/reviewdb/client/BooleanProjectConfig.java
+++ b/java/com/google/gerrit/entities/BooleanProjectConfig.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
/**
* Contains all inheritable boolean project configs and maps internal representations to API
diff --git a/java/com/google/gerrit/entities/BranchNameKey.java b/java/com/google/gerrit/entities/BranchNameKey.java
new file mode 100644
index 0000000000..cbb2e25adb
--- /dev/null
+++ b/java/com/google/gerrit/entities/BranchNameKey.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import com.google.auto.value.AutoValue;
+
+/** Branch name key */
+@AutoValue
+public abstract class BranchNameKey implements Comparable<BranchNameKey> {
+ public static BranchNameKey create(Project.NameKey projectName, String branchName) {
+ return new AutoValue_BranchNameKey(projectName, RefNames.fullName(branchName));
+ }
+
+ public static BranchNameKey create(String projectName, String branchName) {
+ return create(Project.nameKey(projectName), branchName);
+ }
+
+ public abstract Project.NameKey project();
+
+ public abstract String branch();
+
+ public String shortName() {
+ return RefNames.shortName(branch());
+ }
+
+ @Override
+ public final int compareTo(BranchNameKey o) {
+ // TODO(dborowitz): Only compares branch name in order to match old StringKey behavior.
+ // Consider comparing project name first.
+ return branch().compareTo(o.branch());
+ }
+
+ @Override
+ public final String toString() {
+ return project() + "," + KeyUtil.encode(branch());
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/client/Change.java b/java/com/google/gerrit/entities/Change.java
index 79739dc948..739bd38097 100644
--- a/java/com/google/gerrit/reviewdb/client/Change.java
+++ b/java/com/google/gerrit/entities/Change.java
@@ -12,19 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.entities.RefNames.REFS_CHANGES;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.auto.value.AutoValue;
+import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.ChangeStatus;
-import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.client.StringKey;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.sql.Timestamp;
import java.util.Arrays;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
/**
- * A change proposed to be merged into a {@link Branch}.
+ * A change proposed to be merged into a branch.
*
* <p>The data graph rooted below a Change can be quite complex:
*
@@ -37,7 +44,7 @@ import java.util.Arrays;
* |
* +- {@link PatchSetApproval}: a +/- vote on the change's current state.
* |
- * +- {@link PatchLineComment}: comment about a specific line
+ * +- {@link Comment}: comment about a specific line
* </pre>
*
* <p>
@@ -53,8 +60,8 @@ import java.util.Arrays;
* commit can contain zero patches, if the merge has no conflicts, or has no impact other than to
* cut off a line of development.
*
- * <p>Each PatchLineComment is a draft or a published comment about a single line of the associated
- * file. These are the inline comment entities created by users as they perform a review.
+ * <p>Each Comment is a draft or a published comment about a single line of the associated file.
+ * These are the inline comment entities created by users as they perform a review.
*
* <p>When additional PatchSets appear under a change, these PatchSets reference <i>replacement</i>
* commits; alternative commits that could be made to the project instead of the original commit
@@ -69,10 +76,10 @@ import java.util.Arrays;
* <h5>ChangeMessage</h5>
*
* <p>The ChangeMessage entity is a general free-form comment about the whole change, rather than
- * PatchLineComment's file and line specific context. The ChangeMessage appears at the start of any
- * email generated by Gerrit, and is shown on the change overview page, rather than in a
- * file-specific context. Users often use this entity to describe general remarks about the overall
- * concept proposed by the change.
+ * Comment's file and line specific context. The ChangeMessage appears at the start of any email
+ * generated by Gerrit, and is shown on the change overview page, rather than in a file-specific
+ * context. Users often use this entity to describe general remarks about the overall concept
+ * proposed by the change.
*
* <p>
*
@@ -93,49 +100,27 @@ import java.util.Arrays;
* notice of a replacement patch set is sent, or when notice of the change submission occurs.
*/
public final class Change {
- public static Id id(int id) {
- return new Id(id);
- }
-
- public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- public int id;
-
- protected Id() {}
-
- public Id(int id) {
- this.id = id;
- }
-
- @Override
- public int get() {
- return id;
- }
-
- @Override
- protected void set(int newValue) {
- id = newValue;
- }
+ private static final SecureRandom rng;
- public String toRefPrefix() {
- return refPrefixBuilder().toString();
+ static {
+ try {
+ rng = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Cannot create RNG for Change-Id generator", e);
}
+ }
- StringBuilder refPrefixBuilder() {
- StringBuilder r = new StringBuilder(32).append(REFS_CHANGES);
- int m = id % 100;
- if (m < 10) {
- r.append('0');
- }
- return r.append(m).append('/').append(id).append('/');
- }
+ public static Id id(int id) {
+ return new AutoValue_Change_Id(id);
+ }
+ @AutoValue
+ public abstract static class Id {
/** Parse a Change.Id out of a string representation. */
public static Id parse(String str) {
- final Id r = new Id();
- r.fromString(str);
- return r;
+ Integer id = Ints.tryParse(str);
+ checkArgument(id != null, "invalid change ID: %s", str);
+ return Change.id(id);
}
public static Id fromRef(String ref) {
@@ -150,7 +135,7 @@ public final class Change {
if (ref.substring(ce).equals(RefNames.META_SUFFIX)
|| ref.substring(ce).equals(RefNames.ROBOT_COMMENTS_SUFFIX)
|| PatchSet.Id.fromRef(ref, ce) >= 0) {
- return new Change.Id(Integer.parseInt(ref.substring(cs, ce)));
+ return Change.id(Integer.parseInt(ref.substring(cs, ce)));
}
return null;
}
@@ -173,7 +158,7 @@ public final class Change {
}
int ce = nextNonDigit(ref, cs);
if (ce < ref.length() && ref.charAt(ce) == '/' && isNumeric(ref, ce + 1)) {
- return new Change.Id(Integer.parseInt(ref.substring(cs, ce)));
+ return Change.id(Integer.parseInt(ref.substring(cs, ce)));
}
return null;
}
@@ -195,14 +180,14 @@ public final class Change {
int endChangeId = nextNonDigit(ref, startChangeId);
String id = ref.substring(startChangeId, endChangeId);
if (id != null && !id.isEmpty()) {
- return new Change.Id(Integer.parseInt(id));
+ return Change.id(Integer.parseInt(id));
}
return null;
}
public static Id fromRefPart(String ref) {
Integer id = RefNames.parseShardedRefPart(ref);
- return id != null ? new Change.Id(id) : null;
+ return id != null ? Change.id(id) : null;
}
static int startIndex(String ref) {
@@ -253,35 +238,66 @@ public final class Change {
}
return i;
}
+
+ abstract int id();
+
+ public int get() {
+ return id();
+ }
+
+ public String toRefPrefix() {
+ return refPrefixBuilder().toString();
+ }
+
+ StringBuilder refPrefixBuilder() {
+ StringBuilder r = new StringBuilder(32).append(REFS_CHANGES);
+ int m = get() % 100;
+ if (m < 10) {
+ r.append('0');
+ }
+ return r.append(m).append('/').append(get()).append('/');
+ }
+
+ @Override
+ public final String toString() {
+ return Integer.toString(get());
+ }
+ }
+
+ public static ObjectId generateChangeId() {
+ byte[] rand = new byte[Constants.OBJECT_ID_STRING_LENGTH];
+ rng.nextBytes(rand);
+ String randomString = new String(rand, UTF_8);
+
+ try (ObjectInserter f = new ObjectInserter.Formatter()) {
+ return f.idFor(Constants.OBJ_COMMIT, Constants.encode(randomString));
+ }
+ }
+
+ public static Key generateKey() {
+ return key("I" + generateChangeId().name());
}
public static Key key(String key) {
- return new Key(key);
+ return new AutoValue_Change_Key(key);
}
/**
* Globally unique identification of this change. This generally takes the form of a string
* "Ixxxxxx...", and is stored in the Change-Id footer of a commit.
*/
- public static class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- protected String id;
-
- protected Key() {}
-
- public Key(String id) {
- this.id = id;
+ @AutoValue
+ public abstract static class Key {
+ // TODO(dborowitz): This hardly seems worth it: why would someone pass a URL-encoded change key?
+ // Ideally the standard key() factory method would enforce the format and throw IAE.
+ public static Key parse(String str) {
+ return Change.key(KeyUtil.decode(str));
}
- @Override
- public String get() {
- return id;
- }
+ abstract String key();
- @Override
- protected void set(String newValue) {
- id = newValue;
+ public String get() {
+ return key();
}
/** Construct a key that is after all keys prefixed by this key. */
@@ -289,7 +305,7 @@ public final class Change {
final StringBuilder revEnd = new StringBuilder(get().length() + 1);
revEnd.append(get());
revEnd.append('\u9fa5');
- return new Key(revEnd.toString());
+ return Change.key(revEnd.toString());
}
/** Obtain a shorter version of this key string, using a leading prefix. */
@@ -298,11 +314,9 @@ public final class Change {
return s.substring(0, Math.min(s.length(), 9));
}
- /** Parse a Change.Key out of a string representation. */
- public static Key parse(String str) {
- final Key r = new Key();
- r.fromString(str);
- return r;
+ @Override
+ public final String toString() {
+ return get();
}
}
@@ -412,13 +426,6 @@ public final class Change {
}
}
- // TODO(davido): Remove in 3.0, after all sites upgraded to version,
- // where DRAFT status was removed. This code path is still needed,
- // when changes are deserialized from the secondary index, during
- // the online migration to the new schema version wasn't completed.
- if (c == 'd') {
- return Status.NEW;
- }
return null;
}
@@ -456,7 +463,7 @@ public final class Change {
protected Account.Id owner;
/** The branch (and project) this change merges into. */
- protected Branch.NameKey dest;
+ protected BranchNameKey dest;
// DELETED: id = 9 (open)
@@ -512,7 +519,7 @@ public final class Change {
Change.Key newKey,
Change.Id newId,
Account.Id ownedBy,
- Branch.NameKey forBranch,
+ BranchNameKey forBranch,
Timestamp ts) {
changeKey = newKey;
changeId = newId;
@@ -599,16 +606,16 @@ public final class Change {
this.owner = owner;
}
- public Branch.NameKey getDest() {
+ public BranchNameKey getDest() {
return dest;
}
- public void setDest(Branch.NameKey dest) {
+ public void setDest(BranchNameKey dest) {
this.dest = dest;
}
public Project.NameKey getProject() {
- return dest.getParentKey();
+ return dest.project();
}
public String getSubject() {
@@ -626,7 +633,7 @@ public final class Change {
/** Get the id of the most current {@link PatchSet} in this change. */
public PatchSet.Id currentPatchSetId() {
if (currentPatchSetId > 0) {
- return new PatchSet.Id(changeId, currentPatchSetId);
+ return PatchSet.id(changeId, currentPatchSetId);
}
return null;
}
@@ -649,7 +656,7 @@ public final class Change {
}
public void setCurrentPatchSet(PatchSet.Id psId, String subject, String originalSubject) {
- if (!psId.getParentKey().equals(changeId)) {
+ if (!psId.changeId().equals(changeId)) {
throw new IllegalArgumentException("patch set ID " + psId + " is not for change " + changeId);
}
currentPatchSetId = psId.get();
diff --git a/java/com/google/gerrit/reviewdb/client/ChangeMessage.java b/java/com/google/gerrit/entities/ChangeMessage.java
index de318bd271..f34cc7d850 100644
--- a/java/com/google/gerrit/reviewdb/client/ChangeMessage.java
+++ b/java/com/google/gerrit/entities/ChangeMessage.java
@@ -12,57 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
+import com.google.auto.value.AutoValue;
import com.google.gerrit.common.Nullable;
-import com.google.gwtorm.client.StringKey;
import java.sql.Timestamp;
import java.util.Objects;
/** A message attached to a {@link Change}. */
public final class ChangeMessage {
public static Key key(Change.Id changeId, String uuid) {
- return new Key(changeId, uuid);
+ return new AutoValue_ChangeMessage_Key(changeId, uuid);
}
- public static class Key extends StringKey<Change.Id> {
- private static final long serialVersionUID = 1L;
+ @AutoValue
+ public abstract static class Key {
+ public abstract Change.Id changeId();
- protected Change.Id changeId;
-
- protected String uuid;
-
- protected Key() {
- changeId = new Change.Id();
- }
-
- public Key(Change.Id change, String uuid) {
- this.changeId = change;
- this.uuid = uuid;
- }
-
- @Override
- public Change.Id getParentKey() {
- return changeId;
- }
-
- public Change.Id changeId() {
- return getParentKey();
- }
-
- @Override
- public String get() {
- return uuid;
- }
-
- public String uuid() {
- return get();
- }
-
- @Override
- public void set(String newValue) {
- uuid = newValue;
- }
+ public abstract String uuid();
}
protected Key key;
diff --git a/java/com/google/gerrit/reviewdb/client/CodedEnum.java b/java/com/google/gerrit/entities/CodedEnum.java
index 11e7efaad4..90629a0543 100644
--- a/java/com/google/gerrit/reviewdb/client/CodedEnum.java
+++ b/java/com/google/gerrit/entities/CodedEnum.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
/** Extension of Enum which provides distinct character code values. */
public interface CodedEnum {
diff --git a/java/com/google/gerrit/reviewdb/client/Comment.java b/java/com/google/gerrit/entities/Comment.java
index e03d0fa24e..55d739a8b2 100644
--- a/java/com/google/gerrit/reviewdb/client/Comment.java
+++ b/java/com/google/gerrit/entities/Comment.java
@@ -12,11 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.gerrit.common.Nullable;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.Objects;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
/**
* This class represents inline comments in NoteDb. This means it determines the JSON format for
@@ -24,30 +29,33 @@ import java.util.Objects;
*
* <p>Changing fields in this class changes the storage format of inline comments in NoteDb and may
* require a corresponding data migration (adding new optional fields is generally okay).
- *
- * <p>{@link PatchLineComment} historically represented comments in ReviewDb. There are a few
- * notable differences:
- *
- * <ul>
- * <li>PatchLineComment knows the comment status (published or draft). For comments in NoteDb the
- * status is determined by the branch in which they are stored (published comments are stored
- * in the change meta ref; draft comments are store in refs/draft-comments branches in
- * All-Users). Hence Comment doesn't need to contain the status, but the status is implicitly
- * known by where the comments are read from.
- * <li>PatchLineComment knows the change ID. For comments in NoteDb, the change ID is determined
- * by the branch in which they are stored (the ref name contains the change ID). Hence Comment
- * doesn't need to contain the change ID, but the change ID is implicitly known by where the
- * comments are read from.
- * </ul>
- *
- * <p>For all utility classes and middle layer functionality using Comment over PatchLineComment is
- * preferred, as ReviewDb is gone so PatchLineComment is slated for deletion as well. This means
- * Comment should be used everywhere and only for storing inline comment in ReviewDb a conversion to
- * PatchLineComment is done. Converting Comments to PatchLineComments and vice verse is done by
- * CommentsUtil#toPatchLineComments(Change.Id, PatchLineComment.Status, Iterable) and
- * CommentsUtil#toComments(String, Iterable).
*/
public class Comment {
+ public enum Status {
+ DRAFT('d'),
+
+ PUBLISHED('P');
+
+ private final char code;
+
+ Status(char c) {
+ code = c;
+ }
+
+ public char getCode() {
+ return code;
+ }
+
+ public static Status forCode(char c) {
+ for (Status s : Status.values()) {
+ if (s.code == c) {
+ return s;
+ }
+ }
+ return null;
+ }
+ }
+
public static class Key {
public String uuid;
public String filename;
@@ -65,17 +73,10 @@ public class Comment {
@Override
public String toString() {
- return new StringBuilder()
- .append("Comment.Key{")
- .append("uuid=")
- .append(uuid)
- .append(',')
- .append("filename=")
- .append(filename)
- .append(',')
- .append("patchSetId=")
- .append(patchSetId)
- .append('}')
+ return MoreObjects.toStringHelper(this)
+ .add("uuid", uuid)
+ .add("filename", filename)
+ .add("patchSetId", patchSetId)
.toString();
}
@@ -104,7 +105,7 @@ public class Comment {
}
public Account.Id getId() {
- return new Account.Id(id);
+ return Account.id(id);
}
@Override
@@ -122,15 +123,30 @@ public class Comment {
@Override
public String toString() {
- return new StringBuilder()
- .append("Comment.Identity{")
- .append("id=")
- .append(id)
- .append('}')
- .toString();
+ return MoreObjects.toStringHelper(this).add("id", id).toString();
}
}
+ /**
+ * The Range class defines continuous range of character.
+ *
+ * <p>The pair (startLine, startChar) defines the first character in the range. The pair (endLine,
+ * endChar) defines the first character AFTER the range (i.e. it doesn't belong the range).
+ * (endLine, endChar) must be a valid character inside text, except EOF case.
+ *
+ * <p>Special cases:
+ *
+ * <ul>
+ * <li>Zero length range: (startLine, startChar) = (endLine, endChar). Range defines insert
+ * position right before the (startLine, startChar) character (for {@link FixReplacement})
+ * <li>EOF case - range includes the last character in the file:
+ * <ul>
+ * <li>if a file ends with EOL mark, then (endLine, endChar) = (num_of_lines + 1, 0)
+ * <li>if a file doesn't end with EOL mark, then (endLine, endChar) = (num_of_lines,
+ * num_of_chars_in_last_line)
+ * </ul>
+ * </ul>
+ */
public static class Range implements Comparable<Range> {
private static final Comparator<Range> RANGE_COMPARATOR =
Comparator.<Range>comparingInt(range -> range.startLine)
@@ -138,10 +154,10 @@ public class Comment {
.thenComparingInt(range -> range.endLine)
.thenComparingInt(range -> range.endChar);
- public int startLine; // 1-based, inclusive
- public int startChar; // 0-based, inclusive
- public int endLine; // 1-based, exclusive
- public int endChar; // 0-based, exclusive
+ public int startLine; // 1-based
+ public int startChar; // 0-based
+ public int endLine; // 1-based
+ public int endChar; // 0-based
public Range(Range r) {
this(r.startLine, r.startChar, r.endLine, r.endChar);
@@ -177,20 +193,11 @@ public class Comment {
@Override
public String toString() {
- return new StringBuilder()
- .append("Comment.Range{")
- .append("startLine=")
- .append(startLine)
- .append(',')
- .append("startChar=")
- .append(startChar)
- .append(',')
- .append("endLine=")
- .append(endLine)
- .append(',')
- .append("endChar=")
- .append(endChar)
- .append('}')
+ return MoreObjects.toStringHelper(this)
+ .add("startLine", startLine)
+ .add("startChar", startChar)
+ .add("endLine", endLine)
+ .add("endChar", endChar)
.toString();
}
@@ -201,7 +208,9 @@ public class Comment {
}
public Key key;
+ /** The line number (1-based) to which the comment refers, or 0 for a file comment. */
public int lineNbr;
+
public Identity author;
protected Identity realAuthor;
public Timestamp writtenOn;
@@ -211,17 +220,15 @@ public class Comment {
public Range range;
public String tag;
- // Hex commit SHA1 of the commit of the patchset to which this comment applies.
- public String revId;
+ // Hex commit SHA1 of the commit of the patchset to which this comment applies. Other classes call
+ // this "commitId", but this class uses the old ReviewDb term "revId", and this field name is
+ // serialized into JSON in NoteDb, so it can't easily be changed. Callers do not access this field
+ // directly, and instead use the public getter/setter that wraps an ObjectId.
+ private String revId;
+
public String serverId;
public boolean unresolved;
- /**
- * Whether the comment was parsed from a JSON representation (false) or the legacy custom notes
- * format (true).
- */
- public transient boolean legacyFormat;
-
public Comment(Comment c) {
this(
new Key(c.key),
@@ -269,8 +276,13 @@ public class Comment {
this.range = range != null ? range.asCommentRange() : null;
}
- public void setRevId(RevId revId) {
- this.revId = revId != null ? revId.get() : null;
+ @Nullable
+ public ObjectId getCommitId() {
+ return revId != null ? ObjectId.fromString(revId) : null;
+ }
+
+ public void setCommitId(@Nullable AnyObjectId commitId) {
+ this.revId = commitId != null ? commitId.name() : null;
}
public void setRealAuthor(Account.Id id) {
@@ -322,44 +334,22 @@ public class Comment {
@Override
public String toString() {
- return new StringBuilder()
- .append("Comment{")
- .append("key=")
- .append(key)
- .append(',')
- .append("lineNbr=")
- .append(lineNbr)
- .append(',')
- .append("author=")
- .append(author.getId().get())
- .append(',')
- .append("realAuthor=")
- .append(realAuthor != null ? realAuthor.getId().get() : "")
- .append(',')
- .append("writtenOn=")
- .append(writtenOn.toString())
- .append(',')
- .append("side=")
- .append(side)
- .append(',')
- .append("message=")
- .append(Objects.toString(message, ""))
- .append(',')
- .append("parentUuid=")
- .append(Objects.toString(parentUuid, ""))
- .append(',')
- .append("range=")
- .append(Objects.toString(range, ""))
- .append(',')
- .append("revId=")
- .append(revId != null ? revId : "")
- .append(',')
- .append("tag=")
- .append(Objects.toString(tag, ""))
- .append(',')
- .append("unresolved=")
- .append(unresolved)
- .append('}')
- .toString();
+ return toStringHelper().toString();
+ }
+
+ protected ToStringHelper toStringHelper() {
+ return MoreObjects.toStringHelper(this)
+ .add("key", key)
+ .add("lineNbr", lineNbr)
+ .add("author", author.getId())
+ .add("realAuthor", realAuthor != null ? realAuthor.getId() : "")
+ .add("writtenOn", writtenOn)
+ .add("side", side)
+ .add("message", Objects.toString(message, ""))
+ .add("parentUuid", Objects.toString(parentUuid, ""))
+ .add("range", Objects.toString(range, ""))
+ .add("revId", Objects.toString(revId, ""))
+ .add("tag", Objects.toString(tag, ""))
+ .add("unresolved", unresolved);
}
}
diff --git a/java/com/google/gerrit/reviewdb/client/CommentRange.java b/java/com/google/gerrit/entities/CommentRange.java
index e6c5078218..f58780fd3b 100644
--- a/java/com/google/gerrit/reviewdb/client/CommentRange.java
+++ b/java/com/google/gerrit/entities/CommentRange.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
public class CommentRange {
diff --git a/java/com/google/gerrit/reviewdb/client/CoreDownloadSchemes.java b/java/com/google/gerrit/entities/CoreDownloadSchemes.java
index 2ca89c8bb4..37c10f1a49 100644
--- a/java/com/google/gerrit/reviewdb/client/CoreDownloadSchemes.java
+++ b/java/com/google/gerrit/entities/CoreDownloadSchemes.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
/** Download scheme string constants supported by the download-commands core plugin. */
public class CoreDownloadSchemes {
diff --git a/java/com/google/gerrit/reviewdb/client/FixReplacement.java b/java/com/google/gerrit/entities/FixReplacement.java
index 66630e43c3..046300e5a6 100644
--- a/java/com/google/gerrit/reviewdb/client/FixReplacement.java
+++ b/java/com/google/gerrit/entities/FixReplacement.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
public class FixReplacement {
public String path;
diff --git a/java/com/google/gerrit/reviewdb/client/FixSuggestion.java b/java/com/google/gerrit/entities/FixSuggestion.java
index d766a3a659..ac4e720fc9 100644
--- a/java/com/google/gerrit/reviewdb/client/FixSuggestion.java
+++ b/java/com/google/gerrit/entities/FixSuggestion.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import java.util.List;
diff --git a/java/com/google/gwtorm/client/StandardKeyEncoder.java b/java/com/google/gerrit/entities/KeyUtil.java
index 1ceb0dcecd..40fb757efc 100644
--- a/java/com/google/gwtorm/client/StandardKeyEncoder.java
+++ b/java/com/google/gerrit/entities/KeyUtil.java
@@ -1,4 +1,4 @@
-// Copyright 2008 Google Inc.
+// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,13 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gwtorm.client;
+package com.google.gerrit.entities;
-import com.google.gwtorm.client.KeyUtil.Encoder;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
-public class StandardKeyEncoder extends Encoder {
+public class KeyUtil {
private static final char[] hexc = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
@@ -49,8 +48,7 @@ public class StandardKeyEncoder extends Encoder {
for (char i = 'a'; i <= 'f'; i++) hexb[i] = (byte) ((i - 'a') + 10);
}
- @Override
- public String encode(final String e) {
+ public static String encode(final String e) {
final byte[] b;
try {
b = e.getBytes("UTF-8");
@@ -73,8 +71,7 @@ public class StandardKeyEncoder extends Encoder {
return r.toString();
}
- @Override
- public String decode(final String e) {
+ public static String decode(final String e) {
if (e.indexOf('%') < 0) {
return e.replace('+', ' ');
}
diff --git a/java/com/google/gerrit/reviewdb/client/LabelId.java b/java/com/google/gerrit/entities/LabelId.java
index abf131b09c..1cc45c8c42 100644
--- a/java/com/google/gerrit/reviewdb/client/LabelId.java
+++ b/java/com/google/gerrit/entities/LabelId.java
@@ -12,38 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
-import com.google.gwtorm.client.StringKey;
-
-public class LabelId extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
+import com.google.auto.value.AutoValue;
+@AutoValue
+public abstract class LabelId {
static final String LEGACY_SUBMIT_NAME = "SUBM";
public static LabelId create(String n) {
- return new LabelId(n);
+ return new AutoValue_LabelId(n);
}
public static LabelId legacySubmit() {
- return new LabelId(LEGACY_SUBMIT_NAME);
+ return create(LEGACY_SUBMIT_NAME);
}
- public String id;
-
- public LabelId() {}
-
- public LabelId(String n) {
- id = n;
- }
+ abstract String id();
- @Override
public String get() {
- return id;
- }
-
- @Override
- protected void set(String newValue) {
- id = newValue;
+ return id();
}
}
diff --git a/java/com/google/gerrit/reviewdb/client/Patch.java b/java/com/google/gerrit/entities/Patch.java
index d192df0d47..cc38bdad2e 100644
--- a/java/com/google/gerrit/reviewdb/client/Patch.java
+++ b/java/com/google/gerrit/entities/Patch.java
@@ -12,9 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
-import com.google.gwtorm.client.StringKey;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Splitter;
+import com.google.common.primitives.Ints;
+import java.util.List;
/** A single modified file in a {@link PatchSet}. */
public final class Patch {
@@ -36,58 +41,29 @@ public final class Patch {
}
public static Key key(PatchSet.Id patchSetId, String fileName) {
- return new Key(patchSetId, fileName);
+ return new AutoValue_Patch_Key(patchSetId, fileName);
}
- public static class Key extends StringKey<PatchSet.Id> {
- private static final long serialVersionUID = 1L;
-
- protected PatchSet.Id patchSetId;
-
- protected String fileName;
-
- protected Key() {
- patchSetId = new PatchSet.Id();
- }
-
- public Key(PatchSet.Id ps, String name) {
- this.patchSetId = ps;
- this.fileName = name;
- }
-
- @Override
- public PatchSet.Id getParentKey() {
- return patchSetId;
- }
-
- public PatchSet.Id patchSetId() {
- return getParentKey();
- }
-
- @Override
- public String get() {
- return fileName;
- }
-
- public String fileName() {
- return get();
+ @AutoValue
+ public abstract static class Key {
+ /** Parse a Patch.Key out of a string representation. */
+ public static Key parse(String str) {
+ List<String> parts = Splitter.on(',').limit(3).splitToList(str);
+ checkKeyFormat(parts.size() == 3, str);
+ Integer changeId = Ints.tryParse(parts.get(0));
+ checkKeyFormat(changeId != null, str);
+ Integer patchSetNum = Ints.tryParse(parts.get(1));
+ checkKeyFormat(patchSetNum != null, str);
+ return key(PatchSet.id(Change.id(changeId), patchSetNum), parts.get(2));
}
- @Override
- protected void set(String newValue) {
- fileName = newValue;
+ private static void checkKeyFormat(boolean test, String input) {
+ checkArgument(test, "invalid patch key: %s", input);
}
- /** Parse a Patch.Id out of a string representation. */
- public static Key parse(String str) {
- final Key r = new Key();
- r.fromString(str);
- return r;
- }
+ public abstract PatchSet.Id patchSetId();
- public String getFileName() {
- return get();
- }
+ public abstract String fileName();
}
/** Type of modification made to the file path. */
@@ -271,7 +247,7 @@ public final class Patch {
}
public String getFileName() {
- return key.fileName;
+ return key.fileName();
}
public String getSourceFileName() {
diff --git a/java/com/google/gerrit/entities/PatchSet.java b/java/com/google/gerrit/entities/PatchSet.java
new file mode 100644
index 0000000000..8b93dbc92f
--- /dev/null
+++ b/java/com/google/gerrit/entities/PatchSet.java
@@ -0,0 +1,233 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.util.Objects.requireNonNull;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
+import com.google.common.primitives.Ints;
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jgit.lib.ObjectId;
+
+/** A single revision of a {@link Change}. */
+@AutoValue
+public abstract class PatchSet {
+ /** Is the reference name a change reference? */
+ public static boolean isChangeRef(String name) {
+ return Id.fromRef(name) != null;
+ }
+
+ /**
+ * Is the reference name a change reference?
+ *
+ * @deprecated use isChangeRef instead.
+ */
+ @Deprecated
+ public static boolean isRef(String name) {
+ return isChangeRef(name);
+ }
+
+ public static String joinGroups(List<String> groups) {
+ requireNonNull(groups);
+ for (String group : groups) {
+ checkArgument(!group.contains(","), "group may not contain ',': %s", group);
+ }
+ return String.join(",", groups);
+ }
+
+ public static ImmutableList<String> splitGroups(String joinedGroups) {
+ return Streams.stream(Splitter.on(',').split(joinedGroups)).collect(toImmutableList());
+ }
+
+ public static Id id(Change.Id changeId, int id) {
+ return new AutoValue_PatchSet_Id(changeId, id);
+ }
+
+ @AutoValue
+ public abstract static class Id {
+ /** Parse a PatchSet.Id out of a string representation. */
+ public static Id parse(String str) {
+ List<String> parts = Splitter.on(',').splitToList(str);
+ checkIdFormat(parts.size() == 2, str);
+ Integer changeId = Ints.tryParse(parts.get(0));
+ checkIdFormat(changeId != null, str);
+ Integer id = Ints.tryParse(parts.get(1));
+ checkIdFormat(id != null, str);
+ return PatchSet.id(Change.id(changeId), id);
+ }
+
+ private static void checkIdFormat(boolean test, String input) {
+ checkArgument(test, "invalid patch set ID: %s", input);
+ }
+
+ /** Parse a PatchSet.Id from a {@link #refName()} result. */
+ public static Id fromRef(String ref) {
+ int cs = Change.Id.startIndex(ref);
+ if (cs < 0) {
+ return null;
+ }
+ int ce = Change.Id.nextNonDigit(ref, cs);
+ int patchSetId = fromRef(ref, ce);
+ if (patchSetId < 0) {
+ return null;
+ }
+ int changeId = Integer.parseInt(ref.substring(cs, ce));
+ return PatchSet.id(Change.id(changeId), patchSetId);
+ }
+
+ static int fromRef(String ref, int changeIdEnd) {
+ // Patch set ID.
+ int ps = changeIdEnd + 1;
+ if (ps >= ref.length() || ref.charAt(ps) == '0') {
+ return -1;
+ }
+ for (int i = ps; i < ref.length(); i++) {
+ if (ref.charAt(i) < '0' || ref.charAt(i) > '9') {
+ return -1;
+ }
+ }
+ return Integer.parseInt(ref.substring(ps));
+ }
+
+ public static String toId(int number) {
+ return number == 0 ? "edit" : String.valueOf(number);
+ }
+
+ public String getId() {
+ return toId(id());
+ }
+
+ public abstract Change.Id changeId();
+
+ abstract int id();
+
+ public int get() {
+ return id();
+ }
+
+ public String toRefName() {
+ return changeId().refPrefixBuilder().append(id()).toString();
+ }
+
+ @Override
+ public final String toString() {
+ return changeId().toString() + ',' + id();
+ }
+ }
+
+ public static Builder builder() {
+ return new AutoValue_PatchSet.Builder().groups(ImmutableList.of());
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder id(Id id);
+
+ public abstract Id id();
+
+ public abstract Builder commitId(ObjectId commitId);
+
+ public abstract Optional<ObjectId> commitId();
+
+ public abstract Builder uploader(Account.Id uploader);
+
+ public abstract Builder createdOn(Timestamp createdOn);
+
+ public abstract Builder groups(Iterable<String> groups);
+
+ public abstract ImmutableList<String> groups();
+
+ public abstract Builder pushCertificate(Optional<String> pushCertificate);
+
+ public abstract Builder pushCertificate(String pushCertificate);
+
+ public abstract Builder description(Optional<String> description);
+
+ public abstract Builder description(String description);
+
+ public abstract Optional<String> description();
+
+ public abstract PatchSet build();
+ }
+
+ /** ID of the patch set. */
+ public abstract Id id();
+
+ /**
+ * Commit ID of the patch set, also known as the revision.
+ *
+ * <p>If this is a deserialized instance that was originally serialized by an older version of
+ * Gerrit, and the old data erroneously did not include a {@code commitId}, then this method will
+ * return {@link ObjectId#zeroId()}.
+ */
+ public abstract ObjectId commitId();
+
+ /**
+ * Account that uploaded the patch set.
+ *
+ * <p>If this is a deserialized instance that was originally serialized by an older version of
+ * Gerrit, and the old data erroneously did not include an {@code uploader}, then this method will
+ * return an account ID of 0.
+ */
+ public abstract Account.Id uploader();
+
+ /**
+ * When this patch set was first introduced onto the change.
+ *
+ * <p>If this is a deserialized instance that was originally serialized by an older version of
+ * Gerrit, and the old data erroneously did not include a {@code createdOn}, then this method will
+ * return a timestamp of 0.
+ */
+ public abstract Timestamp createdOn();
+
+ /**
+ * Opaque group identifier, usually assigned during creation.
+ *
+ * <p>This field is actually a comma-separated list of values, as in rare cases involving merge
+ * commits a patch set may belong to multiple groups.
+ *
+ * <p>Changes on the same branch having patch sets with intersecting groups are considered
+ * related, as in the "Related Changes" tab.
+ */
+ public abstract ImmutableList<String> groups();
+
+ /** Certificate sent with a push that created this patch set. */
+ public abstract Optional<String> pushCertificate();
+
+ /**
+ * Optional user-supplied description for this patch set.
+ *
+ * <p>When this field is an empty {@code Optional}, the description was never set on the patch
+ * set. When this field is present but an empty string, the description was set and later cleared.
+ */
+ public abstract Optional<String> description();
+
+ /** Patch set number. */
+ public int number() {
+ return id().get();
+ }
+
+ /** Name of the corresponding patch set ref. */
+ public String refName() {
+ return id().toRefName();
+ }
+}
diff --git a/java/com/google/gerrit/entities/PatchSetApproval.java b/java/com/google/gerrit/entities/PatchSetApproval.java
new file mode 100644
index 0000000000..a4bb2518a0
--- /dev/null
+++ b/java/com/google/gerrit/entities/PatchSetApproval.java
@@ -0,0 +1,139 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.primitives.Shorts;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.Optional;
+
+/** An approval (or negative approval) on a patch set. */
+@AutoValue
+public abstract class PatchSetApproval {
+ public static Key key(PatchSet.Id patchSetId, Account.Id accountId, LabelId labelId) {
+ return new AutoValue_PatchSetApproval_Key(patchSetId, accountId, labelId);
+ }
+
+ @AutoValue
+ public abstract static class Key {
+ public abstract PatchSet.Id patchSetId();
+
+ public abstract Account.Id accountId();
+
+ public abstract LabelId labelId();
+
+ public boolean isLegacySubmit() {
+ return LabelId.LEGACY_SUBMIT_NAME.equals(labelId().get());
+ }
+ }
+
+ public static Builder builder() {
+ return new AutoValue_PatchSetApproval.Builder().postSubmit(false);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder key(Key key);
+
+ public abstract Key key();
+
+ public abstract Builder value(short value);
+
+ public Builder value(int value) {
+ return value(Shorts.checkedCast(value));
+ }
+
+ public abstract Builder granted(Timestamp granted);
+
+ public Builder granted(Date granted) {
+ return granted(new Timestamp(granted.getTime()));
+ }
+
+ public abstract Builder tag(String tag);
+
+ public abstract Builder tag(Optional<String> tag);
+
+ public abstract Builder realAccountId(Account.Id realAccountId);
+
+ abstract Optional<Account.Id> realAccountId();
+
+ public abstract Builder postSubmit(boolean isPostSubmit);
+
+ abstract PatchSetApproval autoBuild();
+
+ public PatchSetApproval build() {
+ if (!realAccountId().isPresent()) {
+ realAccountId(key().accountId());
+ }
+ return autoBuild();
+ }
+ }
+
+ public abstract Key key();
+
+ /**
+ * Value assigned by the user.
+ *
+ * <p>The precise meaning of "value" is up to each category.
+ *
+ * <p>In general:
+ *
+ * <ul>
+ * <li><b>&lt; 0:</b> The approval is rejected/revoked.
+ * <li><b>= 0:</b> No indication either way is provided.
+ * <li><b>&gt; 0:</b> The approval is approved/positive.
+ * </ul>
+ *
+ * and in the negative and positive direction a magnitude can be assumed.The further from 0 the
+ * more assertive the approval.
+ */
+ public abstract short value();
+
+ public abstract Timestamp granted();
+
+ public abstract Optional<String> tag();
+
+ /** Real user that made this approval on behalf of the user recorded in {@link Key#accountId}. */
+ public abstract Account.Id realAccountId();
+
+ public abstract boolean postSubmit();
+
+ public abstract Builder toBuilder();
+
+ public PatchSetApproval copyWithPatchSet(PatchSet.Id psId) {
+ return toBuilder().key(key(psId, key().accountId(), key().labelId())).build();
+ }
+
+ public PatchSet.Id patchSetId() {
+ return key().patchSetId();
+ }
+
+ public Account.Id accountId() {
+ return key().accountId();
+ }
+
+ public LabelId labelId() {
+ return key().labelId();
+ }
+
+ public String label() {
+ return labelId().get();
+ }
+
+ public boolean isLegacySubmit() {
+ return key().isLegacySubmit();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/client/PatchSetInfo.java b/java/com/google/gerrit/entities/PatchSetInfo.java
index f949013be6..e3c661380f 100644
--- a/java/com/google/gerrit/reviewdb/client/PatchSetInfo.java
+++ b/java/com/google/gerrit/entities/PatchSetInfo.java
@@ -12,19 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
+
+import static java.util.Objects.requireNonNull;
import java.util.List;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
/** Additional data about a {@link PatchSet} not normally loaded. */
public final class PatchSetInfo {
public static class ParentInfo {
- public RevId id;
+ public ObjectId commitId;
public String shortMessage;
- public ParentInfo(RevId id, String shortMessage) {
- this.id = id;
- this.shortMessage = shortMessage;
+ public ParentInfo(AnyObjectId commitId, String shortMessage) {
+ this.commitId = commitId.copy();
+ this.shortMessage = requireNonNull(shortMessage);
}
protected ParentInfo() {}
@@ -47,8 +51,8 @@ public final class PatchSetInfo {
/** List of parents of the patch set. */
protected List<ParentInfo> parents;
- /** SHA-1 of commit */
- protected String revId;
+ /** ID of commit. */
+ protected ObjectId commitId;
/** Optional user-supplied description for the patch set. */
protected String description;
@@ -107,12 +111,12 @@ public final class PatchSetInfo {
return parents;
}
- public void setRevId(String s) {
- revId = s;
+ public void setCommitId(AnyObjectId commitId) {
+ this.commitId = commitId.copy();
}
- public String getRevId() {
- return revId;
+ public ObjectId getCommitId() {
+ return commitId;
}
public void setDescription(String description) {
diff --git a/java/com/google/gerrit/reviewdb/client/Project.java b/java/com/google/gerrit/entities/Project.java
index e025c0f875..867b14db79 100644
--- a/java/com/google/gerrit/reviewdb/client/Project.java
+++ b/java/com/google/gerrit/entities/Project.java
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
+
+import static java.util.Objects.requireNonNull;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gwtorm.client.StringKey;
+import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -35,50 +37,60 @@ public final class Project {
return new NameKey(name);
}
- /** Project name key */
- public static class NameKey extends StringKey<com.google.gwtorm.client.Key<?>> {
+ /**
+ * Project name key.
+ *
+ * <p>This class has subclasses such as {@code AllProjectsName}, which make Guice injection more
+ * convenient. Subclasses must compare equal if they have the same name, regardless of the
+ * specific class. This implies that subclasses may not add additional fields.
+ *
+ * <p>Because of this unusual subclassing behavior, this class is not an {@code @AutoValue},
+ * unlike other key types in this package. However, this is strictly an implementation detail; its
+ * interface and semantics are otherwise analogous to the {@code @AutoValue} types.
+ */
+ public static class NameKey implements Serializable, Comparable<NameKey> {
private static final long serialVersionUID = 1L;
- protected String name;
+ /** Parse a Project.NameKey out of a string representation. */
+ public static NameKey parse(String str) {
+ return nameKey(KeyUtil.decode(str));
+ }
+
+ public static String asStringOrNull(NameKey key) {
+ return key == null ? null : key.get();
+ }
- protected NameKey() {}
+ private final String name;
- public NameKey(String n) {
- name = n;
+ protected NameKey(String name) {
+ this.name = requireNonNull(name);
}
- @Override
public String get() {
return name;
}
@Override
- protected void set(String newValue) {
- name = newValue;
- }
-
- @Override
- public int hashCode() {
+ public final int hashCode() {
return get().hashCode();
}
@Override
- public boolean equals(Object b) {
+ public final boolean equals(Object b) {
if (b instanceof NameKey) {
return get().equals(((NameKey) b).get());
}
return false;
}
- /** Parse a Project.NameKey out of a string representation. */
- public static NameKey parse(String str) {
- final NameKey r = new NameKey();
- r.fromString(str);
- return r;
+ @Override
+ public final int compareTo(NameKey o) {
+ return get().compareTo(o.get());
}
- public static String asStringOrNull(NameKey key) {
- return key == null ? null : key.get();
+ @Override
+ public final String toString() {
+ return KeyUtil.encode(get());
}
}
@@ -220,7 +232,7 @@ public final class Project {
}
public void setParentName(String n) {
- parent = n != null ? new NameKey(n) : null;
+ parent = n != null ? nameKey(n) : null;
}
public void setParentName(NameKey n) {
diff --git a/java/com/google/gerrit/reviewdb/client/RefNames.java b/java/com/google/gerrit/entities/RefNames.java
index 1f119218c8..400861c8a3 100644
--- a/java/com/google/gerrit/reviewdb/client/RefNames.java
+++ b/java/com/google/gerrit/entities/RefNames.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import com.google.gerrit.common.UsedAt;
@@ -123,6 +123,16 @@ public class RefNames {
return shard(id.get(), r).append(META_SUFFIX).toString();
}
+ public static String patchSetRef(PatchSet.Id id) {
+ StringBuilder r = newStringBuilder().append(REFS_CHANGES);
+ return shard(id.changeId().get(), r).append('/').append(id.get()).toString();
+ }
+
+ public static String changeRefPrefix(Change.Id id) {
+ StringBuilder r = newStringBuilder().append(REFS_CHANGES);
+ return shard(id.get(), r).append('/').toString();
+ }
+
public static String robotCommentsRef(Change.Id id) {
StringBuilder r = newStringBuilder().append(REFS_CHANGES);
return shard(id.get(), r).append(ROBOT_COMMENTS_SUFFIX).toString();
diff --git a/java/com/google/gerrit/entities/RobotComment.java b/java/com/google/gerrit/entities/RobotComment.java
new file mode 100644
index 0000000000..a7951add84
--- /dev/null
+++ b/java/com/google/gerrit/entities/RobotComment.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class RobotComment extends Comment {
+ public String robotId;
+ public String robotRunId;
+ public String url;
+ public Map<String, String> properties;
+ public List<FixSuggestion> fixSuggestions;
+
+ public RobotComment(
+ Key key,
+ Account.Id author,
+ Timestamp writtenOn,
+ short side,
+ String message,
+ String serverId,
+ String robotId,
+ String robotRunId) {
+ super(key, author, writtenOn, side, message, serverId, false);
+ this.robotId = robotId;
+ this.robotRunId = robotRunId;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper()
+ .add("robotId", robotId)
+ .add("robotRunId", robotRunId)
+ .add("url", url)
+ .add("properties", Objects.toString(properties, ""))
+ .add("fixSuggestions", Objects.toString(fixSuggestions, ""))
+ .toString();
+ }
+}
diff --git a/java/com/google/gerrit/entities/SubmoduleSubscription.java b/java/com/google/gerrit/entities/SubmoduleSubscription.java
new file mode 100644
index 0000000000..5ea1b1e355
--- /dev/null
+++ b/java/com/google/gerrit/entities/SubmoduleSubscription.java
@@ -0,0 +1,80 @@
+// Copyright (C) 2011 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import java.util.Objects;
+
+/**
+ * Defining a project/branch subscription to a project/branch project.
+ *
+ * <p>This means a class instance represents a repo/branch subscription to a project/branch (the
+ * subscriber).
+ *
+ * <p>A subscriber operates a submodule in defined path.
+ */
+public final class SubmoduleSubscription {
+ protected BranchNameKey superProject;
+
+ protected String submodulePath;
+
+ protected BranchNameKey submodule;
+
+ public SubmoduleSubscription(BranchNameKey superProject, BranchNameKey submodule, String path) {
+ this.superProject = superProject;
+ this.submodule = submodule;
+ this.submodulePath = path;
+ }
+
+ /**
+ * Indicates the super project, aka subscriber: the project owner of the gitlinks to the
+ * submodules.
+ */
+ public BranchNameKey getSuperProject() {
+ return superProject;
+ }
+
+ public String getPath() {
+ return submodulePath;
+ }
+
+ public BranchNameKey getSubmodule() {
+ return submodule;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof SubmoduleSubscription) {
+ SubmoduleSubscription s = (SubmoduleSubscription) o;
+ return superProject.equals(s.superProject)
+ && submodulePath.equals(s.submodulePath)
+ && submodule.equals(s.submodule);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(superProject, submodulePath, submodule);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getSuperProject()).append(':').append(getPath());
+ sb.append(" follows ");
+ sb.append(getSubmodule());
+ return sb.toString();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/client/UserIdentity.java b/java/com/google/gerrit/entities/UserIdentity.java
index 0b7aee3846..e07d21aac8 100644
--- a/java/com/google/gerrit/reviewdb/client/UserIdentity.java
+++ b/java/com/google/gerrit/entities/UserIdentity.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import java.sql.Timestamp;
diff --git a/java/com/google/gerrit/reviewdb/converter/AccountIdProtoConverter.java b/java/com/google/gerrit/entities/converter/AccountIdProtoConverter.java
index 8dd179467b..1e846fbca0 100644
--- a/java/com/google/gerrit/reviewdb/converter/AccountIdProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/AccountIdProtoConverter.java
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.protobuf.Parser;
+@Immutable
public enum AccountIdProtoConverter implements ProtoConverter<Entities.Account_Id, Account.Id> {
INSTANCE;
@@ -28,7 +30,7 @@ public enum AccountIdProtoConverter implements ProtoConverter<Entities.Account_I
@Override
public Account.Id fromProto(Entities.Account_Id proto) {
- return new Account.Id(proto.getId());
+ return Account.id(proto.getId());
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverter.java b/java/com/google/gerrit/entities/converter/BranchNameKeyProtoConverter.java
index 4558f9bae9..4d0e306956 100644
--- a/java/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/BranchNameKeyProtoConverter.java
@@ -12,32 +12,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.protobuf.Parser;
+@Immutable
public enum BranchNameKeyProtoConverter
- implements ProtoConverter<Entities.Branch_NameKey, Branch.NameKey> {
+ implements ProtoConverter<Entities.Branch_NameKey, BranchNameKey> {
INSTANCE;
private final ProtoConverter<Entities.Project_NameKey, Project.NameKey> projectNameConverter =
ProjectNameKeyProtoConverter.INSTANCE;
@Override
- public Entities.Branch_NameKey toProto(Branch.NameKey nameKey) {
+ public Entities.Branch_NameKey toProto(BranchNameKey nameKey) {
return Entities.Branch_NameKey.newBuilder()
- .setProjectName(projectNameConverter.toProto(nameKey.getParentKey()))
- .setBranchName(nameKey.get())
+ .setProject(projectNameConverter.toProto(nameKey.project()))
+ .setBranch(nameKey.branch())
.build();
}
@Override
- public Branch.NameKey fromProto(Entities.Branch_NameKey proto) {
- return new Branch.NameKey(
- projectNameConverter.fromProto(proto.getProjectName()), proto.getBranchName());
+ public BranchNameKey fromProto(Entities.Branch_NameKey proto) {
+ return BranchNameKey.create(
+ projectNameConverter.fromProto(proto.getProject()), proto.getBranch());
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverter.java b/java/com/google/gerrit/entities/converter/ChangeIdProtoConverter.java
index 14ed59c43b..0d4ec70570 100644
--- a/java/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/ChangeIdProtoConverter.java
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.protobuf.Parser;
+@Immutable
public enum ChangeIdProtoConverter implements ProtoConverter<Entities.Change_Id, Change.Id> {
INSTANCE;
@@ -28,7 +30,7 @@ public enum ChangeIdProtoConverter implements ProtoConverter<Entities.Change_Id,
@Override
public Change.Id fromProto(Entities.Change_Id proto) {
- return new Change.Id(proto.getId());
+ return Change.id(proto.getId());
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverter.java b/java/com/google/gerrit/entities/converter/ChangeKeyProtoConverter.java
index dccd7d9d1a..f3ccdfaf3d 100644
--- a/java/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/ChangeKeyProtoConverter.java
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.protobuf.Parser;
+@Immutable
public enum ChangeKeyProtoConverter implements ProtoConverter<Entities.Change_Key, Change.Key> {
INSTANCE;
@@ -28,7 +30,7 @@ public enum ChangeKeyProtoConverter implements ProtoConverter<Entities.Change_Ke
@Override
public Change.Key fromProto(Entities.Change_Key proto) {
- return new Change.Key(proto.getId());
+ return Change.key(proto.getId());
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverter.java b/java/com/google/gerrit/entities/converter/ChangeMessageKeyProtoConverter.java
index bb532dfaa4..3e93c5a2fe 100644
--- a/java/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/ChangeMessageKeyProtoConverter.java
@@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.protobuf.Parser;
+@Immutable
public enum ChangeMessageKeyProtoConverter
implements ProtoConverter<Entities.ChangeMessage_Key, ChangeMessage.Key> {
INSTANCE;
@@ -29,14 +31,14 @@ public enum ChangeMessageKeyProtoConverter
@Override
public Entities.ChangeMessage_Key toProto(ChangeMessage.Key messageKey) {
return Entities.ChangeMessage_Key.newBuilder()
- .setChangeId(changeIdConverter.toProto(messageKey.getParentKey()))
- .setUuid(messageKey.get())
+ .setChangeId(changeIdConverter.toProto(messageKey.changeId()))
+ .setUuid(messageKey.uuid())
.build();
}
@Override
public ChangeMessage.Key fromProto(Entities.ChangeMessage_Key proto) {
- return new ChangeMessage.Key(changeIdConverter.fromProto(proto.getChangeId()), proto.getUuid());
+ return ChangeMessage.key(changeIdConverter.fromProto(proto.getChangeId()), proto.getUuid());
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverter.java b/java/com/google/gerrit/entities/converter/ChangeMessageProtoConverter.java
index 0895d8d89b..19c1212499 100644
--- a/java/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/ChangeMessageProtoConverter.java
@@ -12,16 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.protobuf.Parser;
import java.sql.Timestamp;
import java.util.Objects;
+@Immutable
public enum ChangeMessageProtoConverter
implements ProtoConverter<Entities.ChangeMessage, ChangeMessage> {
INSTANCE;
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeProtoConverter.java b/java/com/google/gerrit/entities/converter/ChangeProtoConverter.java
index 30b6d27b43..5b066ea87c 100644
--- a/java/com/google/gerrit/reviewdb/converter/ChangeProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/ChangeProtoConverter.java
@@ -12,16 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.protobuf.Parser;
import java.sql.Timestamp;
+@Immutable
public enum ChangeProtoConverter implements ProtoConverter<Entities.Change, Change> {
INSTANCE;
@@ -31,7 +33,7 @@ public enum ChangeProtoConverter implements ProtoConverter<Entities.Change, Chan
ChangeKeyProtoConverter.INSTANCE;
private final ProtoConverter<Entities.Account_Id, Account.Id> accountIdConverter =
AccountIdProtoConverter.INSTANCE;
- private final ProtoConverter<Entities.Branch_NameKey, Branch.NameKey> branchNameConverter =
+ private final ProtoConverter<Entities.Branch_NameKey, BranchNameKey> branchNameConverter =
BranchNameKeyProtoConverter.INSTANCE;
@Override
@@ -86,7 +88,7 @@ public enum ChangeProtoConverter implements ProtoConverter<Entities.Change, Chan
proto.hasChangeKey() ? changeKeyConverter.fromProto(proto.getChangeKey()) : null;
Account.Id owner =
proto.hasOwnerAccountId() ? accountIdConverter.fromProto(proto.getOwnerAccountId()) : null;
- Branch.NameKey destination =
+ BranchNameKey destination =
proto.hasDest() ? branchNameConverter.fromProto(proto.getDest()) : null;
Change change =
new Change(key, changeId, owner, destination, new Timestamp(proto.getCreatedOn()));
@@ -100,7 +102,7 @@ public enum ChangeProtoConverter implements ProtoConverter<Entities.Change, Chan
String subject = proto.hasSubject() ? proto.getSubject() : null;
String originalSubject = proto.hasOriginalSubject() ? proto.getOriginalSubject() : null;
change.setCurrentPatchSet(
- new PatchSet.Id(changeId, proto.getCurrentPatchSetId()), subject, originalSubject);
+ PatchSet.id(changeId, proto.getCurrentPatchSetId()), subject, originalSubject);
if (proto.hasTopic()) {
change.setTopic(proto.getTopic());
}
diff --git a/java/com/google/gerrit/reviewdb/converter/LabelIdProtoConverter.java b/java/com/google/gerrit/entities/converter/LabelIdProtoConverter.java
index 274f23b26c..a1894ac0aa 100644
--- a/java/com/google/gerrit/reviewdb/converter/LabelIdProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/LabelIdProtoConverter.java
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.LabelId;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.LabelId;
import com.google.protobuf.Parser;
+@Immutable
public enum LabelIdProtoConverter implements ProtoConverter<Entities.LabelId, LabelId> {
INSTANCE;
@@ -28,7 +30,7 @@ public enum LabelIdProtoConverter implements ProtoConverter<Entities.LabelId, La
@Override
public LabelId fromProto(Entities.LabelId proto) {
- return new LabelId(proto.getId());
+ return LabelId.create(proto.getId());
}
@Override
diff --git a/java/com/google/gerrit/entities/converter/ObjectIdProtoConverter.java b/java/com/google/gerrit/entities/converter/ObjectIdProtoConverter.java
new file mode 100644
index 0000000000..6c805282b3
--- /dev/null
+++ b/java/com/google/gerrit/entities/converter/ObjectIdProtoConverter.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities.converter;
+
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.proto.Entities;
+import com.google.protobuf.Parser;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Proto converter for {@code ObjectId}s.
+ *
+ * <p>This converter uses the hex representation of object IDs embedded in a wrapper proto type,
+ * rather than a more parsimonious implementation (e.g. a raw byte array), for two reasons:
+ *
+ * <ul>
+ * <li>Hex strings are easier to read and work with when reading and writing protos in text
+ * formats, for example in test failure messages, or when using command-line tools.
+ * <li>This maintains backwards wire compatibility with a pre-NoteDb implementation.
+ * </ul>
+ */
+@Immutable
+public enum ObjectIdProtoConverter implements ProtoConverter<Entities.ObjectId, ObjectId> {
+ INSTANCE;
+
+ @Override
+ public Entities.ObjectId toProto(ObjectId objectId) {
+ return Entities.ObjectId.newBuilder().setName(objectId.name()).build();
+ }
+
+ @Override
+ public ObjectId fromProto(Entities.ObjectId proto) {
+ return ObjectId.fromString(proto.getName());
+ }
+
+ @Override
+ public Parser<Entities.ObjectId> getParser() {
+ return Entities.ObjectId.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverter.java b/java/com/google/gerrit/entities/converter/PatchSetApprovalKeyProtoConverter.java
index 353830195d..c7d1714192 100644
--- a/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/PatchSetApprovalKeyProtoConverter.java
@@ -12,15 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.protobuf.Parser;
+@Immutable
public enum PatchSetApprovalKeyProtoConverter
implements ProtoConverter<Entities.PatchSetApproval_Key, PatchSetApproval.Key> {
INSTANCE;
@@ -35,18 +37,18 @@ public enum PatchSetApprovalKeyProtoConverter
@Override
public Entities.PatchSetApproval_Key toProto(PatchSetApproval.Key key) {
return Entities.PatchSetApproval_Key.newBuilder()
- .setPatchSetId(patchSetIdConverter.toProto(key.getParentKey()))
- .setAccountId(accountIdConverter.toProto(key.getAccountId()))
- .setCategoryId(labelIdConverter.toProto(key.getLabelId()))
+ .setPatchSetId(patchSetIdConverter.toProto(key.patchSetId()))
+ .setAccountId(accountIdConverter.toProto(key.accountId()))
+ .setLabelId(labelIdConverter.toProto(key.labelId()))
.build();
}
@Override
public PatchSetApproval.Key fromProto(Entities.PatchSetApproval_Key proto) {
- return new PatchSetApproval.Key(
+ return PatchSetApproval.key(
patchSetIdConverter.fromProto(proto.getPatchSetId()),
accountIdConverter.fromProto(proto.getAccountId()),
- labelIdConverter.fromProto(proto.getCategoryId()));
+ labelIdConverter.fromProto(proto.getLabelId()));
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverter.java b/java/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverter.java
index 418076f47f..78a35ff57a 100644
--- a/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverter.java
@@ -12,15 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.protobuf.Parser;
import java.sql.Timestamp;
import java.util.Objects;
+@Immutable
public enum PatchSetApprovalProtoConverter
implements ProtoConverter<Entities.PatchSetApproval, PatchSetApproval> {
INSTANCE;
@@ -34,21 +36,18 @@ public enum PatchSetApprovalProtoConverter
public Entities.PatchSetApproval toProto(PatchSetApproval patchSetApproval) {
Entities.PatchSetApproval.Builder builder =
Entities.PatchSetApproval.newBuilder()
- .setKey(patchSetApprovalKeyProtoConverter.toProto(patchSetApproval.getKey()))
- .setValue(patchSetApproval.getValue())
- .setGranted(patchSetApproval.getGranted().getTime())
- .setPostSubmit(patchSetApproval.isPostSubmit());
+ .setKey(patchSetApprovalKeyProtoConverter.toProto(patchSetApproval.key()))
+ .setValue(patchSetApproval.value())
+ .setGranted(patchSetApproval.granted().getTime())
+ .setPostSubmit(patchSetApproval.postSubmit());
- String tag = patchSetApproval.getTag();
- if (tag != null) {
- builder.setTag(tag);
- }
- Account.Id realAccountId = patchSetApproval.getRealAccountId();
+ patchSetApproval.tag().ifPresent(builder::setTag);
+ Account.Id realAccountId = patchSetApproval.realAccountId();
// PatchSetApproval#getRealAccountId automatically delegates to PatchSetApproval#getAccountId if
// the real author is not set. However, the previous protobuf representation kept
// 'realAccountId' empty if it wasn't set. To ensure binary compatibility, simulate the previous
// behavior.
- if (realAccountId != null && !Objects.equals(realAccountId, patchSetApproval.getAccountId())) {
+ if (realAccountId != null && !Objects.equals(realAccountId, patchSetApproval.accountId())) {
builder.setRealAccountId(accountIdConverter.toProto(realAccountId));
}
@@ -57,21 +56,19 @@ public enum PatchSetApprovalProtoConverter
@Override
public PatchSetApproval fromProto(Entities.PatchSetApproval proto) {
- PatchSetApproval patchSetApproval =
- new PatchSetApproval(
- patchSetApprovalKeyProtoConverter.fromProto(proto.getKey()),
- (short) proto.getValue(),
- new Timestamp(proto.getGranted()));
+ PatchSetApproval.Builder builder =
+ PatchSetApproval.builder()
+ .key(patchSetApprovalKeyProtoConverter.fromProto(proto.getKey()))
+ .value(proto.getValue())
+ .granted(new Timestamp(proto.getGranted()))
+ .postSubmit(proto.getPostSubmit());
if (proto.hasTag()) {
- patchSetApproval.setTag(proto.getTag());
+ builder.tag(proto.getTag());
}
if (proto.hasRealAccountId()) {
- patchSetApproval.setRealAccountId(accountIdConverter.fromProto(proto.getRealAccountId()));
- }
- if (proto.hasPostSubmit()) {
- patchSetApproval.setPostSubmit(proto.getPostSubmit());
+ builder.realAccountId(accountIdConverter.fromProto(proto.getRealAccountId()));
}
- return patchSetApproval;
+ return builder.build();
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverter.java b/java/com/google/gerrit/entities/converter/PatchSetIdProtoConverter.java
index a2b95bd97d..60c13f18f9 100644
--- a/java/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/PatchSetIdProtoConverter.java
@@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.protobuf.Parser;
+@Immutable
public enum PatchSetIdProtoConverter implements ProtoConverter<Entities.PatchSet_Id, PatchSet.Id> {
INSTANCE;
@@ -28,14 +30,14 @@ public enum PatchSetIdProtoConverter implements ProtoConverter<Entities.PatchSet
@Override
public Entities.PatchSet_Id toProto(PatchSet.Id patchSetId) {
return Entities.PatchSet_Id.newBuilder()
- .setChangeId(changeIdConverter.toProto(patchSetId.getParentKey()))
- .setPatchSetId(patchSetId.get())
+ .setChangeId(changeIdConverter.toProto(patchSetId.changeId()))
+ .setId(patchSetId.get())
.build();
}
@Override
public PatchSet.Id fromProto(Entities.PatchSet_Id proto) {
- return new PatchSet.Id(changeIdConverter.fromProto(proto.getChangeId()), proto.getPatchSetId());
+ return PatchSet.id(changeIdConverter.fromProto(proto.getChangeId()), proto.getId());
}
@Override
diff --git a/java/com/google/gerrit/entities/converter/PatchSetProtoConverter.java b/java/com/google/gerrit/entities/converter/PatchSetProtoConverter.java
new file mode 100644
index 0000000000..13a6e71a46
--- /dev/null
+++ b/java/com/google/gerrit/entities/converter/PatchSetProtoConverter.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities.converter;
+
+import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.proto.Entities;
+import com.google.protobuf.Parser;
+import java.sql.Timestamp;
+import java.util.List;
+import org.eclipse.jgit.lib.ObjectId;
+
+@Immutable
+public enum PatchSetProtoConverter implements ProtoConverter<Entities.PatchSet, PatchSet> {
+ INSTANCE;
+
+ private final ProtoConverter<Entities.PatchSet_Id, PatchSet.Id> patchSetIdConverter =
+ PatchSetIdProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.ObjectId, ObjectId> objectIdConverter =
+ ObjectIdProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.Account_Id, Account.Id> accountIdConverter =
+ AccountIdProtoConverter.INSTANCE;
+
+ @Override
+ public Entities.PatchSet toProto(PatchSet patchSet) {
+ Entities.PatchSet.Builder builder =
+ Entities.PatchSet.newBuilder()
+ .setId(patchSetIdConverter.toProto(patchSet.id()))
+ .setCommitId(objectIdConverter.toProto(patchSet.commitId()))
+ .setUploaderAccountId(accountIdConverter.toProto(patchSet.uploader()))
+ .setCreatedOn(patchSet.createdOn().getTime());
+ List<String> groups = patchSet.groups();
+ if (!groups.isEmpty()) {
+ builder.setGroups(PatchSet.joinGroups(groups));
+ }
+ patchSet.pushCertificate().ifPresent(builder::setPushCertificate);
+ patchSet.description().ifPresent(builder::setDescription);
+ return builder.build();
+ }
+
+ @Override
+ public PatchSet fromProto(Entities.PatchSet proto) {
+ PatchSet.Builder builder =
+ PatchSet.builder()
+ .id(patchSetIdConverter.fromProto(proto.getId()))
+ .groups(
+ proto.hasGroups() ? PatchSet.splitGroups(proto.getGroups()) : ImmutableList.of());
+ if (proto.hasPushCertificate()) {
+ builder.pushCertificate(proto.getPushCertificate());
+ }
+ if (proto.hasDescription()) {
+ builder.description(proto.getDescription());
+ }
+
+ // The following fields used to theoretically be nullable in PatchSet, but in practice no
+ // production codepath should have ever serialized an instance that was missing one of these
+ // fields.
+ //
+ // However, since some protos may theoretically be missing these fields, we need to support
+ // them. Populate specific sentinel values for each field as documented in the PatchSet javadoc.
+ // Callers that encounter one of these sentinels will likely fail, for example by failing to
+ // look up the zeroId. They would have also failed back when the fields were nullable, for
+ // example with NPE; the current behavior just fails slightly differently.
+ builder
+ .commitId(
+ proto.hasCommitId()
+ ? objectIdConverter.fromProto(proto.getCommitId())
+ : ObjectId.zeroId())
+ .uploader(
+ proto.hasUploaderAccountId()
+ ? accountIdConverter.fromProto(proto.getUploaderAccountId())
+ : Account.id(0))
+ .createdOn(proto.hasCreatedOn() ? new Timestamp(proto.getCreatedOn()) : new Timestamp(0));
+
+ return builder.build();
+ }
+
+ @Override
+ public Parser<Entities.PatchSet> getParser() {
+ return Entities.PatchSet.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverter.java b/java/com/google/gerrit/entities/converter/ProjectNameKeyProtoConverter.java
index f7d809ec27..6bb0f79ea2 100644
--- a/java/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/ProjectNameKeyProtoConverter.java
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.protobuf.Parser;
+@Immutable
public enum ProjectNameKeyProtoConverter
implements ProtoConverter<Entities.Project_NameKey, Project.NameKey> {
INSTANCE;
@@ -29,7 +31,7 @@ public enum ProjectNameKeyProtoConverter
@Override
public Project.NameKey fromProto(Entities.Project_NameKey proto) {
- return new Project.NameKey(proto.getName());
+ return Project.nameKey(proto.getName());
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/converter/ProtoConverter.java b/java/com/google/gerrit/entities/converter/ProtoConverter.java
index 568759c3c8..a5c4052f47 100644
--- a/java/com/google/gerrit/reviewdb/converter/ProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/ProtoConverter.java
@@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
+import com.google.errorprone.annotations.Immutable;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Parser;
+@Immutable
public interface ProtoConverter<P extends MessageLite, C> {
P toProto(C valueClass);
diff --git a/java/com/google/gerrit/exceptions/BUILD b/java/com/google/gerrit/exceptions/BUILD
index e08c3fd79f..ef59be1338 100644
--- a/java/com/google/gerrit/exceptions/BUILD
+++ b/java/com/google/gerrit/exceptions/BUILD
@@ -4,5 +4,5 @@ java_library(
name = "exceptions",
srcs = glob(["*.java"]),
visibility = ["//visibility:public"],
- deps = ["//java/com/google/gerrit/reviewdb:server"],
+ deps = ["//java/com/google/gerrit/entities"],
)
diff --git a/java/com/google/gerrit/exceptions/InvalidMergeStrategyException.java b/java/com/google/gerrit/exceptions/InvalidMergeStrategyException.java
new file mode 100644
index 0000000000..d9c5776407
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/InvalidMergeStrategyException.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.exceptions;
+
+public class InvalidMergeStrategyException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidMergeStrategyException(String strategy) {
+ super("invalid merge strategy: " + strategy);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/NoSuchGroupException.java b/java/com/google/gerrit/exceptions/NoSuchGroupException.java
index dca28cb91b..95efac397c 100644
--- a/java/com/google/gerrit/exceptions/NoSuchGroupException.java
+++ b/java/com/google/gerrit/exceptions/NoSuchGroupException.java
@@ -14,7 +14,7 @@
package com.google.gerrit.exceptions;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
/** Indicates the account group does not exist. */
public class NoSuchGroupException extends Exception {
diff --git a/java/com/google/gerrit/extensions/BUILD b/java/com/google/gerrit/extensions/BUILD
index b5e58630d7..da5dc8bdcf 100644
--- a/java/com/google/gerrit/extensions/BUILD
+++ b/java/com/google/gerrit/extensions/BUILD
@@ -1,8 +1,11 @@
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("//lib:guava.bzl", "GUAVA_DOC_URL")
-load("//lib/jgit:jgit.bzl", "JGIT_DOC_URL")
load("//tools/bzl:javadoc.bzl", "java_doc")
+_DOC_VERS = "5.5.0.201909110433-r"
+
+JGIT_DOC_URL = "https://download.eclipse.org/jgit/site/" + _DOC_VERS + "/apidocs"
+
java_binary(
name = "extension-api",
main_class = "Dummy",
@@ -23,7 +26,6 @@ java_library(
],
)
-#TODO(davido): There is no provided_deps argument to java_library rule
java_library(
name = "api",
srcs = glob(["**/*.java"]),
diff --git a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
index 3d5fcccde8..f0462c6ea5 100644
--- a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
+++ b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.ListChangesOption;
+import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
@@ -209,6 +210,10 @@ public interface ChangeApi {
return suggestReviewers().withQuery(query);
}
+ default SuggestedReviewersRequest suggestCcs(String query) throws RestApiException {
+ return suggestReviewers().forCc().withQuery(query);
+ }
+
/**
* Retrieve reviewers ({@code ReviewerState.REVIEWER} and {@code ReviewerState.CC}) on the change.
*/
@@ -241,12 +246,16 @@ public interface ChangeApi {
* <ul>
* <li>{@code CHECK} is omitted, to skip consistency checks.
* <li>{@code SKIP_MERGEABLE} is omitted, so the {@code mergeable} bit <em>is</em> set.
+ * <li>{@code SKIP_DIFFSTAT} is omitted to ensure diffstat calculations.
* </ul>
*/
default ChangeInfo get() throws RestApiException {
return get(
EnumSet.complementOf(
- EnumSet.of(ListChangesOption.CHECK, ListChangesOption.SKIP_MERGEABLE)));
+ EnumSet.of(
+ ListChangesOption.CHECK,
+ ListChangesOption.SKIP_MERGEABLE,
+ ListChangesOption.SKIP_DIFFSTAT)));
}
/** {@link #get(ListChangesOption...)} with no options included. */
@@ -377,6 +386,8 @@ public interface ChangeApi {
abstract class SuggestedReviewersRequest {
private String query;
private int limit;
+ private boolean excludeGroups;
+ private ReviewerState reviewerState = ReviewerState.REVIEWER;
public abstract List<SuggestedReviewerInfo> get() throws RestApiException;
@@ -390,6 +401,16 @@ public interface ChangeApi {
return this;
}
+ public SuggestedReviewersRequest excludeGroups(boolean excludeGroups) {
+ this.excludeGroups = excludeGroups;
+ return this;
+ }
+
+ public SuggestedReviewersRequest forCc() {
+ this.reviewerState = ReviewerState.CC;
+ return this;
+ }
+
public String getQuery() {
return query;
}
@@ -397,6 +418,14 @@ public interface ChangeApi {
public int getLimit() {
return limit;
}
+
+ public boolean getExcludeGroups() {
+ return excludeGroups;
+ }
+
+ public ReviewerState getReviewerState() {
+ return reviewerState;
+ }
}
/**
diff --git a/java/com/google/gerrit/extensions/api/changes/NotifyInfo.java b/java/com/google/gerrit/extensions/api/changes/NotifyInfo.java
index ef49651bc0..dd29635b1b 100644
--- a/java/com/google/gerrit/extensions/api/changes/NotifyInfo.java
+++ b/java/com/google/gerrit/extensions/api/changes/NotifyInfo.java
@@ -14,13 +14,38 @@
package com.google.gerrit.extensions.api.changes;
+import com.google.common.base.MoreObjects;
import java.util.List;
+import java.util.Objects;
/** Detailed information about who should be notified about an update. */
public class NotifyInfo {
public List<String> accounts;
+ /**
+ * @param accounts may be either just a list of: account IDs, Full names, usernames, or emails.
+ * Also could be a list of those: "Full name <email@example.com>" or "Full name (<ID>)"
+ */
public NotifyInfo(List<String> accounts) {
this.accounts = accounts;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof NotifyInfo)) {
+ return false;
+ }
+ NotifyInfo other = (NotifyInfo) o;
+ return Objects.equals(other.accounts, accounts);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(accounts);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("accounts", accounts).toString();
+ }
}
diff --git a/java/com/google/gerrit/extensions/api/changes/RevertInput.java b/java/com/google/gerrit/extensions/api/changes/RevertInput.java
index c1be9b04f0..c4272e493c 100644
--- a/java/com/google/gerrit/extensions/api/changes/RevertInput.java
+++ b/java/com/google/gerrit/extensions/api/changes/RevertInput.java
@@ -24,4 +24,6 @@ public class RevertInput {
public NotifyHandling notify = NotifyHandling.ALL;
public Map<RecipientType, NotifyInfo> notifyDetails;
+
+ public String topic;
}
diff --git a/java/com/google/gerrit/extensions/api/changes/RevisionApi.java b/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
index 68575ca6cc..f854d4aaa5 100644
--- a/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
+++ b/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
@@ -136,7 +136,7 @@ public interface RevisionApi {
SubmitType testSubmitType(TestSubmitRuleInput in) throws RestApiException;
- List<TestSubmitRuleInfo> testSubmitRule(TestSubmitRuleInput in) throws RestApiException;
+ TestSubmitRuleInfo testSubmitRule(TestSubmitRuleInput in) throws RestApiException;
MergeListRequest getMergeList() throws RestApiException;
@@ -341,7 +341,7 @@ public interface RevisionApi {
}
@Override
- public List<TestSubmitRuleInfo> testSubmitRule(TestSubmitRuleInput in) throws RestApiException {
+ public TestSubmitRuleInfo testSubmitRule(TestSubmitRuleInput in) throws RestApiException {
throw new NotImplementedException();
}
diff --git a/java/com/google/gerrit/extensions/api/groups/GroupInput.java b/java/com/google/gerrit/extensions/api/groups/GroupInput.java
index ab38434168..30af08fb5a 100644
--- a/java/com/google/gerrit/extensions/api/groups/GroupInput.java
+++ b/java/com/google/gerrit/extensions/api/groups/GroupInput.java
@@ -18,6 +18,7 @@ import java.util.List;
public class GroupInput {
public String name;
+ public String uuid;
public String description;
public Boolean visibleToAll;
public String ownerId;
diff --git a/java/com/google/gerrit/extensions/api/projects/ProjectApi.java b/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
index 3d70996fd3..c6d9dee3e4 100644
--- a/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
+++ b/java/com/google/gerrit/extensions/api/projects/ProjectApi.java
@@ -201,6 +201,9 @@ public interface ProjectApi {
*/
void index(boolean indexChildren) throws RestApiException;
+ /** Reindexes all changes of the project. */
+ void indexChanges() throws RestApiException;
+
/**
* A default implementation which allows source compatibility when adding new methods to the
* interface.
@@ -370,5 +373,10 @@ public interface ProjectApi {
public void index(boolean indexChildren) throws RestApiException {
throw new NotImplementedException();
}
+
+ @Override
+ public void indexChanges() throws RestApiException {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/java/com/google/gerrit/extensions/client/Comment.java b/java/com/google/gerrit/extensions/client/Comment.java
index 3bca4bb9ec..d5fbf8964f 100644
--- a/java/com/google/gerrit/extensions/client/Comment.java
+++ b/java/com/google/gerrit/extensions/client/Comment.java
@@ -30,7 +30,9 @@ public abstract class Comment {
public String path;
public Side side;
public Integer parent;
- public Integer line; // value 0 or null indicates a file comment, normal lines start at 1
+ /** Value 0 or null indicates a file comment, normal lines start at 1. */
+ public Integer line;
+
public Range range;
public String inReplyTo;
public Timestamp updated;
diff --git a/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java b/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
index 652abcc7ab..ed01a4dd90 100644
--- a/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
@@ -38,7 +38,7 @@ public class DiffPreferencesInfo {
IGNORE_NONE,
IGNORE_TRAILING,
IGNORE_LEADING_AND_TRAILING,
- IGNORE_ALL;
+ IGNORE_ALL
}
public Integer context;
@@ -73,17 +73,12 @@ public class DiffPreferencesInfo {
i.fontSize = DEFAULT_FONT_SIZE;
i.lineLength = DEFAULT_LINE_LENGTH;
i.cursorBlinkRate = 0;
- i.ignoreWhitespace = Whitespace.IGNORE_NONE;
i.expandAllComments = false;
i.intralineDifference = true;
i.manualReview = false;
- i.retainHeader = false;
i.showLineEndings = true;
i.showTabs = true;
i.showWhitespaceErrors = true;
- i.skipDeleted = false;
- i.skipUnchanged = false;
- i.skipUncommented = false;
i.syntaxHighlighting = true;
i.hideTopMenu = false;
i.autoHideDiffTableHeader = true;
@@ -92,6 +87,11 @@ public class DiffPreferencesInfo {
i.hideEmptyPane = false;
i.matchBrackets = false;
i.lineWrapping = false;
+ i.ignoreWhitespace = Whitespace.IGNORE_NONE;
+ i.retainHeader = false;
+ i.skipDeleted = false;
+ i.skipUnchanged = false;
+ i.skipUncommented = false;
return i;
}
}
diff --git a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
index 8eb54e17b4..212f6da7a9 100644
--- a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
@@ -67,14 +67,6 @@ public class GeneralPreferencesInfo {
}
}
- public enum ReviewCategoryStrategy {
- NONE,
- NAME,
- EMAIL,
- USERNAME,
- ABBREV
- }
-
public enum DiffView {
SIDE_BY_SIDE,
UNIFIED_DIFF
@@ -130,10 +122,6 @@ public class GeneralPreferencesInfo {
/** Number of changes to show in a screen. */
public Integer changesPerPage;
- /** Should the site header be displayed when logged in ? */
- public Boolean showSiteHeader;
- /** Should the Flash helper movie be used to copy text to the clipboard? */
- public Boolean useFlashClipboard;
/** Type of download URL the user prefers to use. */
public String downloadScheme;
@@ -145,20 +133,15 @@ public class GeneralPreferencesInfo {
public DiffView diffView;
public Boolean sizeBarInChangeTable;
public Boolean legacycidInChangeTable;
- public ReviewCategoryStrategy reviewCategoryStrategy;
public Boolean muteCommonPathPrefixes;
public Boolean signedOffBy;
- public List<MenuItem> my;
- public List<String> changeTable;
public EmailStrategy emailStrategy;
public EmailFormat emailFormat;
public DefaultBase defaultBaseForMerges;
public Boolean publishCommentsOnPush;
public Boolean workInProgressByDefault;
-
- public boolean isShowInfoInReviewCategory() {
- return getReviewCategoryStrategy() != ReviewCategoryStrategy.NONE;
- }
+ public List<MenuItem> my;
+ public List<String> changeTable;
public DateFormat getDateFormat() {
if (dateFormat == null) {
@@ -174,13 +157,6 @@ public class GeneralPreferencesInfo {
return timeFormat;
}
- public ReviewCategoryStrategy getReviewCategoryStrategy() {
- if (reviewCategoryStrategy == null) {
- return ReviewCategoryStrategy.NONE;
- }
- return reviewCategoryStrategy;
- }
-
public DiffView getDiffView() {
if (diffView == null) {
return DiffView.SIDE_BY_SIDE;
@@ -205,11 +181,6 @@ public class GeneralPreferencesInfo {
public static GeneralPreferencesInfo defaults() {
GeneralPreferencesInfo p = new GeneralPreferencesInfo();
p.changesPerPage = DEFAULT_PAGESIZE;
- p.showSiteHeader = true;
- p.useFlashClipboard = true;
- p.emailStrategy = EmailStrategy.ENABLED;
- p.emailFormat = EmailFormat.HTML_PLAINTEXT;
- p.reviewCategoryStrategy = ReviewCategoryStrategy.NONE;
p.downloadScheme = null;
p.dateFormat = DateFormat.STD;
p.timeFormat = TimeFormat.HHMM_12;
@@ -221,6 +192,8 @@ public class GeneralPreferencesInfo {
p.legacycidInChangeTable = false;
p.muteCommonPathPrefixes = true;
p.signedOffBy = false;
+ p.emailStrategy = EmailStrategy.ENABLED;
+ p.emailFormat = EmailFormat.HTML_PLAINTEXT;
p.defaultBaseForMerges = DefaultBase.FIRST_PARENT;
p.publishCommentsOnPush = false;
p.workInProgressByDefault = false;
diff --git a/java/com/google/gerrit/extensions/client/ListChangesOption.java b/java/com/google/gerrit/extensions/client/ListChangesOption.java
index c842adc61f..425265b3fe 100644
--- a/java/com/google/gerrit/extensions/client/ListChangesOption.java
+++ b/java/com/google/gerrit/extensions/client/ListChangesOption.java
@@ -75,7 +75,13 @@ public enum ListChangesOption implements ListOption {
TRACKING_IDS(21),
/** Skip mergeability data */
- SKIP_MERGEABLE(22);
+ SKIP_MERGEABLE(22),
+
+ /**
+ * Skip diffstat computation that compute the insertions field (number of lines inserted) and
+ * deletions field (number of lines deleted)
+ */
+ SKIP_DIFFSTAT(23);
private final int value;
diff --git a/java/com/google/gerrit/extensions/common/AccountDetailInfo.java b/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
index a498ab0465..3ffa97a212 100644
--- a/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountDetailInfo.java
@@ -18,7 +18,6 @@ import java.sql.Timestamp;
public class AccountDetailInfo extends AccountInfo {
public Timestamp registeredOn;
- public Boolean inactive;
public AccountDetailInfo(Integer id) {
super(id);
diff --git a/java/com/google/gerrit/extensions/common/AccountInfo.java b/java/com/google/gerrit/extensions/common/AccountInfo.java
index f20509ba6a..d1bbe88230 100644
--- a/java/com/google/gerrit/extensions/common/AccountInfo.java
+++ b/java/com/google/gerrit/extensions/common/AccountInfo.java
@@ -14,6 +14,7 @@
package com.google.gerrit.extensions.common;
+import com.google.common.base.MoreObjects;
import java.util.List;
import java.util.Objects;
@@ -26,6 +27,7 @@ public class AccountInfo {
public List<AvatarInfo> avatars;
public Boolean _moreAccounts;
public String status;
+ public Boolean inactive;
public AccountInfo(Integer id) {
this._accountId = id;
@@ -54,6 +56,16 @@ public class AccountInfo {
}
@Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("id", _accountId)
+ .add("name", name)
+ .add("email", email)
+ .add("username", username)
+ .toString();
+ }
+
+ @Override
public int hashCode() {
return Objects.hash(
_accountId, name, email, secondaryEmails, username, avatars, _moreAccounts, status);
diff --git a/java/com/google/gerrit/extensions/common/AvatarInfo.java b/java/com/google/gerrit/extensions/common/AvatarInfo.java
index 00f18191ea..de609ebf2b 100644
--- a/java/com/google/gerrit/extensions/common/AvatarInfo.java
+++ b/java/com/google/gerrit/extensions/common/AvatarInfo.java
@@ -21,7 +21,7 @@ public class AvatarInfo {
* <p>The web UI prefers avatar images to be square, both the height and width of the image should
* be this size. The height is the more important dimension to match than the width.
*/
- public static final int DEFAULT_SIZE = 26;
+ public static final int DEFAULT_SIZE = 32;
public String url;
public Integer height;
diff --git a/java/com/google/gerrit/extensions/common/ChangeConfigInfo.java b/java/com/google/gerrit/extensions/common/ChangeConfigInfo.java
index 1e822e37e6..e8aeb40eb8 100644
--- a/java/com/google/gerrit/extensions/common/ChangeConfigInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeConfigInfo.java
@@ -23,4 +23,5 @@ public class ChangeConfigInfo {
public String replyTooltip;
public int updateDelay;
public Boolean submitWholeTopic;
+ public Boolean excludeMergeableInChangeInfo;
}
diff --git a/java/com/google/gerrit/extensions/common/GroupAuditEventInfo.java b/java/com/google/gerrit/extensions/common/GroupAuditEventInfo.java
index 3e6f7625ce..711337a342 100644
--- a/java/com/google/gerrit/extensions/common/GroupAuditEventInfo.java
+++ b/java/com/google/gerrit/extensions/common/GroupAuditEventInfo.java
@@ -15,6 +15,7 @@
package com.google.gerrit.extensions.common;
import java.sql.Timestamp;
+import java.util.Optional;
public abstract class GroupAuditEventInfo {
public enum Type {
@@ -30,35 +31,35 @@ public abstract class GroupAuditEventInfo {
public static UserMemberAuditEventInfo createAddUserEvent(
AccountInfo user, Timestamp date, AccountInfo member) {
- return new UserMemberAuditEventInfo(Type.ADD_USER, user, date, member);
+ return new UserMemberAuditEventInfo(Type.ADD_USER, user, Optional.of(date), member);
}
public static UserMemberAuditEventInfo createRemoveUserEvent(
- AccountInfo user, Timestamp date, AccountInfo member) {
+ AccountInfo user, Optional<Timestamp> date, AccountInfo member) {
return new UserMemberAuditEventInfo(Type.REMOVE_USER, user, date, member);
}
public static GroupMemberAuditEventInfo createAddGroupEvent(
AccountInfo user, Timestamp date, GroupInfo member) {
- return new GroupMemberAuditEventInfo(Type.ADD_GROUP, user, date, member);
+ return new GroupMemberAuditEventInfo(Type.ADD_GROUP, user, Optional.of(date), member);
}
public static GroupMemberAuditEventInfo createRemoveGroupEvent(
- AccountInfo user, Timestamp date, GroupInfo member) {
+ AccountInfo user, Optional<Timestamp> date, GroupInfo member) {
return new GroupMemberAuditEventInfo(Type.REMOVE_GROUP, user, date, member);
}
- protected GroupAuditEventInfo(Type type, AccountInfo user, Timestamp date) {
+ protected GroupAuditEventInfo(Type type, AccountInfo user, Optional<Timestamp> date) {
this.type = type;
this.user = user;
- this.date = date;
+ this.date = date.orElse(null);
}
public static class UserMemberAuditEventInfo extends GroupAuditEventInfo {
public AccountInfo member;
- public UserMemberAuditEventInfo(
- Type type, AccountInfo user, Timestamp date, AccountInfo member) {
+ private UserMemberAuditEventInfo(
+ Type type, AccountInfo user, Optional<Timestamp> date, AccountInfo member) {
super(type, user, date);
this.member = member;
}
@@ -67,8 +68,8 @@ public abstract class GroupAuditEventInfo {
public static class GroupMemberAuditEventInfo extends GroupAuditEventInfo {
public GroupInfo member;
- public GroupMemberAuditEventInfo(
- Type type, AccountInfo user, Timestamp date, GroupInfo member) {
+ private GroupMemberAuditEventInfo(
+ Type type, AccountInfo user, Optional<Timestamp> date, GroupInfo member) {
super(type, user, date);
this.member = member;
}
diff --git a/java/com/google/gerrit/extensions/common/testing/BUILD b/java/com/google/gerrit/extensions/common/testing/BUILD
index 9cecb66e0c..c976de0a04 100644
--- a/java/com/google/gerrit/extensions/common/testing/BUILD
+++ b/java/com/google/gerrit/extensions/common/testing/BUILD
@@ -9,7 +9,7 @@ java_library(
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/truth",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
"//lib/truth",
],
)
diff --git a/java/com/google/gerrit/extensions/common/testing/CommitInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/CommitInfoSubject.java
index f0f5516a5e..d6fcb37524 100644
--- a/java/com/google/gerrit/extensions/common/testing/CommitInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/CommitInfoSubject.java
@@ -24,7 +24,7 @@ import com.google.common.truth.Subject;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.truth.ListSubject;
-public class CommitInfoSubject extends Subject<CommitInfoSubject, CommitInfo> {
+public class CommitInfoSubject extends Subject {
public static CommitInfoSubject assertThat(CommitInfo commitInfo) {
return assertAbout(commits()).that(commitInfo);
@@ -34,37 +34,35 @@ public class CommitInfoSubject extends Subject<CommitInfoSubject, CommitInfo> {
return CommitInfoSubject::new;
}
+ private final CommitInfo commitInfo;
+
private CommitInfoSubject(FailureMetadata failureMetadata, CommitInfo commitInfo) {
super(failureMetadata, commitInfo);
+ this.commitInfo = commitInfo;
}
public StringSubject commit() {
isNotNull();
- CommitInfo commitInfo = actual();
- return check("commit()").that(commitInfo.commit);
+ return check("commit").that(commitInfo.commit);
}
public ListSubject<CommitInfoSubject, CommitInfo> parents() {
isNotNull();
- CommitInfo commitInfo = actual();
- return check("parents()").about(elements()).thatCustom(commitInfo.parents, commits());
+ return check("parents").about(elements()).thatCustom(commitInfo.parents, commits());
}
public GitPersonSubject committer() {
isNotNull();
- CommitInfo commitInfo = actual();
- return check("committer()").about(gitPersons()).that(commitInfo.committer);
+ return check("committer").about(gitPersons()).that(commitInfo.committer);
}
public GitPersonSubject author() {
isNotNull();
- CommitInfo commitInfo = actual();
- return check("author()").about(gitPersons()).that(commitInfo.author);
+ return check("author").about(gitPersons()).that(commitInfo.author);
}
public StringSubject message() {
isNotNull();
- CommitInfo commitInfo = actual();
return check("message").that(commitInfo.message);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/ContentEntrySubject.java b/java/com/google/gerrit/extensions/common/testing/ContentEntrySubject.java
index 25750c1e09..b55f7c209a 100644
--- a/java/com/google/gerrit/extensions/common/testing/ContentEntrySubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/ContentEntrySubject.java
@@ -27,7 +27,7 @@ import com.google.common.truth.Subject;
import com.google.gerrit.extensions.common.DiffInfo.ContentEntry;
import com.google.gerrit.truth.ListSubject;
-public class ContentEntrySubject extends Subject<ContentEntrySubject, ContentEntry> {
+public class ContentEntrySubject extends Subject {
public static ContentEntrySubject assertThat(ContentEntry contentEntry) {
return assertAbout(contentEntries()).that(contentEntry);
@@ -37,13 +37,15 @@ public class ContentEntrySubject extends Subject<ContentEntrySubject, ContentEnt
return ContentEntrySubject::new;
}
+ private final ContentEntry contentEntry;
+
private ContentEntrySubject(FailureMetadata failureMetadata, ContentEntry contentEntry) {
super(failureMetadata, contentEntry);
+ this.contentEntry = contentEntry;
}
public void isDueToRebase() {
isNotNull();
- ContentEntry contentEntry = actual();
if (contentEntry.dueToRebase == null || !contentEntry.dueToRebase) {
failWithActual(simpleFact("expected entry to be marked 'dueToRebase'"));
}
@@ -51,7 +53,6 @@ public class ContentEntrySubject extends Subject<ContentEntrySubject, ContentEnt
public void isNotDueToRebase() {
isNotNull();
- ContentEntry contentEntry = actual();
if (contentEntry.dueToRebase != null && contentEntry.dueToRebase) {
failWithActual(simpleFact("expected entry not to be marked 'dueToRebase'"));
}
@@ -59,7 +60,6 @@ public class ContentEntrySubject extends Subject<ContentEntrySubject, ContentEnt
public ListSubject<StringSubject, String> commonLines() {
isNotNull();
- ContentEntry contentEntry = actual();
return check("commonLines()")
.about(elements())
.that(contentEntry.ab, StandardSubjectBuilder::that);
@@ -67,31 +67,26 @@ public class ContentEntrySubject extends Subject<ContentEntrySubject, ContentEnt
public ListSubject<StringSubject, String> linesOfA() {
isNotNull();
- ContentEntry contentEntry = actual();
return check("linesOfA()").about(elements()).that(contentEntry.a, StandardSubjectBuilder::that);
}
public ListSubject<StringSubject, String> linesOfB() {
isNotNull();
- ContentEntry contentEntry = actual();
return check("linesOfB()").about(elements()).that(contentEntry.b, StandardSubjectBuilder::that);
}
public IterableSubject intralineEditsOfA() {
isNotNull();
- ContentEntry contentEntry = actual();
return check("intralineEditsOfA()").that(contentEntry.editA);
}
public IterableSubject intralineEditsOfB() {
isNotNull();
- ContentEntry contentEntry = actual();
return check("intralineEditsOfB()").that(contentEntry.editB);
}
public IntegerSubject numberOfSkippedLines() {
isNotNull();
- ContentEntry contentEntry = actual();
return check("numberOfSkippedLines()").that(contentEntry.skip);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java
index ee37bde8ee..8853a30b56 100644
--- a/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java
@@ -26,39 +26,38 @@ import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.DiffInfo.ContentEntry;
import com.google.gerrit.truth.ListSubject;
-public class DiffInfoSubject extends Subject<DiffInfoSubject, DiffInfo> {
+public class DiffInfoSubject extends Subject {
public static DiffInfoSubject assertThat(DiffInfo diffInfo) {
return assertAbout(DiffInfoSubject::new).that(diffInfo);
}
+ private final DiffInfo diffInfo;
+
private DiffInfoSubject(FailureMetadata failureMetadata, DiffInfo diffInfo) {
super(failureMetadata, diffInfo);
+ this.diffInfo = diffInfo;
}
public ListSubject<ContentEntrySubject, ContentEntry> content() {
isNotNull();
- DiffInfo diffInfo = actual();
- return check("content()")
+ return check("content")
.about(elements())
.thatCustom(diffInfo.content, ContentEntrySubject.contentEntries());
}
- public ComparableSubject<?, ChangeType> changeType() {
+ public ComparableSubject<ChangeType> changeType() {
isNotNull();
- DiffInfo diffInfo = actual();
- return check("changeType()").that(diffInfo.changeType);
+ return check("changeType").that(diffInfo.changeType);
}
public FileMetaSubject metaA() {
isNotNull();
- DiffInfo diffInfo = actual();
- return check("metaA()").about(fileMetas()).that(diffInfo.metaA);
+ return check("metaA").about(fileMetas()).that(diffInfo.metaA);
}
public FileMetaSubject metaB() {
isNotNull();
- DiffInfo diffInfo = actual();
- return check("metaB()").about(fileMetas()).that(diffInfo.metaB);
+ return check("metaB").about(fileMetas()).that(diffInfo.metaB);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/EditInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/EditInfoSubject.java
index 1c99141fdf..b5622e02ce 100644
--- a/java/com/google/gerrit/extensions/common/testing/EditInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/EditInfoSubject.java
@@ -24,7 +24,7 @@ import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.truth.OptionalSubject;
import java.util.Optional;
-public class EditInfoSubject extends Subject<EditInfoSubject, EditInfo> {
+public class EditInfoSubject extends Subject {
public static EditInfoSubject assertThat(EditInfo editInfo) {
return assertAbout(edits()).that(editInfo);
@@ -39,19 +39,20 @@ public class EditInfoSubject extends Subject<EditInfoSubject, EditInfo> {
return OptionalSubject.assertThat(editInfoOptional, edits());
}
+ private final EditInfo editInfo;
+
private EditInfoSubject(FailureMetadata failureMetadata, EditInfo editInfo) {
super(failureMetadata, editInfo);
+ this.editInfo = editInfo;
}
public CommitInfoSubject commit() {
isNotNull();
- EditInfo editInfo = actual();
- return check("commit()").about(commits()).that(editInfo.commit);
+ return check("commit").about(commits()).that(editInfo.commit);
}
public StringSubject baseRevision() {
isNotNull();
- EditInfo editInfo = actual();
- return check("baseRevision()").that(editInfo.baseRevision);
+ return check("baseRevision").that(editInfo.baseRevision);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/FileInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/FileInfoSubject.java
index 3ebf8388d9..d011d5d21d 100644
--- a/java/com/google/gerrit/extensions/common/testing/FileInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/FileInfoSubject.java
@@ -22,31 +22,31 @@ import com.google.common.truth.IntegerSubject;
import com.google.common.truth.Subject;
import com.google.gerrit.extensions.common.FileInfo;
-public class FileInfoSubject extends Subject<FileInfoSubject, FileInfo> {
+public class FileInfoSubject extends Subject {
public static FileInfoSubject assertThat(FileInfo fileInfo) {
return assertAbout(FileInfoSubject::new).that(fileInfo);
}
+ private final FileInfo fileInfo;
+
private FileInfoSubject(FailureMetadata failureMetadata, FileInfo fileInfo) {
super(failureMetadata, fileInfo);
+ this.fileInfo = fileInfo;
}
public IntegerSubject linesInserted() {
isNotNull();
- FileInfo fileInfo = actual();
- return check("linesInserted()").that(fileInfo.linesInserted);
+ return check("linesInserted").that(fileInfo.linesInserted);
}
public IntegerSubject linesDeleted() {
isNotNull();
- FileInfo fileInfo = actual();
- return check("linesDeleted()").that(fileInfo.linesDeleted);
+ return check("linesDeleted").that(fileInfo.linesDeleted);
}
- public ComparableSubject<?, Character> status() {
+ public ComparableSubject<Character> status() {
isNotNull();
- FileInfo fileInfo = actual();
- return check("status()").that(fileInfo.status);
+ return check("status").that(fileInfo.status);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java b/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java
index d1b2031b99..fb09a1ff9b 100644
--- a/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java
@@ -21,7 +21,7 @@ import com.google.common.truth.IntegerSubject;
import com.google.common.truth.Subject;
import com.google.gerrit.extensions.common.DiffInfo.FileMeta;
-public class FileMetaSubject extends Subject<FileMetaSubject, FileMeta> {
+public class FileMetaSubject extends Subject {
public static FileMetaSubject assertThat(FileMeta fileMeta) {
return assertAbout(fileMetas()).that(fileMeta);
@@ -31,13 +31,15 @@ public class FileMetaSubject extends Subject<FileMetaSubject, FileMeta> {
return FileMetaSubject::new;
}
+ private final FileMeta fileMeta;
+
private FileMetaSubject(FailureMetadata failureMetadata, FileMeta fileMeta) {
super(failureMetadata, fileMeta);
+ this.fileMeta = fileMeta;
}
public IntegerSubject totalLineCount() {
isNotNull();
- FileMeta fileMeta = actual();
return check("totalLineCount()").that(fileMeta.lines);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/FixReplacementInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/FixReplacementInfoSubject.java
index 6ba5f8b291..9ba69dc2d3 100644
--- a/java/com/google/gerrit/extensions/common/testing/FixReplacementInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/FixReplacementInfoSubject.java
@@ -22,8 +22,7 @@ import com.google.common.truth.StringSubject;
import com.google.common.truth.Subject;
import com.google.gerrit.extensions.common.FixReplacementInfo;
-public class FixReplacementInfoSubject
- extends Subject<FixReplacementInfoSubject, FixReplacementInfo> {
+public class FixReplacementInfoSubject extends Subject {
public static FixReplacementInfoSubject assertThat(FixReplacementInfo fixReplacementInfo) {
return assertAbout(fixReplacements()).that(fixReplacementInfo);
@@ -33,23 +32,26 @@ public class FixReplacementInfoSubject
return FixReplacementInfoSubject::new;
}
+ private final FixReplacementInfo fixReplacementInfo;
+
private FixReplacementInfoSubject(
FailureMetadata failureMetadata, FixReplacementInfo fixReplacementInfo) {
super(failureMetadata, fixReplacementInfo);
+ this.fixReplacementInfo = fixReplacementInfo;
}
public StringSubject path() {
isNotNull();
- return check("path()").that(actual().path);
+ return check("path").that(fixReplacementInfo.path);
}
public RangeSubject range() {
isNotNull();
- return check("range()").about(ranges()).that(actual().range);
+ return check("range").about(ranges()).that(fixReplacementInfo.range);
}
public StringSubject replacement() {
isNotNull();
- return check("replacement()").that(actual().replacement);
+ return check("replacement").that(fixReplacementInfo.replacement);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java
index 98dac38aa8..4ac725adc9 100644
--- a/java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java
@@ -21,12 +21,11 @@ import static com.google.gerrit.truth.ListSubject.elements;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.StringSubject;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.extensions.common.FixReplacementInfo;
import com.google.gerrit.extensions.common.FixSuggestionInfo;
import com.google.gerrit.truth.ListSubject;
-public class FixSuggestionInfoSubject extends Subject<FixSuggestionInfoSubject, FixSuggestionInfo> {
+public class FixSuggestionInfoSubject extends Subject {
public static FixSuggestionInfoSubject assertThat(FixSuggestionInfo fixSuggestionInfo) {
return assertAbout(fixSuggestions()).that(fixSuggestionInfo);
@@ -36,20 +35,23 @@ public class FixSuggestionInfoSubject extends Subject<FixSuggestionInfoSubject,
return FixSuggestionInfoSubject::new;
}
+ private final FixSuggestionInfo fixSuggestionInfo;
+
private FixSuggestionInfoSubject(
FailureMetadata failureMetadata, FixSuggestionInfo fixSuggestionInfo) {
super(failureMetadata, fixSuggestionInfo);
+ this.fixSuggestionInfo = fixSuggestionInfo;
}
public StringSubject fixId() {
- return Truth.assertThat(actual().fixId).named("fixId");
+ return check("fixId").that(fixSuggestionInfo.fixId);
}
public ListSubject<FixReplacementInfoSubject, FixReplacementInfo> replacements() {
isNotNull();
- return check("replacements()")
+ return check("replacements")
.about(elements())
- .thatCustom(actual().replacements, fixReplacements());
+ .thatCustom(fixSuggestionInfo.replacements, fixReplacements());
}
public FixReplacementInfoSubject onlyReplacement() {
@@ -58,6 +60,6 @@ public class FixSuggestionInfoSubject extends Subject<FixSuggestionInfoSubject,
public StringSubject description() {
isNotNull();
- return check("description()").that(actual().description);
+ return check("description").that(fixSuggestionInfo.description);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java b/java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java
index dee0636a54..d827d5d9f9 100644
--- a/java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java
@@ -14,8 +14,8 @@
package com.google.gerrit.extensions.common.testing;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertAbout;
+import static java.util.Objects.requireNonNull;
import com.google.common.truth.ComparableSubject;
import com.google.common.truth.FailureMetadata;
@@ -27,7 +27,7 @@ import java.sql.Timestamp;
import java.util.Date;
import org.eclipse.jgit.lib.PersonIdent;
-public class GitPersonSubject extends Subject<GitPersonSubject, GitPerson> {
+public class GitPersonSubject extends Subject {
public static GitPersonSubject assertThat(GitPerson gitPerson) {
return assertAbout(gitPersons()).that(gitPerson);
@@ -37,36 +37,35 @@ public class GitPersonSubject extends Subject<GitPersonSubject, GitPerson> {
return GitPersonSubject::new;
}
+ private final GitPerson gitPerson;
+
private GitPersonSubject(FailureMetadata failureMetadata, GitPerson gitPerson) {
super(failureMetadata, gitPerson);
+ this.gitPerson = gitPerson;
}
public StringSubject name() {
isNotNull();
- GitPerson gitPerson = actual();
- return check("name()").that(gitPerson.name);
+ return check("name").that(gitPerson.name);
}
public StringSubject email() {
isNotNull();
- GitPerson gitPerson = actual();
- return check("email()").that(gitPerson.email);
+ return check("email").that(gitPerson.email);
}
- public ComparableSubject<?, Timestamp> date() {
+ public ComparableSubject<Timestamp> date() {
isNotNull();
- GitPerson gitPerson = actual();
- return check("date()").that(gitPerson.date);
+ return check("date").that(gitPerson.date);
}
public IntegerSubject tz() {
isNotNull();
- GitPerson gitPerson = actual();
- return check("tz()").that(gitPerson.tz);
+ return check("tz").that(gitPerson.tz);
}
public void hasSameDateAs(GitPerson other) {
- checkNotNull(other, "'other' GitPerson must not be null");
+ requireNonNull(other, "'other' GitPerson must not be null");
isNotNull();
date().isEqualTo(other.date);
tz().isEqualTo(other.tz);
@@ -76,7 +75,7 @@ public class GitPersonSubject extends Subject<GitPersonSubject, GitPerson> {
isNotNull();
name().isEqualTo(ident.getName());
email().isEqualTo(ident.getEmailAddress());
- check("roundedDate()").that(new Date(actual().date.getTime())).isEqualTo(ident.getWhen());
+ check("roundedDate()").that(new Date(gitPerson.date.getTime())).isEqualTo(ident.getWhen());
tz().isEqualTo(ident.getTimeZoneOffset());
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/RangeSubject.java b/java/com/google/gerrit/extensions/common/testing/RangeSubject.java
index 12acb8d8c7..10abca2459 100644
--- a/java/com/google/gerrit/extensions/common/testing/RangeSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/RangeSubject.java
@@ -22,7 +22,7 @@ import com.google.common.truth.IntegerSubject;
import com.google.common.truth.Subject;
import com.google.gerrit.extensions.client.Comment;
-public class RangeSubject extends Subject<RangeSubject, Comment.Range> {
+public class RangeSubject extends Subject {
public static RangeSubject assertThat(Comment.Range range) {
return assertAbout(ranges()).that(range);
@@ -32,36 +32,39 @@ public class RangeSubject extends Subject<RangeSubject, Comment.Range> {
return RangeSubject::new;
}
+ private final Comment.Range range;
+
private RangeSubject(FailureMetadata failureMetadata, Comment.Range range) {
super(failureMetadata, range);
+ this.range = range;
}
public IntegerSubject startLine() {
- return check("startLine()").that(actual().startLine);
+ return check("startLine").that(range.startLine);
}
public IntegerSubject startCharacter() {
- return check("startCharacter()").that(actual().startCharacter);
+ return check("startCharacter").that(range.startCharacter);
}
public IntegerSubject endLine() {
- return check("endLine()").that(actual().endLine);
+ return check("endLine").that(range.endLine);
}
public IntegerSubject endCharacter() {
- return check("endCharacter()").that(actual().endCharacter);
+ return check("endCharacter").that(range.endCharacter);
}
public void isValid() {
isNotNull();
- if (!actual().isValid()) {
+ if (!range.isValid()) {
failWithActual(simpleFact("expected to be valid"));
}
}
public void isInvalid() {
isNotNull();
- if (actual().isValid()) {
+ if (range.isValid()) {
failWithActual(simpleFact("expected to be invalid"));
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/RobotCommentInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/RobotCommentInfoSubject.java
index 033f54ba59..0698735acf 100644
--- a/java/com/google/gerrit/extensions/common/testing/RobotCommentInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/RobotCommentInfoSubject.java
@@ -24,11 +24,11 @@ import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.truth.ListSubject;
import java.util.List;
-public class RobotCommentInfoSubject extends Subject<RobotCommentInfoSubject, RobotCommentInfo> {
+public class RobotCommentInfoSubject extends Subject {
public static ListSubject<RobotCommentInfoSubject, RobotCommentInfo> assertThatList(
List<RobotCommentInfo> robotCommentInfos) {
- return ListSubject.assertThat(robotCommentInfos, robotComments()).named("robotCommentInfos");
+ return ListSubject.assertThat(robotCommentInfos, robotComments());
}
public static RobotCommentInfoSubject assertThat(RobotCommentInfo robotCommentInfo) {
@@ -39,15 +39,18 @@ public class RobotCommentInfoSubject extends Subject<RobotCommentInfoSubject, Ro
return RobotCommentInfoSubject::new;
}
+ private final RobotCommentInfo robotCommentInfo;
+
private RobotCommentInfoSubject(
FailureMetadata failureMetadata, RobotCommentInfo robotCommentInfo) {
super(failureMetadata, robotCommentInfo);
+ this.robotCommentInfo = robotCommentInfo;
}
public ListSubject<FixSuggestionInfoSubject, FixSuggestionInfo> fixSuggestions() {
- return check("fixSuggestions()")
+ return check("fixSuggestions")
.about(elements())
- .thatCustom(actual().fixSuggestions, FixSuggestionInfoSubject.fixSuggestions());
+ .thatCustom(robotCommentInfo.fixSuggestions, FixSuggestionInfoSubject.fixSuggestions());
}
public FixSuggestionInfoSubject onlyFixSuggestion() {
diff --git a/java/com/google/gerrit/extensions/events/AccountActivationListener.java b/java/com/google/gerrit/extensions/events/AccountActivationListener.java
new file mode 100644
index 0000000000..b45533b8bf
--- /dev/null
+++ b/java/com/google/gerrit/extensions/events/AccountActivationListener.java
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/**
+ * Notified whenever an account got activated or deactivated.
+ *
+ * <p>This listener is called only after an account got (de)activated and hence cannot cancel the
+ * (de)activation. See {@link
+ * com.google.gerrit.server.validators.AccountActivationValidationListener} for a listener that can
+ * cancel a (de)activation.
+ */
+@ExtensionPoint
+public interface AccountActivationListener {
+ /**
+ * Invoked after an account got activated
+ *
+ * @param id of the account
+ */
+ default void onAccountActivated(int id) {}
+
+ /**
+ * Invoked after an account got deactivated
+ *
+ * @param id of the account
+ */
+ default void onAccountDeactivated(int id) {}
+}
diff --git a/java/com/google/gerrit/extensions/events/ChangeEvent.java b/java/com/google/gerrit/extensions/events/ChangeEvent.java
index 0b510528b3..def75b7e38 100644
--- a/java/com/google/gerrit/extensions/events/ChangeEvent.java
+++ b/java/com/google/gerrit/extensions/events/ChangeEvent.java
@@ -20,6 +20,11 @@ import java.sql.Timestamp;
/** Interface to be extended by Events with a Change. */
public interface ChangeEvent extends GerritEvent {
+ /**
+ * Information about the change. Some fields might be null.
+ *
+ * @see com.google.gerrit.server.extensions.events.EventUtil
+ */
ChangeInfo getChange();
AccountInfo getWho();
diff --git a/java/com/google/gerrit/extensions/events/ChangeIndexedListener.java b/java/com/google/gerrit/extensions/events/ChangeIndexedListener.java
index 8dd64edd97..64bc022353 100644
--- a/java/com/google/gerrit/extensions/events/ChangeIndexedListener.java
+++ b/java/com/google/gerrit/extensions/events/ChangeIndexedListener.java
@@ -20,6 +20,21 @@ import com.google.gerrit.extensions.annotations.ExtensionPoint;
@ExtensionPoint
public interface ChangeIndexedListener {
/**
+ * Invoked when a change is scheduled for indexing.
+ *
+ * @param projectName project containing the change
+ * @param id id of the change that was scheduled for indexing
+ */
+ default void onChangeScheduledForIndexing(String projectName, int id) {}
+
+ /**
+ * Invoked when a change is scheduled for deletion from indexing.
+ *
+ * @param id id of the change that was scheduled for deletion from indexing
+ */
+ default void onChangeScheduledForDeletionFromIndex(int id) {}
+
+ /**
* Invoked when a change is indexed.
*
* @param projectName project containing the change
diff --git a/java/com/google/gerrit/extensions/events/RevisionEvent.java b/java/com/google/gerrit/extensions/events/RevisionEvent.java
index f0cfa2c4b6..db7830e0bc 100644
--- a/java/com/google/gerrit/extensions/events/RevisionEvent.java
+++ b/java/com/google/gerrit/extensions/events/RevisionEvent.java
@@ -18,5 +18,11 @@ import com.google.gerrit.extensions.common.RevisionInfo;
/** Interface to be extended by Events with a Revision. */
public interface RevisionEvent extends ChangeEvent {
+
+ /**
+ * Information about the revision. Some fields might be null.
+ *
+ * @see com.google.gerrit.server.extensions.events.EventUtil
+ */
RevisionInfo getRevision();
}
diff --git a/java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java b/java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java
index fa2288aa84..270a040c9b 100644
--- a/java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java
+++ b/java/com/google/gerrit/extensions/restapi/ResourceNotFoundException.java
@@ -38,9 +38,8 @@ public class ResourceNotFoundException extends RestApiException {
super("Not found: " + id.get(), cause);
}
- @SuppressWarnings("unchecked")
- @Override
public ResourceNotFoundException caching(CacheControl c) {
- return super.caching(c);
+ setCaching(c);
+ return this;
}
}
diff --git a/java/com/google/gerrit/extensions/restapi/Response.java b/java/com/google/gerrit/extensions/restapi/Response.java
index 8f2dd5fea1..5504cfdfec 100644
--- a/java/com/google/gerrit/extensions/restapi/Response.java
+++ b/java/com/google/gerrit/extensions/restapi/Response.java
@@ -14,6 +14,10 @@
package com.google.gerrit.extensions.restapi;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.gerrit.common.Nullable;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
/** Special return value to mean specific HTTP status codes in a REST API. */
@@ -51,24 +55,48 @@ public abstract class Response<T> {
return new Redirect(location);
}
+ /**
+ * HTTP 500 Internal Server Error: failure due to an unexpected exception.
+ *
+ * <p>Can be returned from REST endpoints, instead of throwing the exception, if additional
+ * properties (e.g. a traceId) should be set on the response.
+ *
+ * @param cause the exception that caused the request to fail, must not be a {@link
+ * RestApiException} because such an exception would result in a 4XX response code
+ */
+ public static <T> InternalServerError<T> internalServerError(Exception cause) {
+ return new InternalServerError<>(cause);
+ }
+
/** Arbitrary status code with wrapped result. */
public static <T> Response<T> withStatusCode(int statusCode, T value) {
return new Impl<>(statusCode, value);
}
@SuppressWarnings({"unchecked", "rawtypes"})
- public static <T> T unwrap(T obj) {
+ public static <T> T unwrap(T obj) throws Exception {
while (obj instanceof Response) {
obj = (T) ((Response) obj).value();
}
return obj;
}
+ private String traceId;
+
+ public Response<T> traceId(@Nullable String traceId) {
+ this.traceId = traceId;
+ return this;
+ }
+
+ public Optional<String> traceId() {
+ return Optional.ofNullable(traceId);
+ }
+
public abstract boolean isNone();
public abstract int statusCode();
- public abstract T value();
+ public abstract T value() throws Exception;
public abstract CacheControl caching();
@@ -154,13 +182,38 @@ public abstract class Response<T> {
}
/** An HTTP redirect to another location. */
- public static final class Redirect {
+ public static final class Redirect extends Response<Object> {
private final String location;
private Redirect(String url) {
this.location = url;
}
+ @Override
+ public boolean isNone() {
+ return false;
+ }
+
+ @Override
+ public int statusCode() {
+ return 302;
+ }
+
+ @Override
+ public Object value() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public CacheControl caching() {
+ return CacheControl.NONE;
+ }
+
+ @Override
+ public Response<Object> caching(CacheControl c) {
+ throw new UnsupportedOperationException();
+ }
+
public String location() {
return location;
}
@@ -182,13 +235,38 @@ public abstract class Response<T> {
}
/** Accepted as task for asynchronous execution. */
- public static final class Accepted {
+ public static final class Accepted extends Response<Object> {
private final String location;
private Accepted(String url) {
this.location = url;
}
+ @Override
+ public boolean isNone() {
+ return false;
+ }
+
+ @Override
+ public int statusCode() {
+ return 202;
+ }
+
+ @Override
+ public Object value() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public CacheControl caching() {
+ return CacheControl.NONE;
+ }
+
+ @Override
+ public Response<Object> caching(CacheControl c) {
+ throw new UnsupportedOperationException();
+ }
+
public String location() {
return location;
}
@@ -208,4 +286,57 @@ public abstract class Response<T> {
return String.format("[202 Accepted] %s", location);
}
}
+
+ public static final class InternalServerError<T> extends Response<T> {
+ private final Exception cause;
+
+ private InternalServerError(Exception cause) {
+ checkArgument(!(cause instanceof RestApiException), "cause must not be a RestApiException");
+ this.cause = cause;
+ }
+
+ @Override
+ public boolean isNone() {
+ return false;
+ }
+
+ @Override
+ public int statusCode() {
+ return 500;
+ }
+
+ @Override
+ public T value() throws Exception {
+ throw cause();
+ }
+
+ @Override
+ public CacheControl caching() {
+ return CacheControl.NONE;
+ }
+
+ @Override
+ public Response<T> caching(CacheControl c) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Exception cause() {
+ return cause;
+ }
+
+ @Override
+ public int hashCode() {
+ return cause.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof InternalServerError && ((InternalServerError<?>) o).cause.equals(cause);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[500 Internal Server Error] %s", cause.getClass());
+ }
+ }
}
diff --git a/java/com/google/gerrit/extensions/restapi/RestApiException.java b/java/com/google/gerrit/extensions/restapi/RestApiException.java
index b09723e8df..f3d7decb0c 100644
--- a/java/com/google/gerrit/extensions/restapi/RestApiException.java
+++ b/java/com/google/gerrit/extensions/restapi/RestApiException.java
@@ -14,6 +14,8 @@
package com.google.gerrit.extensions.restapi;
+import static java.util.Objects.requireNonNull;
+
/** Root exception type for REST API failures. */
public class RestApiException extends Exception {
private static final long serialVersionUID = 1L;
@@ -33,9 +35,7 @@ public class RestApiException extends Exception {
return caching;
}
- @SuppressWarnings("unchecked")
- public <T extends RestApiException> T caching(CacheControl c) {
- caching = c;
- return (T) this;
+ protected void setCaching(CacheControl caching) {
+ this.caching = requireNonNull(caching);
}
}
diff --git a/java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java b/java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java
index 25cdb767d1..72ca74b9bf 100644
--- a/java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestCollectionCreateView.java
@@ -33,13 +33,24 @@ public interface RestCollectionCreateView<P extends RestResource, C extends Rest
/**
* Process the view operation by creating the resource.
*
+ * <p>The returned response defines the status code that is returned to the client. For
+ * RestCollectionCreateViews this is usually {@code 201 Created} because a resource is created,
+ * but other 2XX or 3XX status codes are also possible (e.g. {@link Response.Redirect} can be
+ * returned for {@code 302 Found}).
+ *
+ * <p>The value of the returned response is automatically converted to JSON unless it is a {@link
+ * BinaryResult}.
+ *
+ * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+ * any other exception the client will get a {@code 500 Internal Server Error} response.
+ *
* @param parentResource parent resource of the resource that should be created
+ * @param id the ID of the child resource that should be created
* @param input input after parsing from request.
- * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion
- * to JSON.
+ * @return response to return to the client
* @throws RestApiException if the resource creation is rejected
* @throws Exception the implementation of the view failed. The exception will be logged and HTTP
* 500 Internal Server Error will be returned to the client.
*/
- Object apply(P parentResource, IdString id, I input) throws Exception;
+ Response<?> apply(P parentResource, IdString id, I input) throws Exception;
}
diff --git a/java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java b/java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java
index 7e5649c3ca..c08d06a471 100644
--- a/java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestCollectionDeleteMissingView.java
@@ -37,13 +37,25 @@ public interface RestCollectionDeleteMissingView<P extends RestResource, C exten
/**
* Process the view operation by deleting the resource.
*
+ * <p>The returned response defines the status code that is returned to the client. For
+ * RestCollectionDeleteMissingViews this is usually {@code 204 No Content} because a resource is
+ * deleted, but other 2XX or 3XX status codes are also possible (e.g. {@code 200 OK}, {@code 302
+ * Found} for a redirect).
+ *
+ * <p>The returned response usually does not have any value (status code {@code 204 No Content}).
+ * If a value in the returned response is set it is automatically converted to JSON unless it is a
+ * {@link BinaryResult}.
+ *
+ * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+ * any other exception the client will get a {@code 500 Internal Server Error} response.
+ *
* @param parentResource parent resource of the resource that should be deleted
- * @param input input after parsing from request.
- * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion
- * to JSON.
+ * @param id the ID of the child resource that should be deleted
+ * @param input input after parsing from request
+ * @return response to return to the client
* @throws RestApiException if the resource creation is rejected
* @throws Exception the implementation of the view failed. The exception will be logged and HTTP
* 500 Internal Server Error will be returned to the client.
*/
- Object apply(P parentResource, IdString id, I input) throws Exception;
+ Response<?> apply(P parentResource, IdString id, I input) throws Exception;
}
diff --git a/java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java b/java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java
index acabf96323..fcaa15bb79 100644
--- a/java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestCollectionModifyView.java
@@ -28,5 +28,25 @@ package com.google.gerrit.extensions.restapi;
public interface RestCollectionModifyView<P extends RestResource, C extends RestResource, I>
extends RestCollectionView<P, C, I> {
- Object apply(P parentResource, I input) throws Exception;
+ /**
+ * Process the modification on the collection resource.
+ *
+ * <p>The value of the returned response is automatically converted to JSON unless it is a {@link
+ * BinaryResult}.
+ *
+ * <p>The returned response defines the status code that is returned to the client. For
+ * RestCollectionModifyViews this is usually {@code 200 OK}, but other 2XX or 3XX status codes are
+ * also possible (e.g. {@code 201 Created} if a resource was created, {@code 202 Accepted} if a
+ * background task was scheduled, {@code 204 No Content} if no content is returned, {@code 302
+ * Found} for a redirect).
+ *
+ * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+ * any other exception the client will get a {@code 500 Internal Server Error} response.
+ *
+ * @param parentResource the collection resource on which the modification is done
+ * @return response to return to the client
+ * @throws Exception the implementation of the view failed. The exception will be logged and HTTP
+ * 500 Internal Server Error will be returned to the client.
+ */
+ Response<?> apply(P parentResource, I input) throws Exception;
}
diff --git a/java/com/google/gerrit/extensions/restapi/RestModifyView.java b/java/com/google/gerrit/extensions/restapi/RestModifyView.java
index 79053dd5a1..e397bd0a78 100644
--- a/java/com/google/gerrit/extensions/restapi/RestModifyView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestModifyView.java
@@ -28,11 +28,21 @@ public interface RestModifyView<R extends RestResource, I> extends RestView<R> {
/**
* Process the view operation by altering the resource.
*
- * @param resource resource to modify.
- * @param input input after parsing from request.
- * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion
- * to JSON.
- * @throws AuthException the client is not permitted to access this view.
+ * <p>The value of the returned response is automatically converted to JSON unless it is a {@link
+ * BinaryResult}.
+ *
+ * <p>The returned response defines the status code that is returned to the client. For
+ * RestModifyViews this is usually {@code 200 OK}, but other 2XX or 3XX status codes are also
+ * possible (e.g. {@code 202 Accepted} if a background task was scheduled, {@code 204 No Content}
+ * if no content is returned, {@code 302 Found} for a redirect).
+ *
+ * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+ * any other exception the client will get a {@code 500 Internal Server Error} response.
+ *
+ * @param resource resource to modify
+ * @param input input after parsing from request
+ * @return response to return to the client
+ * @throws AuthException the caller is not permitted to access this view.
* @throws BadRequestException the request was incorrectly specified and cannot be handled by this
* view.
* @throws ResourceConflictException the resource state does not permit this view to make the
@@ -40,6 +50,6 @@ public interface RestModifyView<R extends RestResource, I> extends RestView<R> {
* @throws Exception the implementation of the view failed. The exception will be logged and HTTP
* 500 Internal Server Error will be returned to the client.
*/
- Object apply(R resource, I input)
+ Response<?> apply(R resource, I input)
throws AuthException, BadRequestException, ResourceConflictException, Exception;
}
diff --git a/java/com/google/gerrit/extensions/restapi/RestReadView.java b/java/com/google/gerrit/extensions/restapi/RestReadView.java
index a3c31d3b71..8991f0b552 100644
--- a/java/com/google/gerrit/extensions/restapi/RestReadView.java
+++ b/java/com/google/gerrit/extensions/restapi/RestReadView.java
@@ -17,16 +17,27 @@ package com.google.gerrit.extensions.restapi;
/**
* RestView to read a resource without modification.
*
+ * <p>RestReadViews are invoked by the HTTP GET method.
+ *
* @param <R> type of resource the view reads.
*/
public interface RestReadView<R extends RestResource> extends RestView<R> {
/**
* Process the view operation by reading from the resource.
*
- * @param resource resource to read.
- * @return result to return to the client. Use {@link BinaryResult} to avoid automatic conversion
- * to JSON.
- * @throws AuthException the client is not permitted to access this view.
+ * <p>The value of the returned response is automatically converted to JSON unless it is a {@link
+ * BinaryResult}.
+ *
+ * <p>The returned response defines the status code that is returned to the client. For
+ * RestReadViews this is usually {@code 200 OK}, but other 2XX or 3XX status codes are also
+ * possible (e.g. {@link Response.Redirect} can be returned for {@code 302 Found}).
+ *
+ * <p>Throwing a subclass of {@link RestApiException} results in a 4XX response to the client. For
+ * any other exception the client will get a {@code 500 Internal Server Error} response.
+ *
+ * @param resource resource to read
+ * @return response to return to the client
+ * @throws AuthException the caller is not permitted to access this view.
* @throws BadRequestException the request was incorrectly specified and cannot be handled by this
* view.
* @throws ResourceConflictException the resource state does not permit this view to make the
@@ -34,6 +45,6 @@ public interface RestReadView<R extends RestResource> extends RestView<R> {
* @throws Exception the implementation of the view failed. The exception will be logged and HTTP
* 500 Internal Server Error will be returned to the client.
*/
- Object apply(R resource)
+ Response<?> apply(R resource)
throws AuthException, BadRequestException, ResourceConflictException, Exception;
}
diff --git a/java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java b/java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java
index 510920519a..c5304e386e 100644
--- a/java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java
+++ b/java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java
@@ -26,7 +26,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Optional;
-public class BinaryResultSubject extends Subject<BinaryResultSubject, BinaryResult> {
+public class BinaryResultSubject extends Subject {
public static BinaryResultSubject assertThat(BinaryResult binaryResult) {
return assertAbout(binaryResults()).that(binaryResult);
@@ -41,8 +41,11 @@ public class BinaryResultSubject extends Subject<BinaryResultSubject, BinaryResu
return OptionalSubject.assertThat(binaryResultOptional, binaryResults());
}
+ private final BinaryResult binaryResult;
+
private BinaryResultSubject(FailureMetadata failureMetadata, BinaryResult binaryResult) {
super(failureMetadata, binaryResult);
+ this.binaryResult = binaryResult;
}
public StringSubject asString() throws IOException {
@@ -50,7 +53,6 @@ public class BinaryResultSubject extends Subject<BinaryResultSubject, BinaryResu
// We shouldn't close the BinaryResult within this method as it might still
// be used afterwards. Besides, closing it doesn't have an effect for most
// implementations of a BinaryResult.
- BinaryResult binaryResult = actual();
return check("asString()").that(binaryResult.asString());
}
@@ -59,7 +61,6 @@ public class BinaryResultSubject extends Subject<BinaryResultSubject, BinaryResu
// We shouldn't close the BinaryResult within this method as it might still
// be used afterwards. Besides, closing it doesn't have an effect for most
// implementations of a BinaryResult.
- BinaryResult binaryResult = actual();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
binaryResult.writeTo(byteArrayOutputStream);
byte[] bytes = byteArrayOutputStream.toByteArray();
diff --git a/java/com/google/gerrit/extensions/validators/CommentForValidation.java b/java/com/google/gerrit/extensions/validators/CommentForValidation.java
new file mode 100644
index 0000000000..51ae5ae975
--- /dev/null
+++ b/java/com/google/gerrit/extensions/validators/CommentForValidation.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.validators;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * Holds a comment's text and {@link CommentType} in order to pass it to a validation plugin.
+ *
+ * @see CommentValidator
+ */
+@AutoValue
+public abstract class CommentForValidation {
+
+ /** The type of comment. */
+ public enum CommentType {
+ /** A regular (inline) comment. */
+ INLINE_COMMENT,
+ /** A file comment. */
+ FILE_COMMENT,
+ /** A change message. */
+ CHANGE_MESSAGE
+ }
+
+ public static CommentForValidation create(CommentType type, String text) {
+ return new AutoValue_CommentForValidation(type, text);
+ }
+
+ public abstract CommentType getType();
+
+ public abstract String getText();
+
+ public CommentValidationFailure failValidation(String message) {
+ return CommentValidationFailure.create(this, message);
+ }
+}
diff --git a/java/com/google/gerrit/extensions/validators/CommentValidationFailure.java b/java/com/google/gerrit/extensions/validators/CommentValidationFailure.java
new file mode 100644
index 0000000000..1a832760ca
--- /dev/null
+++ b/java/com/google/gerrit/extensions/validators/CommentValidationFailure.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.validators;
+
+import com.google.auto.value.AutoValue;
+
+/** A comment or review message was rejected by a {@link CommentValidator}. */
+@AutoValue
+public abstract class CommentValidationFailure {
+ static CommentValidationFailure create(
+ CommentForValidation commentForValidation, String message) {
+ return new AutoValue_CommentValidationFailure(commentForValidation, message);
+ }
+
+ /** Returns the offending comment. */
+ public abstract CommentForValidation getComment();
+
+ /** A friendly message set by the {@link CommentValidator}. */
+ public abstract String getMessage();
+}
diff --git a/java/com/google/gerrit/extensions/validators/CommentValidator.java b/java/com/google/gerrit/extensions/validators/CommentValidator.java
new file mode 100644
index 0000000000..cfefdefc8f
--- /dev/null
+++ b/java/com/google/gerrit/extensions/validators/CommentValidator.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.validators;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/**
+ * Validates review comments and messages. Rejecting any comment/message will prevent all comments
+ * from being published.
+ */
+@ExtensionPoint
+public interface CommentValidator {
+
+ /**
+ * Validate the specified comments.
+ *
+ * @return An empty list if all comments are valid, or else a list of validation failures.
+ */
+ ImmutableList<CommentValidationFailure> validateComments(
+ ImmutableList<CommentForValidation> comments);
+}
diff --git a/java/com/google/gerrit/git/BUILD b/java/com/google/gerrit/git/BUILD
index 4c4d5bcc89..05747165c3 100644
--- a/java/com/google/gerrit/git/BUILD
+++ b/java/com/google/gerrit/git/BUILD
@@ -5,7 +5,10 @@ java_library(
srcs = glob(["*.java"]),
visibility = ["//visibility:public"],
deps = [
+ "//java/com/google/gerrit/common:annotations",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
+ "//lib/auto:auto-value",
+ "//lib/auto:auto-value-annotations",
],
)
diff --git a/java/com/google/gerrit/git/GitUpdateFailureException.java b/java/com/google/gerrit/git/GitUpdateFailureException.java
new file mode 100644
index 0000000000..76ef2176b2
--- /dev/null
+++ b/java/com/google/gerrit/git/GitUpdateFailureException.java
@@ -0,0 +1,95 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.git;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
+import java.io.IOException;
+import java.util.Optional;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/** Thrown when updating a ref in Git fails. */
+public class GitUpdateFailureException extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ private final ImmutableList<GitUpdateFailure> failures;
+
+ public GitUpdateFailureException(String message, RefUpdate refUpdate) {
+ super(message);
+ this.failures = ImmutableList.of(GitUpdateFailure.create(refUpdate));
+ }
+
+ public GitUpdateFailureException(String message, BatchRefUpdate batchRefUpdate) {
+ super(message);
+ this.failures =
+ batchRefUpdate.getCommands().stream()
+ .filter(c -> c.getResult() != ReceiveCommand.Result.OK)
+ .map(GitUpdateFailure::create)
+ .collect(toImmutableList());
+ }
+
+ /** @return the names of the refs for which the update failed. */
+ public ImmutableList<String> getFailedRefs() {
+ return failures.stream().map(GitUpdateFailure::ref).collect(toImmutableList());
+ }
+
+ /** @return the failures that caused this exception. */
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public ImmutableList<GitUpdateFailure> getFailures() {
+ return failures;
+ }
+
+ @AutoValue
+ public abstract static class GitUpdateFailure {
+ private static GitUpdateFailure create(RefUpdate refUpdate) {
+ return builder().ref(refUpdate.getName()).result(refUpdate.getResult().name()).build();
+ }
+
+ private static GitUpdateFailure create(ReceiveCommand receiveCommand) {
+ return builder()
+ .ref(receiveCommand.getRefName())
+ .result(receiveCommand.getResult().name())
+ .message(receiveCommand.getMessage())
+ .build();
+ }
+
+ public abstract String ref();
+
+ public abstract String result();
+
+ public abstract Optional<String> message();
+
+ public static GitUpdateFailure.Builder builder() {
+ return new AutoValue_GitUpdateFailureException_GitUpdateFailure.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder ref(String ref);
+
+ abstract Builder result(String result);
+
+ abstract Builder message(@Nullable String message);
+
+ abstract GitUpdateFailure build();
+ }
+ }
+}
diff --git a/java/com/google/gerrit/git/LockFailureException.java b/java/com/google/gerrit/git/LockFailureException.java
index 9e67d700a6..371488da3f 100644
--- a/java/com/google/gerrit/git/LockFailureException.java
+++ b/java/com/google/gerrit/git/LockFailureException.java
@@ -14,36 +14,18 @@
package com.google.gerrit.git;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.transport.ReceiveCommand;
/** Thrown when updating a ref in Git fails with LOCK_FAILURE. */
-public class LockFailureException extends IOException {
+public class LockFailureException extends GitUpdateFailureException {
private static final long serialVersionUID = 1L;
- private final ImmutableList<String> refs;
-
public LockFailureException(String message, RefUpdate refUpdate) {
- super(message);
- refs = ImmutableList.of(refUpdate.getName());
+ super(message, refUpdate);
}
public LockFailureException(String message, BatchRefUpdate batchRefUpdate) {
- super(message);
- refs =
- batchRefUpdate.getCommands().stream()
- .filter(c -> c.getResult() == ReceiveCommand.Result.LOCK_FAILURE)
- .map(ReceiveCommand::getRefName)
- .collect(toImmutableList());
- }
-
- /** Subset of ref names that caused the lock failure. */
- public ImmutableList<String> getFailedRefs() {
- return refs;
+ super(message, batchRefUpdate);
}
}
diff --git a/java/com/google/gerrit/git/ObjectIds.java b/java/com/google/gerrit/git/ObjectIds.java
new file mode 100644
index 0000000000..4d8304662c
--- /dev/null
+++ b/java/com/google/gerrit/git/ObjectIds.java
@@ -0,0 +1,131 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.git;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.gerrit.common.Nullable;
+import java.io.IOException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+
+/** Static utilities for working with {@code ObjectId}s. */
+public class ObjectIds {
+ /** Length of a binary SHA-1 byte array. */
+ public static final int LEN = Constants.OBJECT_ID_LENGTH;
+
+ /** Length of a hex SHA-1 string. */
+ public static final int STR_LEN = Constants.OBJECT_ID_STRING_LENGTH;
+
+ /** Default abbreviated length of a hex SHA-1 string. */
+ public static final int ABBREV_STR_LEN = 7;
+
+ /**
+ * Abbreviate an ID's hex string representation to 7 chars.
+ *
+ * @param id object ID.
+ * @return abbreviated hex string representation, exactly 7 chars.
+ */
+ public static String abbreviateName(AnyObjectId id) {
+ return abbreviateName(id, ABBREV_STR_LEN);
+ }
+
+ /**
+ * Abbreviate an ID's hex string representation to {@code n} chars.
+ *
+ * @param id object ID.
+ * @param n number of hex chars, 1 to 40.
+ * @return abbreviated hex string representation, exactly {@code n} chars.
+ */
+ public static String abbreviateName(AnyObjectId id, int n) {
+ checkValidLength(n);
+ return requireNonNull(id).abbreviate(n).name();
+ }
+
+ /**
+ * Abbreviate an ID's hex string representation uniquely to at least 7 chars.
+ *
+ * @param id object ID.
+ * @param reader object reader for determining uniqueness.
+ * @return abbreviated hex string representation, unique according to {@code reader} at least 7
+ * chars.
+ * @throws IOException if an error occurs while looking for ambiguous objects.
+ */
+ public static String abbreviateName(AnyObjectId id, ObjectReader reader) throws IOException {
+ return abbreviateName(id, ABBREV_STR_LEN, reader);
+ }
+
+ /**
+ * Abbreviate an ID's hex string representation uniquely to at least {@code n} chars.
+ *
+ * @param id object ID.
+ * @param n minimum number of hex chars, 1 to 40.
+ * @param reader object reader for determining uniqueness.
+ * @return abbreviated hex string representation, unique according to {@code reader} at least
+ * {@code n} chars.
+ * @throws IOException if an error occurs while looking for ambiguous objects.
+ */
+ public static String abbreviateName(AnyObjectId id, int n, ObjectReader reader)
+ throws IOException {
+ checkValidLength(n);
+ return reader.abbreviate(id, n).name();
+ }
+
+ /**
+ * Copy a nullable ID, preserving null.
+ *
+ * @param id object ID.
+ * @return {@link AnyObjectId#copy} of {@code id}, or {@code null} if {@code id} is null.
+ */
+ @Nullable
+ public static ObjectId copyOrNull(@Nullable AnyObjectId id) {
+ return id != null ? id.copy() : null;
+ }
+
+ /**
+ * Copy a nullable ID, converting null to {@code zeroId}.
+ *
+ * @param id object ID.
+ * @return {@link AnyObjectId#copy} of {@code id}, or {@link ObjectId#zeroId} if {@code id} is
+ * null.
+ */
+ public static ObjectId copyOrZero(@Nullable AnyObjectId id) {
+ return id != null ? id.copy() : ObjectId.zeroId();
+ }
+
+ /**
+ * Return whether the given ID matches the given abbreviation.
+ *
+ * @param id object ID.
+ * @param abbreviation abbreviated hex object ID. May not be null, but may be an invalid hex SHA-1
+ * abbreviation string.
+ * @return true if {@code id} is not null and {@code abbreviation} is a valid hex SHA-1
+ * abbreviation that matches {@code id}, false otherwise.
+ */
+ public static boolean matchesAbbreviation(@Nullable AnyObjectId id, String abbreviation) {
+ requireNonNull(abbreviation);
+ return id != null && id.name().startsWith(abbreviation);
+ }
+
+ private static void checkValidLength(int n) {
+ checkArgument(n > 0);
+ checkArgument(n <= STR_LEN);
+ }
+
+ private ObjectIds() {}
+}
diff --git a/java/com/google/gerrit/git/RefUpdateUtil.java b/java/com/google/gerrit/git/RefUpdateUtil.java
index 5eaf253f1a..bd889623ee 100644
--- a/java/com/google/gerrit/git/RefUpdateUtil.java
+++ b/java/com/google/gerrit/git/RefUpdateUtil.java
@@ -99,7 +99,7 @@ public class RefUpdateUtil {
if (lockFailure + aborted == bru.getCommands().size()) {
throw new LockFailureException("Update aborted with one or more lock failures: " + bru, bru);
} else if (failure > 0) {
- throw new IOException("Update failed: " + bru);
+ throw new GitUpdateFailureException("Update failed: " + bru, bru);
}
}
@@ -130,7 +130,8 @@ public class RefUpdateUtil {
case REJECTED_CURRENT_BRANCH:
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
- throw new IOException("Failed to update " + ru.getName() + ": " + ru.getResult());
+ throw new GitUpdateFailureException(
+ "Failed to update " + ru.getName() + ": " + ru.getResult(), ru);
}
}
@@ -175,7 +176,8 @@ public class RefUpdateUtil {
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
default:
- throw new IOException("Failed to delete " + refName + ": " + ru.getResult());
+ throw new GitUpdateFailureException(
+ "Failed to delete " + refName + ": " + ru.getResult(), ru);
}
}
diff --git a/java/com/google/gerrit/git/testing/BUILD b/java/com/google/gerrit/git/testing/BUILD
index 13fddc1715..fda9aff9f0 100644
--- a/java/com/google/gerrit/git/testing/BUILD
+++ b/java/com/google/gerrit/git/testing/BUILD
@@ -9,7 +9,7 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
"//lib/truth",
"//lib/truth:truth-java8-extension",
],
diff --git a/java/com/google/gerrit/git/testing/CommitSubject.java b/java/com/google/gerrit/git/testing/CommitSubject.java
index 08731072da..41eb45b75d 100644
--- a/java/com/google/gerrit/git/testing/CommitSubject.java
+++ b/java/com/google/gerrit/git/testing/CommitSubject.java
@@ -24,7 +24,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
/** Subject over JGit {@link RevCommit}s. */
-public class CommitSubject extends Subject<CommitSubject, RevCommit> {
+public class CommitSubject extends Subject {
/**
* Constructs a new subject.
@@ -56,8 +56,11 @@ public class CommitSubject extends Subject<CommitSubject, RevCommit> {
commitSubject.hasSha1(expectedSha1);
}
- private CommitSubject(FailureMetadata metadata, RevCommit actual) {
- super(metadata, actual);
+ private final RevCommit commit;
+
+ private CommitSubject(FailureMetadata metadata, RevCommit commit) {
+ super(metadata, commit);
+ this.commit = commit;
}
/**
@@ -67,8 +70,7 @@ public class CommitSubject extends Subject<CommitSubject, RevCommit> {
*/
public void hasCommitMessage(String expectedCommitMessage) {
isNotNull();
- RevCommit commit = actual();
- check("commitMessage()").that(commit.getFullMessage()).isEqualTo(expectedCommitMessage);
+ check("getFullMessage()").that(commit.getFullMessage()).isEqualTo(expectedCommitMessage);
}
/**
@@ -78,7 +80,6 @@ public class CommitSubject extends Subject<CommitSubject, RevCommit> {
*/
public void hasCommitTimestamp(Timestamp expectedCommitTimestamp) {
isNotNull();
- RevCommit commit = actual();
long timestampDiffMs =
Math.abs(commit.getCommitTime() * 1000L - expectedCommitTimestamp.getTime());
check("commitTimestampDiff()").that(timestampDiffMs).isAtMost(SECONDS.toMillis(1));
@@ -91,7 +92,6 @@ public class CommitSubject extends Subject<CommitSubject, RevCommit> {
*/
public void hasSha1(ObjectId expectedSha1) {
isNotNull();
- RevCommit commit = actual();
check("sha1()").that(commit).isEqualTo(expectedSha1);
}
}
diff --git a/java/com/google/gerrit/git/testing/ObjectIdSubject.java b/java/com/google/gerrit/git/testing/ObjectIdSubject.java
index 5fe91f950d..0cfc56396e 100644
--- a/java/com/google/gerrit/git/testing/ObjectIdSubject.java
+++ b/java/com/google/gerrit/git/testing/ObjectIdSubject.java
@@ -20,7 +20,7 @@ import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
import org.eclipse.jgit.lib.ObjectId;
-public class ObjectIdSubject extends Subject<ObjectIdSubject, ObjectId> {
+public class ObjectIdSubject extends Subject {
public static ObjectIdSubject assertThat(ObjectId objectId) {
return assertAbout(objectIds()).that(objectId);
}
@@ -29,13 +29,15 @@ public class ObjectIdSubject extends Subject<ObjectIdSubject, ObjectId> {
return ObjectIdSubject::new;
}
- private ObjectIdSubject(FailureMetadata metadata, ObjectId actual) {
- super(metadata, actual);
+ private final ObjectId objectId;
+
+ private ObjectIdSubject(FailureMetadata metadata, ObjectId objectId) {
+ super(metadata, objectId);
+ this.objectId = objectId;
}
public void hasName(String expectedName) {
isNotNull();
- ObjectId objectId = actual();
- check("name()").that(objectId.getName()).isEqualTo(expectedName);
+ check("getName()").that(objectId.getName()).isEqualTo(expectedName);
}
}
diff --git a/java/com/google/gerrit/git/testing/PushResultSubject.java b/java/com/google/gerrit/git/testing/PushResultSubject.java
index f5c9810bb8..9a46632c30 100644
--- a/java/com/google/gerrit/git/testing/PushResultSubject.java
+++ b/java/com/google/gerrit/git/testing/PushResultSubject.java
@@ -15,7 +15,9 @@
package com.google.gerrit.git.testing;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.truth.Fact.fact;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.git.testing.PushResultSubject.RemoteRefUpdateSubject.refs;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
@@ -26,42 +28,44 @@ import com.google.common.collect.Maps;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.StreamSubject;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.common.Nullable;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate;
-public class PushResultSubject extends Subject<PushResultSubject, PushResult> {
+public class PushResultSubject extends Subject {
public static PushResultSubject assertThat(PushResult actual) {
return assertAbout(PushResultSubject::new).that(actual);
}
- private PushResultSubject(FailureMetadata metadata, PushResult actual) {
- super(metadata, actual);
+ private final PushResult pushResult;
+
+ private PushResultSubject(FailureMetadata metadata, PushResult pushResult) {
+ super(metadata, pushResult);
+ this.pushResult = pushResult;
}
public void hasNoMessages() {
- check("hasNoMessages()")
- .withMessage("expected no messages")
- .that(Strings.nullToEmpty(trimMessages()))
- .isEqualTo("");
+ isNotNull();
+ check("hasNoMessages()").that(Strings.nullToEmpty(getTrimmedMessages())).isEqualTo("");
}
public void hasMessages(String... expectedLines) {
checkArgument(expectedLines.length > 0, "use hasNoMessages()");
isNotNull();
- check("messages()").that(trimMessages()).isEqualTo(String.join("\n", expectedLines));
+ check("getTrimmedMessages()")
+ .that(getTrimmedMessages())
+ .isEqualTo(String.join("\n", expectedLines));
}
public void containsMessages(String... expectedLines) {
checkArgument(expectedLines.length > 0, "use hasNoMessages()");
isNotNull();
- Iterable<String> got = Splitter.on("\n").split(trimMessages());
- check("messages()").that(got).containsAtLeastElementsIn(expectedLines).inOrder();
+ Iterable<String> got = Splitter.on("\n").split(getTrimmedMessages());
+ check("getTrimmedMessages()").that(got).containsAtLeastElementsIn(expectedLines).inOrder();
}
- private String trimMessages() {
- return trimMessages(actual().getMessages());
+ private String getTrimmedMessages() {
+ return trimMessages(pushResult.getMessages());
}
@VisibleForTesting
@@ -78,15 +82,16 @@ public class PushResultSubject extends Subject<PushResultSubject, PushResult> {
}
public void hasProcessed(ImmutableMap<String, Integer> expected) {
+ isNotNull();
ImmutableMap<String, Integer> actual;
- String messages = actual().getMessages();
+ String messages = pushResult.getMessages();
try {
actual = parseProcessed(messages);
} catch (RuntimeException e) {
- Truth.assert_()
- .fail(
- "failed to parse \"Processing changes\" line from messages: %s\n%s",
- messages, Throwables.getStackTraceAsString(e));
+ failWithActual(
+ fact(
+ "failed to parse \"Processing changes\" line from messages, reason:",
+ Throwables.getStackTraceAsString(e)));
return;
}
check("processedCommands()").that(actual).containsExactlyEntriesIn(expected).inOrder();
@@ -119,57 +124,60 @@ public class PushResultSubject extends Subject<PushResultSubject, PushResult> {
}
public RemoteRefUpdateSubject ref(String refName) {
- return assertAbout(
- (FailureMetadata m, RemoteRefUpdate a) -> new RemoteRefUpdateSubject(refName, m, a))
- .that(actual().getRemoteUpdate(refName));
+ isNotNull();
+ return check("getRemoteUpdate(%s)", refName)
+ .about(refs())
+ .that(pushResult.getRemoteUpdate(refName));
}
public RemoteRefUpdateSubject onlyRef(String refName) {
+ isNotNull();
check("setOfRefs()")
.about(StreamSubject.streams())
- .that(actual().getRemoteUpdates().stream().map(RemoteRefUpdate::getRemoteName))
- .named("set of refs")
+ .that(pushResult.getRemoteUpdates().stream().map(RemoteRefUpdate::getRemoteName))
.containsExactly(refName);
return ref(refName);
}
- public static class RemoteRefUpdateSubject
- extends Subject<RemoteRefUpdateSubject, RemoteRefUpdate> {
- private final String refName;
+ public static class RemoteRefUpdateSubject extends Subject {
+ private final RemoteRefUpdate remoteRefUpdate;
+
+ private RemoteRefUpdateSubject(FailureMetadata metadata, RemoteRefUpdate remoteRefUpdate) {
+ super(metadata, remoteRefUpdate);
+ this.remoteRefUpdate = remoteRefUpdate;
+ }
- private RemoteRefUpdateSubject(
- String refName, FailureMetadata metadata, RemoteRefUpdate actual) {
- super(metadata, actual);
- this.refName = refName;
- named("ref update for %s", refName).isNotNull();
+ static Factory<RemoteRefUpdateSubject, RemoteRefUpdate> refs() {
+ return RemoteRefUpdateSubject::new;
}
public void hasStatus(RemoteRefUpdate.Status status) {
- RemoteRefUpdate u = actual();
- Truth.assertThat(u.getStatus())
- .named(
- "status of ref update for %s%s",
- refName, u.getMessage() != null ? ": " + u.getMessage() : "")
+ isNotNull();
+ RemoteRefUpdate u = remoteRefUpdate;
+ check("getStatus()")
+ .withMessage(
+ "status message: %s", u.getMessage() != null ? ": " + u.getMessage() : "<emtpy>")
+ .that(u.getStatus())
.isEqualTo(status);
}
public void hasNoMessage() {
- Truth.assertThat(actual().getMessage())
- .named("message of ref update for %s", refName)
- .isNull();
+ isNotNull();
+ check("getMessage()").that(remoteRefUpdate.getMessage()).isNull();
}
public void hasMessage(String expected) {
- Truth.assertThat(actual().getMessage())
- .named("message of ref update for %s", refName)
- .isEqualTo(expected);
+ isNotNull();
+ check("getMessage()").that(remoteRefUpdate.getMessage()).isEqualTo(expected);
}
public void isOk() {
+ isNotNull();
hasStatus(RemoteRefUpdate.Status.OK);
}
public void isRejected(String expectedMessage) {
+ isNotNull();
hasStatus(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
hasMessage(expectedMessage);
}
diff --git a/java/com/google/gerrit/gpg/BUILD b/java/com/google/gerrit/gpg/BUILD
index f11b9b92bd..9f804c49c5 100644
--- a/java/com/google/gerrit/gpg/BUILD
+++ b/java/com/google/gerrit/gpg/BUILD
@@ -5,18 +5,18 @@ java_library(
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/common:annotations",
- "//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/git",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/api",
"//lib:guava",
+ "//lib:jgit",
"//lib/bouncycastle:bcpg-neverlink",
"//lib/bouncycastle:bcprov-neverlink",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index 9c0885707a..9477cb63f9 100644
--- a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
+++ b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -201,7 +201,7 @@ public class GerritPublicKeyChecker extends PublicKeyChecker {
private Set<String> getAllowedUserIds(IdentifiedUser user) {
Set<String> result = new HashSet<>();
result.addAll(user.getEmailAddresses());
- for (ExternalId extId : user.state().getExternalIds()) {
+ for (ExternalId extId : user.state().externalIds()) {
if (extId.isScheme(SCHEME_GPGKEY)) {
continue; // Omit GPG keys.
}
diff --git a/java/com/google/gerrit/gpg/PublicKeyStore.java b/java/com/google/gerrit/gpg/PublicKeyStore.java
index 7dd01d9a12..519c400730 100644
--- a/java/com/google/gerrit/gpg/PublicKeyStore.java
+++ b/java/com/google/gerrit/gpg/PublicKeyStore.java
@@ -16,10 +16,12 @@ package com.google.gerrit.gpg;
import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.EMPTY_TREE_ID;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.common.base.Preconditions;
import com.google.common.io.ByteStreams;
+import com.google.gerrit.git.ObjectIds;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -45,7 +47,6 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
@@ -75,9 +76,6 @@ import org.eclipse.jgit.util.NB;
* after checking with a {@link PublicKeyChecker}.
*/
public class PublicKeyStore implements AutoCloseable {
- private static final ObjectId EMPTY_TREE =
- ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904");
-
/** Ref where GPG public keys are stored. */
public static final String REFS_GPG_KEYS = "refs/meta/gpg-keys";
@@ -360,7 +358,7 @@ public class PublicKeyStore implements AutoCloseable {
deleteFromNotes(ins, fp);
}
cb.setTreeId(notes.writeTree(ins));
- if (cb.getTreeId().equals(tip != null ? tip.getTree() : EMPTY_TREE)) {
+ if (cb.getTreeId().equals(tip != null ? tip.getTree() : EMPTY_TREE_ID)) {
return RefUpdate.Result.NO_CHANGE;
}
@@ -516,7 +514,7 @@ public class PublicKeyStore implements AutoCloseable {
}
static ObjectId keyObjectId(long keyId) {
- byte[] buf = new byte[Constants.OBJECT_ID_LENGTH];
+ byte[] buf = new byte[ObjectIds.LEN];
NB.encodeInt64(buf, 0, keyId);
return ObjectId.fromRaw(buf);
}
diff --git a/java/com/google/gerrit/gpg/SignedPushModule.java b/java/com/google/gerrit/gpg/SignedPushModule.java
index a05186105d..c11c4e30e4 100644
--- a/java/com/google/gerrit/gpg/SignedPushModule.java
+++ b/java/com/google/gerrit/gpg/SignedPushModule.java
@@ -16,9 +16,9 @@ package com.google.gerrit.gpg;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.EnableSignedPush;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
diff --git a/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java b/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
index b7b03db6ca..652afeab94 100644
--- a/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
+++ b/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
@@ -14,6 +14,8 @@
package com.google.gerrit.gpg.api;
+import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
+
import com.google.gerrit.extensions.api.accounts.GpgKeyApi;
import com.google.gerrit.extensions.api.accounts.GpgKeysInput;
import com.google.gerrit.extensions.common.GpgKeyInfo;
@@ -65,9 +67,11 @@ public class GpgApiAdapterImpl implements GpgApiAdapter {
public Map<String, GpgKeyInfo> listGpgKeys(AccountResource account)
throws RestApiException, GpgException {
try {
- return gpgKeys.get().list().apply(account);
+ return gpgKeys.get().list().apply(account).value();
} catch (PGPException | IOException e) {
throw new GpgException(e);
+ } catch (Exception e) {
+ throw asRestApiException("Cannot list GPG keys", e);
}
}
@@ -79,9 +83,11 @@ public class GpgApiAdapterImpl implements GpgApiAdapter {
in.add = add;
in.delete = delete;
try {
- return postGpgKeys.get().apply(account, in);
+ return postGpgKeys.get().apply(account, in).value();
} catch (PGPException | IOException | ConfigInvalidException e) {
throw new GpgException(e);
+ } catch (Exception e) {
+ throw asRestApiException("Cannot put GPG keys", e);
}
}
diff --git a/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java b/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
index cf09acf470..0ff12e8837 100644
--- a/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
+++ b/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
@@ -14,6 +14,8 @@
package com.google.gerrit.gpg.api;
+import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
+
import com.google.gerrit.extensions.api.accounts.GpgKeyApi;
import com.google.gerrit.extensions.common.GpgKeyInfo;
import com.google.gerrit.extensions.common.Input;
@@ -46,9 +48,9 @@ public class GpgKeyApiImpl implements GpgKeyApi {
@Override
public GpgKeyInfo get() throws RestApiException {
try {
- return get.apply(rsrc);
- } catch (IOException e) {
- throw new RestApiException("Cannot get GPG key", e);
+ return get.apply(rsrc).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get GPG key", e);
}
}
@@ -57,7 +59,7 @@ public class GpgKeyApiImpl implements GpgKeyApi {
try {
delete.apply(rsrc, new Input());
} catch (PGPException | IOException | ConfigInvalidException e) {
- throw new RestApiException("Cannot delete GPG key", e);
+ throw asRestApiException("Cannot delete GPG key", e);
}
}
}
diff --git a/java/com/google/gerrit/gpg/server/DeleteGpgKey.java b/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
index 68bd2d9b4e..1be37f54c9 100644
--- a/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
+++ b/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
@@ -105,7 +105,7 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
} catch (EmailException e) {
logger.atSevere().withCause(e).log(
"Cannot send GPG key deletion message to %s",
- rsrc.getUser().getAccount().getPreferredEmail());
+ rsrc.getUser().getAccount().preferredEmail());
}
break;
case LOCK_FAILURE:
diff --git a/java/com/google/gerrit/gpg/server/GpgKeys.java b/java/com/google/gerrit/gpg/server/GpgKeys.java
index 16592f8b18..b3a2f53e3b 100644
--- a/java/com/google/gerrit/gpg/server/GpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/GpgKeys.java
@@ -27,6 +27,7 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.gpg.BouncyCastleUtil;
@@ -140,7 +141,7 @@ public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
public class ListGpgKeys implements RestReadView<AccountResource> {
@Override
- public Map<String, GpgKeyInfo> apply(AccountResource rsrc)
+ public Response<Map<String, GpgKeyInfo>> apply(AccountResource rsrc)
throws PGPException, IOException, ResourceNotFoundException {
checkVisible(self, rsrc);
Map<String, GpgKeyInfo> keys = new HashMap<>();
@@ -165,7 +166,7 @@ public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
}
}
}
- return keys;
+ return Response.ok(keys);
}
}
@@ -181,12 +182,13 @@ public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
}
@Override
- public GpgKeyInfo apply(GpgKey rsrc) throws IOException {
+ public Response<GpgKeyInfo> apply(GpgKey rsrc) throws IOException {
try (PublicKeyStore store = storeProvider.get()) {
- return toJson(
- rsrc.getKeyRing().getPublicKey(),
- checkerFactory.create().setExpectedUser(rsrc.getUser()),
- store);
+ return Response.ok(
+ toJson(
+ rsrc.getKeyRing().getPublicKey(),
+ checkerFactory.create().setExpectedUser(rsrc.getUser()),
+ store));
}
}
}
diff --git a/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 0bb4ff7434..5396e1c127 100644
--- a/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -18,15 +18,18 @@ import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPGKEY;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Joiner;
+import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.BaseEncoding;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.accounts.GpgKeysInput;
@@ -34,13 +37,15 @@ import com.google.gerrit.extensions.common.GpgKeyInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.gpg.CheckResult;
import com.google.gerrit.gpg.Fingerprint;
import com.google.gerrit.gpg.GerritPublicKeyChecker;
import com.google.gerrit.gpg.PublicKeyChecker;
import com.google.gerrit.gpg.PublicKeyStore;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@@ -53,6 +58,8 @@ import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.mail.send.AddKeySender;
import com.google.gerrit.server.mail.send.DeleteKeySender;
import com.google.gerrit.server.query.account.InternalAccountQuery;
+import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryHelper.ActionType;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -87,6 +94,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
private final Provider<InternalAccountQuery> accountQueryProvider;
private final ExternalIds externalIds;
private final Provider<AccountsUpdate> accountsUpdateProvider;
+ private final RetryHelper retryHelper;
@Inject
PostGpgKeys(
@@ -98,7 +106,8 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
DeleteKeySender.Factory deleteKeySenderFactory,
Provider<InternalAccountQuery> accountQueryProvider,
ExternalIds externalIds,
- @UserInitiated Provider<AccountsUpdate> accountsUpdateProvider) {
+ @UserInitiated Provider<AccountsUpdate> accountsUpdateProvider,
+ RetryHelper retryHelper) {
this.serverIdent = serverIdent;
this.self = self;
this.storeProvider = storeProvider;
@@ -108,12 +117,12 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
this.accountQueryProvider = accountQueryProvider;
this.externalIds = externalIds;
this.accountsUpdateProvider = accountsUpdateProvider;
+ this.retryHelper = retryHelper;
}
@Override
- public Map<String, GpgKeyInfo> apply(AccountResource rsrc, GpgKeysInput input)
- throws ResourceNotFoundException, BadRequestException, ResourceConflictException,
- PGPException, IOException, ConfigInvalidException {
+ public Response<Map<String, GpgKeyInfo>> apply(AccountResource rsrc, GpgKeysInput input)
+ throws RestApiException, PGPException, IOException, ConfigInvalidException {
GpgKeys.checkVisible(self, rsrc);
Collection<ExternalId> existingExtIds =
@@ -129,7 +138,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
ExternalId.Key extIdKey = toExtIdKey(key.getFingerprint());
Account account = getAccountByExternalId(extIdKey);
if (account != null) {
- if (!account.getId().equals(rsrc.getUser().getAccountId())) {
+ if (!account.id().equals(rsrc.getUser().getAccountId())) {
throw new ResourceConflictException("GPG key already associated with another account");
}
} else {
@@ -145,7 +154,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
"Update GPG Keys via API",
rsrc.getUser().getAccountId(),
u -> u.replaceExternalIds(toRemove.keySet(), newExtIds));
- return toJson(newKeys, fingerprintsToRemove, store, rsrc.getUser());
+ return Response.ok(toJson(newKeys, fingerprintsToRemove, store, rsrc.getUser()));
}
}
@@ -196,7 +205,24 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
private void storeKeys(
AccountResource rsrc, List<PGPPublicKeyRing> keyRings, Collection<Fingerprint> toRemove)
- throws BadRequestException, PGPException, IOException {
+ throws RestApiException, PGPException, IOException {
+ try {
+ retryHelper.execute(
+ ActionType.ACCOUNT_UPDATE,
+ () -> tryStoreKeys(rsrc, keyRings, toRemove),
+ LockFailureException.class::isInstance);
+ } catch (Exception e) {
+ Throwables.throwIfUnchecked(e);
+ Throwables.throwIfInstanceOf(e, RestApiException.class);
+ Throwables.throwIfInstanceOf(e, IOException.class);
+ Throwables.throwIfInstanceOf(e, PGPException.class);
+ throw new StorageException(e);
+ }
+ }
+
+ private Void tryStoreKeys(
+ AccountResource rsrc, List<PGPPublicKeyRing> keyRings, Collection<Fingerprint> toRemove)
+ throws RestApiException, PGPException, IOException {
try (PublicKeyStore store = storeProvider.get()) {
List<String> addedKeys = new ArrayList<>();
IdentifiedUser user = rsrc.getUser();
@@ -232,7 +258,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
} catch (EmailException e) {
logger.atSevere().withCause(e).log(
"Cannot send GPG key added message to %s",
- rsrc.getUser().getAccount().getPreferredEmail());
+ rsrc.getUser().getAccount().preferredEmail());
}
}
if (!toRemove.isEmpty()) {
@@ -242,8 +268,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
.send();
} catch (EmailException e) {
logger.atSevere().withCause(e).log(
- "Cannot send GPG key deleted message to %s",
- user.getAccount().getPreferredEmail());
+ "Cannot send GPG key deleted message to %s", user.getAccount().preferredEmail());
}
}
break;
@@ -261,6 +286,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
throw new StorageException(String.format("Failed to save public keys: %s", saveResult));
}
}
+ return null;
}
private ExternalId.Key toExtIdKey(byte[] fp) {
@@ -275,15 +301,15 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
}
if (accountStates.size() > 1) {
- StringBuilder msg = new StringBuilder();
- msg.append("GPG key ")
- .append(extIdKey.get())
- .append(" associated with multiple accounts: ")
- .append(Lists.transform(accountStates, AccountState.ACCOUNT_ID_FUNCTION));
- throw new IllegalStateException(msg.toString());
+ String msg = "GPG key " + extIdKey.get() + " associated with multiple accounts: [";
+ msg =
+ accountStates.stream()
+ .map(a -> a.account().id().toString())
+ .collect(joining(", ", msg, "]"));
+ throw new IllegalStateException(msg);
}
- return accountStates.get(0).getAccount();
+ return accountStates.get(0).account();
}
private Map<String, GpgKeyInfo> toJson(
diff --git a/java/com/google/gerrit/gpg/testing/BUILD b/java/com/google/gerrit/gpg/testing/BUILD
index b227dd5a79..dc390719c0 100644
--- a/java/com/google/gerrit/gpg/testing/BUILD
+++ b/java/com/google/gerrit/gpg/testing/BUILD
@@ -8,7 +8,7 @@ java_library(
deps = [
"//java/com/google/gerrit/gpg",
"//lib:guava",
+ "//lib:jgit",
"//lib/bouncycastle:bcpg-neverlink",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/httpd/AdvertisedObjectsCacheKey.java b/java/com/google/gerrit/httpd/AdvertisedObjectsCacheKey.java
index 68bbd9840d..af8234f72d 100644
--- a/java/com/google/gerrit/httpd/AdvertisedObjectsCacheKey.java
+++ b/java/com/google/gerrit/httpd/AdvertisedObjectsCacheKey.java
@@ -15,8 +15,8 @@
package com.google.gerrit.httpd;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
@AutoValue
abstract class AdvertisedObjectsCacheKey {
diff --git a/java/com/google/gerrit/httpd/BUILD b/java/com/google/gerrit/httpd/BUILD
index 1d4189f4bc..bcb2a2ac68 100644
--- a/java/com/google/gerrit/httpd/BUILD
+++ b/java/com/google/gerrit/httpd/BUILD
@@ -12,14 +12,13 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/exceptions",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/git",
"//java/com/google/gerrit/json",
"//java/com/google/gerrit/launcher",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/audit",
"//java/com/google/gerrit/server/git/receive",
@@ -32,6 +31,8 @@ java_library(
"//lib:args4j",
"//lib:gson",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-servlet",
"//lib:jsch",
"//lib:servlet-api",
"//lib:soy",
@@ -43,7 +44,5 @@ java_library(
"//lib/guice",
"//lib/guice:guice-assistedinject",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit.http.server:jgit-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/httpd/CacheBasedWebSession.java b/java/com/google/gerrit/httpd/CacheBasedWebSession.java
index 177be75926..5c4830c617 100644
--- a/java/com/google/gerrit/httpd/CacheBasedWebSession.java
+++ b/java/com/google/gerrit/httpd/CacheBasedWebSession.java
@@ -19,11 +19,11 @@ import static java.util.concurrent.TimeUnit.HOURS;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.httpd.WebSessionManager.Key;
import com.google.gerrit.httpd.WebSessionManager.Val;
import com.google.gerrit.httpd.restapi.ParameterParser;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
@@ -222,7 +222,7 @@ public abstract class CacheBasedWebSession implements WebSession {
}
private boolean checkAccountStatus(Account.Id id) {
- return byIdCache.get(id).filter(as -> as.getAccount().isActive()).isPresent();
+ return byIdCache.get(id).filter(as -> as.account().isActive()).isPresent();
}
private void saveCookie() {
diff --git a/java/com/google/gerrit/httpd/ContainerAuthFilter.java b/java/com/google/gerrit/httpd/ContainerAuthFilter.java
index d13f2f60f7..517d5dbec3 100644
--- a/java/com/google/gerrit/httpd/ContainerAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ContainerAuthFilter.java
@@ -112,13 +112,13 @@ class ContainerAuthFilter implements Filter {
username = username.toLowerCase(Locale.US);
}
Optional<AccountState> who =
- accountCache.getByUsername(username).filter(a -> a.getAccount().isActive());
+ accountCache.getByUsername(username).filter(a -> a.account().isActive());
if (!who.isPresent()) {
rsp.sendError(SC_UNAUTHORIZED);
return false;
}
WebSession ws = session.get();
- ws.setUserAccountId(who.get().getAccount().getId());
+ ws.setUserAccountId(who.get().account().id());
ws.setAccessPathOk(AccessPath.GIT, true);
ws.setAccessPathOk(AccessPath.REST_API, true);
return true;
diff --git a/java/com/google/gerrit/httpd/DirectChangeByCommit.java b/java/com/google/gerrit/httpd/DirectChangeByCommit.java
index 152a83d1a2..a9be2aa12f 100644
--- a/java/com/google/gerrit/httpd/DirectChangeByCommit.java
+++ b/java/com/google/gerrit/httpd/DirectChangeByCommit.java
@@ -6,11 +6,11 @@ import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.Changes;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -47,7 +47,7 @@ class DirectChangeByCommit extends HttpServlet {
// If exactly one change matches, link to that change.
// TODO Link to a specific patch set, if one matched.
ChangeInfo ci = results.iterator().next();
- token = PageLinks.toChange(new Project.NameKey(ci.project), new Change.Id(ci._number));
+ token = PageLinks.toChange(Project.nameKey(ci.project), Change.id(ci._number));
} else {
// Otherwise, link to the query page.
token = PageLinks.toChangeQuery(query);
diff --git a/java/com/google/gerrit/httpd/GitOverHttpModule.java b/java/com/google/gerrit/httpd/GitOverHttpModule.java
index 8400d60d4b..cbcfb0b1db 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpModule.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpModule.java
@@ -16,7 +16,7 @@ package com.google.gerrit.httpd;
import static com.google.gerrit.httpd.GitOverHttpServlet.URL_REGEX;
-import com.google.gerrit.reviewdb.client.CoreDownloadSchemes;
+import com.google.gerrit.entities.CoreDownloadSchemes;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.DownloadConfig;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index c97ee1048f..eb6b2e089f 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -20,23 +20,27 @@ import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.Capable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.RequestInfo;
+import com.google.gerrit.server.RequestListener;
import com.google.gerrit.server.audit.HttpAuditEvent;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.PermissionAwareRepositoryManager;
+import com.google.gerrit.server.git.TracingHook;
import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.UploadPackInitializer;
+import com.google.gerrit.server.git.UsersSelfAdvertiseRefsHook;
import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.group.GroupAuditService;
+import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -51,6 +55,7 @@ import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import java.io.IOException;
import java.time.Duration;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
@@ -279,7 +284,7 @@ public class GitOverHttpServlet extends GitServlet {
user.setAccessPath(AccessPath.GIT);
try {
- Project.NameKey nameKey = new Project.NameKey(projectName);
+ Project.NameKey nameKey = Project.nameKey(projectName);
ProjectState state = projectCache.checkedGet(nameKey);
if (state == null || !state.statePermitsRead()) {
throw new RepositoryNotFoundException(nameKey.get());
@@ -307,27 +312,38 @@ public class GitOverHttpServlet extends GitServlet {
private final DynamicSet<PreUploadHook> preUploadHooks;
private final DynamicSet<PostUploadHook> postUploadHooks;
private final PluginSetContext<UploadPackInitializer> uploadPackInitializers;
+ private final PermissionBackend permissionBackend;
@Inject
UploadFactory(
TransferConfig tc,
DynamicSet<PreUploadHook> preUploadHooks,
DynamicSet<PostUploadHook> postUploadHooks,
- PluginSetContext<UploadPackInitializer> uploadPackInitializers) {
+ PluginSetContext<UploadPackInitializer> uploadPackInitializers,
+ PermissionBackend permissionBackend) {
this.config = tc;
this.preUploadHooks = preUploadHooks;
this.postUploadHooks = postUploadHooks;
this.uploadPackInitializers = uploadPackInitializers;
+ this.permissionBackend = permissionBackend;
}
@Override
public UploadPack create(HttpServletRequest req, Repository repo) {
- UploadPack up = new UploadPack(repo);
+ ProjectState state = (ProjectState) req.getAttribute(ATT_STATE);
+ UploadPack up =
+ new UploadPack(
+ PermissionAwareRepositoryManager.wrap(
+ repo, permissionBackend.currentUser().project(state.getNameKey())));
up.setPackConfig(config.getPackConfig());
up.setTimeout(config.getTimeout());
up.setPreUploadHook(PreUploadHookChain.newChain(Lists.newArrayList(preUploadHooks)));
up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
- ProjectState state = (ProjectState) req.getAttribute(ATT_STATE);
+ String header = req.getHeader("Git-Protocol");
+ if (header != null) {
+ String[] params = header.split(":");
+ up.setExtraParameters(Arrays.asList(params));
+ }
uploadPackInitializers.runEach(initializer -> initializer.init(state.getNameKey(), up));
return up;
}
@@ -339,6 +355,8 @@ public class GitOverHttpServlet extends GitServlet {
private final Provider<CurrentUser> userProvider;
private final GroupAuditService groupAuditService;
private final Metrics metrics;
+ private final PluginSetContext<RequestListener> requestListeners;
+ private final UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook;
@Inject
UploadFilter(
@@ -346,12 +364,16 @@ public class GitOverHttpServlet extends GitServlet {
PermissionBackend permissionBackend,
Provider<CurrentUser> userProvider,
GroupAuditService groupAuditService,
- Metrics metrics) {
+ Metrics metrics,
+ PluginSetContext<RequestListener> requestListeners,
+ UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook) {
this.uploadValidatorsFactory = uploadValidatorsFactory;
this.permissionBackend = permissionBackend;
this.userProvider = userProvider;
this.groupAuditService = groupAuditService;
this.metrics = metrics;
+ this.requestListeners = requestListeners;
+ this.usersSelfAdvertiseRefsHook = usersSelfAdvertiseRefsHook;
}
@Override
@@ -369,7 +391,14 @@ public class GitOverHttpServlet extends GitServlet {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String sessionId = httpRequest.getSession().getId();
- try {
+ try (TraceContext traceContext = TraceContext.open()) {
+ RequestInfo requestInfo =
+ RequestInfo.builder(
+ RequestInfo.RequestType.GIT_UPLOAD, userProvider.get(), traceContext)
+ .project(state.getNameKey())
+ .build();
+ requestListeners.runEach(l -> l.onRequest(requestInfo));
+
try {
perm.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
@@ -391,8 +420,14 @@ public class GitOverHttpServlet extends GitServlet {
up.setPreUploadHook(
PreUploadHookChain.newChain(
Lists.newArrayList(up.getPreUploadHook(), uploadValidators)));
- up.setAdvertiseRefsHook(new DefaultAdvertiseRefsHook(perm, RefFilterOptions.defaults()));
- next.doFilter(httpRequest, responseWrapper);
+ if (state.isAllUsers()) {
+ up.setAdvertiseRefsHook(usersSelfAdvertiseRefsHook);
+ }
+
+ try (TracingHook tracingHook = new TracingHook()) {
+ up.setProtocolV2Hook(tracingHook);
+ next.doFilter(httpRequest, responseWrapper);
+ }
} finally {
groupAuditService.dispatch(
new HttpAuditEvent(
diff --git a/java/com/google/gerrit/httpd/HttpServletResponseRecorder.java b/java/com/google/gerrit/httpd/HttpServletResponseRecorder.java
index 397d093267..4ae7e5e409 100644
--- a/java/com/google/gerrit/httpd/HttpServletResponseRecorder.java
+++ b/java/com/google/gerrit/httpd/HttpServletResponseRecorder.java
@@ -67,7 +67,7 @@ public class HttpServletResponseRecorder extends HttpServletResponseWrapper {
headers.put(name, value);
}
- @SuppressWarnings("all")
+ @SuppressWarnings({"all", "MissingOverride"})
// @Override is omitted for backwards compatibility with servlet-api 2.5
// TODO: Remove @SuppressWarnings and add @Override when Google upgrades
// to servlet-api 3.1
diff --git a/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java b/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java
index d53a5c52a0..89ad8785d1 100644
--- a/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java
+++ b/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java
@@ -15,9 +15,9 @@
package com.google.gerrit.httpd;
import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.change.ChangesCollection;
@@ -61,7 +61,7 @@ public class NumericChangeIdRedirectServlet extends HttpServlet {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} catch (PermissionBackendException | RuntimeException e) {
- throw new IOException("Unable to lookup change " + id.id, e);
+ throw new IOException("Unable to lookup change " + id.get(), e);
}
String path =
PageLinks.toChange(changeResource.getProject(), changeResource.getChange().getId());
diff --git a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
index eb9d1d7184..e75d8feada 100644
--- a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
@@ -21,9 +21,9 @@ import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException;
@@ -32,6 +32,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.AuthenticationFailedException;
+import com.google.gerrit.server.account.externalids.PasswordVerifier;
import com.google.gerrit.server.auth.AuthenticationUnavailableException;
import com.google.gerrit.server.auth.NoSuchUserException;
import com.google.gerrit.server.config.AuthConfig;
@@ -130,7 +131,7 @@ class ProjectBasicAuthFilter implements Filter {
}
Optional<AccountState> accountState =
- accountCache.getByUsername(username).filter(a -> a.getAccount().isActive());
+ accountCache.getByUsername(username).filter(a -> a.account().isActive());
if (!accountState.isPresent()) {
logger.atWarning().log(
"Authentication failed for %s: account inactive or not provisioned in Gerrit", username);
@@ -142,7 +143,7 @@ class ProjectBasicAuthFilter implements Filter {
GitBasicAuthPolicy gitBasicAuthPolicy = authConfig.getGitBasicAuthPolicy();
if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP
|| gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP) {
- if (who.checkPassword(password, username)) {
+ if (PasswordVerifier.checkPassword(who.externalIds(), username, password)) {
return succeedAuthentication(who);
}
}
@@ -159,7 +160,7 @@ class ProjectBasicAuthFilter implements Filter {
setUserIdentified(whoAuthResult.getAccountId());
return true;
} catch (NoSuchUserException e) {
- if (who.checkPassword(password, username)) {
+ if (PasswordVerifier.checkPassword(who.externalIds(), username, password)) {
return succeedAuthentication(who);
}
logger.atWarning().withCause(e).log(authenticationFailedMsg(username, req));
@@ -183,7 +184,7 @@ class ProjectBasicAuthFilter implements Filter {
}
private boolean succeedAuthentication(AccountState who) {
- setUserIdentified(who.getAccount().getId());
+ setUserIdentified(who.account().id());
return true;
}
diff --git a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
index 4461a52fa1..8300823e7f 100644
--- a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
@@ -22,11 +22,11 @@ import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.auth.oauth.OAuthLoginProvider;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException;
@@ -152,7 +152,7 @@ class ProjectOAuthFilter implements Filter {
}
Optional<AccountState> who =
- accountCache.getByUsername(authInfo.username).filter(a -> a.getAccount().isActive());
+ accountCache.getByUsername(authInfo.username).filter(a -> a.account().isActive());
if (!who.isPresent()) {
logger.atWarning().log(
authenticationFailedMsg(authInfo.username, req)
@@ -161,10 +161,10 @@ class ProjectOAuthFilter implements Filter {
return false;
}
- Account account = who.get().getAccount();
+ Account account = who.get().account();
AuthRequest authRequest = AuthRequest.forExternalUser(authInfo.username);
- authRequest.setEmailAddress(account.getPreferredEmail());
- authRequest.setDisplayName(account.getFullName());
+ authRequest.setEmailAddress(account.preferredEmail());
+ authRequest.setDisplayName(account.fullName());
authRequest.setPassword(authInfo.tokenOrSecret);
authRequest.setAuthPlugin(authInfo.pluginName);
authRequest.setAuthProvider(authInfo.exportName);
diff --git a/java/com/google/gerrit/httpd/RequestMetrics.java b/java/com/google/gerrit/httpd/RequestMetrics.java
index cab4a922a3..e0f9b6ad7f 100644
--- a/java/com/google/gerrit/httpd/RequestMetrics.java
+++ b/java/com/google/gerrit/httpd/RequestMetrics.java
@@ -18,6 +18,7 @@ import com.google.gerrit.metrics.Counter1;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.logging.Metadata;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -28,15 +29,20 @@ public class RequestMetrics {
@Inject
public RequestMetrics(MetricMaker metricMaker) {
+ Field<Integer> statusCodeField =
+ Field.ofInteger("status", Metadata.Builder::httpStatus)
+ .description("HTTP status code")
+ .build();
+
errors =
metricMaker.newCounter(
"http/server/error_count",
new Description("Rate of REST API error responses").setRate().setUnit("errors"),
- Field.ofInteger("status", "HTTP status code"));
+ statusCodeField);
successes =
metricMaker.newCounter(
"http/server/success_count",
new Description("Rate of REST API success responses").setRate().setUnit("successes"),
- Field.ofInteger("status", "HTTP status code"));
+ statusCodeField);
}
}
diff --git a/java/com/google/gerrit/httpd/RunAsFilter.java b/java/com/google/gerrit/httpd/RunAsFilter.java
index 15dbcaba7a..135de42f53 100644
--- a/java/com/google/gerrit/httpd/RunAsFilter.java
+++ b/java/com/google/gerrit/httpd/RunAsFilter.java
@@ -19,10 +19,10 @@ import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.config.AuthConfig;
@@ -105,7 +105,7 @@ class RunAsFilter implements Filter {
Account.Id target;
try {
- target = accountResolver.resolve(runas).asUnique().getAccount().getId();
+ target = accountResolver.resolve(runas).asUnique().account().id();
} catch (UnprocessableEntityException e) {
replyError(req, res, SC_FORBIDDEN, "no account matches " + RUN_AS, null);
return;
diff --git a/java/com/google/gerrit/httpd/UrlModule.java b/java/com/google/gerrit/httpd/UrlModule.java
index 993a0421d8..ac73d222b3 100644
--- a/java/com/google/gerrit/httpd/UrlModule.java
+++ b/java/com/google/gerrit/httpd/UrlModule.java
@@ -18,7 +18,10 @@ import static com.google.inject.Scopes.SINGLETON;
import com.google.common.base.Strings;
import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.AuthType;
+import com.google.gerrit.httpd.raw.AuthorizationCheckServlet;
import com.google.gerrit.httpd.raw.CatServlet;
import com.google.gerrit.httpd.raw.SshInfoServlet;
import com.google.gerrit.httpd.raw.ToolServlet;
@@ -28,8 +31,6 @@ import com.google.gerrit.httpd.restapi.ChangesRestApiServlet;
import com.google.gerrit.httpd.restapi.ConfigRestApiServlet;
import com.google.gerrit.httpd.restapi.GroupsRestApiServlet;
import com.google.gerrit.httpd.restapi.ProjectsRestApiServlet;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.Key;
import com.google.inject.internal.UniqueAnnotations;
@@ -82,6 +83,9 @@ class UrlModule extends ServletModule {
serveRegex("^/(?:a/)?tools/(.*)$").with(ToolServlet.class);
+ // Serve auth check. Mainly used by PolyGerrit for checking if a user is still logged in.
+ serveRegex("^/(?:a/)?auth-check$").with(AuthorizationCheckServlet.class);
+
// Bind servlets for REST root collections.
// The '/plugins/' root collection is already handled by HttpPluginServlet
// which is bound in HttpPluginModule. We cannot bind it here again although
@@ -147,7 +151,7 @@ class UrlModule extends ServletModule {
while (name.endsWith("/")) {
name = name.substring(0, name.length() - 1);
}
- Project.NameKey project = new Project.NameKey(name);
+ Project.NameKey project = Project.nameKey(name);
toGerrit(
PageLinks.toChangeQuery(PageLinks.projectQuery(project, Change.Status.NEW)),
req,
diff --git a/java/com/google/gerrit/httpd/WebSession.java b/java/com/google/gerrit/httpd/WebSession.java
index e476f15c01..e8b54fe7cd 100644
--- a/java/com/google/gerrit/httpd/WebSession.java
+++ b/java/com/google/gerrit/httpd/WebSession.java
@@ -15,7 +15,7 @@
package com.google.gerrit.httpd;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AuthResult;
diff --git a/java/com/google/gerrit/httpd/WebSessionManager.java b/java/com/google/gerrit/httpd/WebSessionManager.java
index d09b4dd44b..c0900ecbce 100644
--- a/java/com/google/gerrit/httpd/WebSessionManager.java
+++ b/java/com/google/gerrit/httpd/WebSessionManager.java
@@ -30,7 +30,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.cache.Cache;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -286,7 +286,7 @@ public class WebSessionManager {
case 0:
break PARSE;
case 1:
- accountId = new Account.Id(readVarInt32(in));
+ accountId = Account.id(readVarInt32(in));
continue;
case 2:
refreshCookieAt = readFixInt64(in);
diff --git a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index 552e667946..97bb44b000 100644
--- a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -18,12 +18,12 @@ import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USE
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_UUID;
import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.httpd.LoginUrlToken;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.httpd.template.SiteHeaderFooter;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
@@ -153,20 +153,20 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
if (!accountState.isPresent()) {
continue;
}
- Account account = accountState.get().getAccount();
+ Account account = accountState.get().account();
String displayName;
- if (accountState.get().getUserName().isPresent()) {
- displayName = accountState.get().getUserName().get();
- } else if (account.getFullName() != null && !account.getFullName().isEmpty()) {
- displayName = account.getFullName();
- } else if (account.getPreferredEmail() != null) {
- displayName = account.getPreferredEmail();
+ if (accountState.get().userName().isPresent()) {
+ displayName = accountState.get().userName().get();
+ } else if (account.fullName() != null && !account.fullName().isEmpty()) {
+ displayName = account.fullName();
+ } else if (account.preferredEmail() != null) {
+ displayName = account.preferredEmail();
} else {
displayName = accountId.toString();
}
Element linkElement = doc.createElement("a");
- linkElement.setAttribute("href", "?account_id=" + account.getId().toString());
+ linkElement.setAttribute("href", "?account_id=" + account.id().toString());
linkElement.setTextContent(displayName);
userlistElement.appendChild(linkElement);
userlistElement.appendChild(doc.createElement("br"));
@@ -176,7 +176,7 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
}
private Optional<AuthResult> auth(Optional<AccountState> account) {
- return account.map(a -> new AuthResult(a.getAccount().getId(), null, false));
+ return account.map(a -> new AuthResult(a.account().id(), null, false));
}
private AuthResult auth(Account.Id account) {
@@ -196,7 +196,7 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
getServletContext().log("Multiple accounts with username " + userName + " found");
return null;
}
- return auth(accountStates.get(0).getAccount().getId());
+ return auth(accountStates.get(0).account().id());
}
private Optional<AuthResult> byPreferredEmail(String email) {
diff --git a/java/com/google/gerrit/httpd/auth/oauth/BUILD b/java/com/google/gerrit/httpd/auth/oauth/BUILD
index 2d9345ffc3..dd4549eacb 100644
--- a/java/com/google/gerrit/httpd/auth/oauth/BUILD
+++ b/java/com/google/gerrit/httpd/auth/oauth/BUILD
@@ -8,18 +8,17 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:annotations",
- "//java/com/google/gerrit/exceptions",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/httpd",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//lib:gson",
"//lib:guava",
+ "//lib:jgit",
"//lib:servlet-api",
"//lib/commons:codec",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java b/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
index 0c8a1a10f3..fcaef5e833 100644
--- a/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
+++ b/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
@@ -19,6 +19,7 @@ import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
import com.google.gerrit.extensions.auth.oauth.OAuthToken;
import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
@@ -27,7 +28,6 @@ import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.httpd.CanonicalWebUrl;
import com.google.gerrit.httpd.WebSession;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
@@ -237,7 +237,7 @@ class OAuthSession {
try {
return SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
- throw new IllegalArgumentException("No SecureRandom available for GitHub authentication", e);
+ throw new IllegalStateException("No SecureRandom available for GitHub authentication", e);
}
}
diff --git a/java/com/google/gerrit/httpd/auth/openid/BUILD b/java/com/google/gerrit/httpd/auth/openid/BUILD
index 2206397619..94f436b4d2 100644
--- a/java/com/google/gerrit/httpd/auth/openid/BUILD
+++ b/java/com/google/gerrit/httpd/auth/openid/BUILD
@@ -10,20 +10,18 @@ java_library(
# We want all these deps to be provided_deps
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/httpd",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/util/http",
"//java/com/google/gerrit/server",
"//lib:guava",
- "//java/com/google/gwtorm",
"//lib:servlet-api",
"//lib/commons:codec",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
"//lib/openid:consumer",
],
)
diff --git a/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java b/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
index 08f2d52b57..37250b4331 100644
--- a/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
+++ b/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
@@ -18,6 +18,7 @@ import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
import com.google.gerrit.extensions.auth.oauth.OAuthToken;
import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
@@ -27,7 +28,6 @@ import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.httpd.CanonicalWebUrl;
import com.google.gerrit.httpd.LoginUrlToken;
import com.google.gerrit.httpd.WebSession;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
@@ -222,7 +222,7 @@ class OAuthSessionOverOpenID {
try {
return SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
- throw new IllegalArgumentException("No SecureRandom available for GitHub authentication", e);
+ throw new IllegalStateException("No SecureRandom available for GitHub authentication", e);
}
}
diff --git a/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java b/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
index 8c3dc10c9b..be975c5e99 100644
--- a/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
+++ b/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
@@ -17,12 +17,13 @@ package com.google.gerrit.httpd.auth.openid;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.auth.openid.OpenIdUrls;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.KeyUtil;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.httpd.CanonicalWebUrl;
import com.google.gerrit.httpd.ProxyProperties;
import com.google.gerrit.httpd.WebSession;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.UrlEncoded;
import com.google.gerrit.server.account.AccountException;
@@ -32,7 +33,6 @@ import com.google.gerrit.server.auth.openid.OpenIdProviderPattern;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gwtorm.client.KeyUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java b/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java
index b438c007f7..4fabb18928 100644
--- a/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java
+++ b/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java
@@ -36,10 +36,10 @@ import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -174,10 +174,8 @@ class GitwebServlet extends HttpServlet {
}
Path myconf = Files.createTempFile(site.tmp_dir, "gitweb_config", ".perl");
- // To make our configuration file only readable or writable by us;
- // this reduces the chances of someone tampering with the file.
- //
- // TODO(dborowitz): Is there a portable way to do this with NIO?
+ // To make our configuration file only readable or writable by us; this reduces the chances of
+ // someone tampering with the file.
File myconfFile = myconf.toFile();
myconfFile.setWritable(false, false /* all */);
myconfFile.setReadable(false, false /* all */);
@@ -414,7 +412,7 @@ class GitwebServlet extends HttpServlet {
name = name.substring(0, name.length() - 4);
}
- Project.NameKey nameKey = new Project.NameKey(name);
+ Project.NameKey nameKey = Project.nameKey(name);
ProjectState projectState;
try {
projectState = projectCache.checkedGet(nameKey);
diff --git a/java/com/google/gerrit/httpd/init/BUILD b/java/com/google/gerrit/httpd/init/BUILD
index 97475d1503..222041a39f 100644
--- a/java/com/google/gerrit/httpd/init/BUILD
+++ b/java/com/google/gerrit/httpd/init/BUILD
@@ -11,6 +11,7 @@ java_library(
"//java/com/google/gerrit/httpd",
"//java/com/google/gerrit/httpd/auth/oauth",
"//java/com/google/gerrit/httpd/auth/openid",
+ "//java/com/google/gerrit/index",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/lucene",
"//java/com/google/gerrit/metrics/dropwizard",
@@ -26,10 +27,10 @@ java_library(
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/sshd",
"//lib:guava",
+ "//lib:jgit",
"//lib:servlet-api",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index bf5cd2a9b8..0befbd355a 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -38,7 +38,9 @@ import com.google.gerrit.httpd.auth.oauth.OAuthModule;
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.httpd.plugins.HttpPluginModule;
import com.google.gerrit.httpd.raw.StaticModule;
+import com.google.gerrit.index.IndexType;
import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.lucene.LuceneIndexModule;
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
import com.google.gerrit.pgm.util.LogFileCompressor;
@@ -72,9 +74,9 @@ import com.google.gerrit.server.events.StreamEventsApiListener;
import com.google.gerrit.server.git.GarbageCollectionModule;
import com.google.gerrit.server.git.GitRepositoryManagerModule;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
+import com.google.gerrit.server.git.SystemReaderInstaller;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.index.OnlineUpgrader;
import com.google.gerrit.server.index.VersionManager;
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
@@ -261,6 +263,13 @@ public class WebAppInitializer extends GuiceServletContextListener implements Fi
Module configModule = new GerritServerConfigModule();
modules.add(configModule);
+ modules.add(
+ new LifecycleModule() {
+ @Override
+ protected void configure() {
+ listener().to(SystemReaderInstaller.class);
+ }
+ });
modules.add(new DropWizardMetricMaker.ApiModule());
return Guice.createInjector(PRODUCTION, modules);
}
@@ -342,13 +351,12 @@ public class WebAppInitializer extends GuiceServletContextListener implements Fi
}
private Module createIndexModule() {
- switch (indexType) {
- case LUCENE:
- return LuceneIndexModule.latestVersion(false);
- case ELASTICSEARCH:
- return ElasticIndexModule.latestVersion(false);
- default:
- throw new IllegalStateException("unsupported index.type = " + indexType);
+ if (indexType.isLucene()) {
+ return LuceneIndexModule.latestVersion(false);
+ } else if (indexType.isElasticsearch()) {
+ return ElasticIndexModule.latestVersion(false);
+ } else {
+ throw new IllegalStateException("unsupported index.type = " + indexType);
}
}
diff --git a/java/com/google/gerrit/httpd/raw/AuthorizationCheckServlet.java b/java/com/google/gerrit/httpd/raw/AuthorizationCheckServlet.java
new file mode 100644
index 0000000000..e8f173c2ac
--- /dev/null
+++ b/java/com/google/gerrit/httpd/raw/AuthorizationCheckServlet.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd.raw;
+
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.util.http.CacheHeaders;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Offers a dedicated endpoint for checking if a user is still logged in. Returns {@code 204
+ * NO_CONTENT} for logged-in users, {@code 403 FORBIDDEN} otherwise.
+ *
+ * <p>Mainly used by PolyGerrit to check if a user is still logged in.
+ */
+@Singleton
+public class AuthorizationCheckServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+ private final Provider<CurrentUser> user;
+
+ @Inject
+ AuthorizationCheckServlet(Provider<CurrentUser> user) {
+ this.user = user;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
+ CacheHeaders.setNotCacheable(res);
+ if (user.get().isIdentifiedUser()) {
+ res.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ } else {
+ res.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/httpd/raw/CatServlet.java b/java/com/google/gerrit/httpd/raw/CatServlet.java
index 1d0e7d8bb1..a29521332d 100644
--- a/java/com/google/gerrit/httpd/raw/CatServlet.java
+++ b/java/com/google/gerrit/httpd/raw/CatServlet.java
@@ -14,12 +14,12 @@
package com.google.gerrit.httpd.raw;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
@@ -118,13 +118,13 @@ public class CatServlet extends HttpServlet {
}
}
- final Change.Id changeId = patchKey.getParentKey().getParentKey();
+ final Change.Id changeId = patchKey.patchSetId().changeId();
String revision;
try {
ChangeNotes notes = changeNotesFactory.createChecked(changeId);
permissionBackend.currentUser().change(notes).check(ChangePermission.READ);
projectCache.checkedGet(notes.getProjectName()).checkStatePermitsRead();
- if (patchKey.getParentKey().get() == 0) {
+ if (patchKey.patchSetId().get() == 0) {
// change edit
Optional<ChangeEdit> edit = changeEditUtil.byChange(notes);
if (edit.isPresent()) {
@@ -134,12 +134,12 @@ public class CatServlet extends HttpServlet {
return;
}
} else {
- PatchSet patchSet = psUtil.get(notes, patchKey.getParentKey());
+ PatchSet patchSet = psUtil.get(notes, patchKey.patchSetId());
if (patchSet == null) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
- revision = patchSet.getRevision().get();
+ revision = patchSet.commitId().name();
}
} catch (ResourceConflictException | NoSuchChangeException | AuthException e) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
@@ -150,7 +150,7 @@ public class CatServlet extends HttpServlet {
return;
}
- String path = patchKey.getFileName();
+ String path = patchKey.fileName();
String restUrl =
String.format(
"%s/changes/%d/revisions/%s/files/%s/download?parent=%d",
diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
new file mode 100644
index 0000000000..8d81d62464
--- /dev/null
+++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
@@ -0,0 +1,148 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd.raw;
+
+import static com.google.template.soy.data.ordainers.GsonOrdainer.serializeObject;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.common.UsedAt.Project;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.accounts.AccountApi;
+import com.google.gerrit.extensions.api.config.Server;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.json.OutputFormat;
+import com.google.gson.Gson;
+import com.google.template.soy.data.SanitizedContent;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+/** Helper for generating parts of {@code index.html}. */
+public class IndexHtmlUtil {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ /**
+ * Returns both static and dynamic parameters of {@code index.html}. The result is to be used when
+ * rendering the soy template.
+ */
+ public static ImmutableMap<String, Object> templateData(
+ GerritApi gerritApi,
+ String canonicalURL,
+ String cdnPath,
+ String faviconPath,
+ Map<String, String[]> urlParameterMap,
+ Function<String, SanitizedContent> urlInScriptTagOrdainer)
+ throws URISyntaxException, RestApiException {
+ return ImmutableMap.<String, Object>builder()
+ .putAll(
+ staticTemplateData(
+ canonicalURL, cdnPath, faviconPath, urlParameterMap, urlInScriptTagOrdainer))
+ .putAll(dynamicTemplateData(gerritApi))
+ .build();
+ }
+
+ /** Returns dynamic parameters of {@code index.html}. */
+ @UsedAt(Project.GOOGLE)
+ public static Map<String, Map<String, SanitizedContent>> dynamicTemplateData(GerritApi gerritApi)
+ throws RestApiException {
+ Gson gson = OutputFormat.JSON_COMPACT.newGson();
+ Map<String, SanitizedContent> initialData = new HashMap<>();
+ Server serverApi = gerritApi.config().server();
+ initialData.put("\"/config/server/info\"", serializeObject(gson, serverApi.getInfo()));
+ initialData.put("\"/config/server/version\"", serializeObject(gson, serverApi.getVersion()));
+ initialData.put("\"/config/server/top-menus\"", serializeObject(gson, serverApi.topMenus()));
+
+ try {
+ AccountApi accountApi = gerritApi.accounts().self();
+ initialData.put("\"/accounts/self/detail\"", serializeObject(gson, accountApi.get()));
+ initialData.put(
+ "\"/accounts/self/preferences\"", serializeObject(gson, accountApi.getPreferences()));
+ initialData.put(
+ "\"/accounts/self/preferences.diff\"",
+ serializeObject(gson, accountApi.getDiffPreferences()));
+ initialData.put(
+ "\"/accounts/self/preferences.edit\"",
+ serializeObject(gson, accountApi.getEditPreferences()));
+ } catch (AuthException e) {
+ logger.atFine().log("Can't inline account-related data because user is unauthenticated");
+ // Don't render data
+ // TODO(hiesel): Tell the client that the user is not authenticated so that it doesn't have to
+ // fetch anyway. This requires more client side modifications.
+ }
+ return ImmutableMap.of("gerritInitialData", initialData);
+ }
+
+ /** Returns all static parameters of {@code index.html}. */
+ static Map<String, Object> staticTemplateData(
+ String canonicalURL,
+ String cdnPath,
+ String faviconPath,
+ Map<String, String[]> urlParameterMap,
+ Function<String, SanitizedContent> urlInScriptTagOrdainer)
+ throws URISyntaxException {
+ String canonicalPath = computeCanonicalPath(canonicalURL);
+
+ String staticPath = "";
+ if (cdnPath != null) {
+ staticPath = cdnPath;
+ } else if (canonicalPath != null) {
+ staticPath = canonicalPath;
+ }
+
+ SanitizedContent sanitizedStaticPath = urlInScriptTagOrdainer.apply(staticPath);
+ ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
+
+ if (canonicalPath != null) {
+ data.put("canonicalPath", canonicalPath);
+ }
+ if (sanitizedStaticPath != null) {
+ data.put("staticResourcePath", sanitizedStaticPath);
+ }
+ if (faviconPath != null) {
+ data.put("faviconPath", faviconPath);
+ }
+ if (urlParameterMap.containsKey("ce")) {
+ data.put("polyfillCE", "true");
+ }
+ if (urlParameterMap.containsKey("sd")) {
+ data.put("polyfillSD", "true");
+ }
+ if (urlParameterMap.containsKey("sc")) {
+ data.put("polyfillSC", "true");
+ }
+ return data.build();
+ }
+
+ private static String computeCanonicalPath(@Nullable String canonicalURL)
+ throws URISyntaxException {
+ if (Strings.isNullOrEmpty(canonicalURL)) {
+ return "";
+ }
+
+ // If we serving from a sub-directory rather than root, determine the path
+ // from the cannonical web URL.
+ URI uri = new URI(canonicalURL);
+ return uri.getPath().replaceAll("/$", "");
+ }
+
+ private IndexHtmlUtil() {}
+}
diff --git a/java/com/google/gerrit/httpd/raw/IndexServlet.java b/java/com/google/gerrit/httpd/raw/IndexServlet.java
index b6594bcb10..a0b41b2155 100644
--- a/java/com/google/gerrit/httpd/raw/IndexServlet.java
+++ b/java/com/google/gerrit/httpd/raw/IndexServlet.java
@@ -17,91 +17,73 @@ package com.google.gerrit.httpd.raw;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_OK;
-import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Resources;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.template.soy.SoyFileSet;
import com.google.template.soy.data.SanitizedContent;
import com.google.template.soy.data.UnsafeSanitizedContentOrdainer;
-import com.google.template.soy.tofu.SoyTofu;
+import com.google.template.soy.jbcsrc.api.SoySauce;
import java.io.IOException;
import java.io.OutputStream;
-import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
+import java.util.function.Function;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class IndexServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
- protected final byte[] indexSource;
+
+ @Nullable private final String canonicalUrl;
+ @Nullable private final String cdnPath;
+ @Nullable private final String faviconPath;
+ private final GerritApi gerritApi;
+ private final SoySauce soySauce;
+ private final Function<String, SanitizedContent> urlOrdainer;
IndexServlet(
- @Nullable String canonicalURL, @Nullable String cdnPath, @Nullable String faviconPath)
- throws URISyntaxException {
- String resourcePath = "com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy";
- SoyFileSet.Builder builder = SoyFileSet.builder();
- builder.add(Resources.getResource(resourcePath));
- SoyTofu.Renderer renderer =
- builder
+ @Nullable String canonicalUrl,
+ @Nullable String cdnPath,
+ @Nullable String faviconPath,
+ GerritApi gerritApi) {
+ this.canonicalUrl = canonicalUrl;
+ this.cdnPath = cdnPath;
+ this.faviconPath = faviconPath;
+ this.gerritApi = gerritApi;
+ this.soySauce =
+ SoyFileSet.builder()
+ .add(Resources.getResource("com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy"))
.build()
- .compileToTofu()
- .newRenderer("com.google.gerrit.httpd.raw.Index")
- .setContentKind(SanitizedContent.ContentKind.HTML)
- .setData(getTemplateData(canonicalURL, cdnPath, faviconPath));
- indexSource = renderer.render().getBytes(UTF_8);
+ .compileTemplates();
+ this.urlOrdainer =
+ (s) ->
+ UnsafeSanitizedContentOrdainer.ordainAsSafe(
+ s, SanitizedContent.ContentKind.TRUSTED_RESOURCE_URI);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
+ SoySauce.Renderer renderer;
+ try {
+ Map<String, String[]> parameterMap = req.getParameterMap();
+ // TODO(hiesel): Remove URL ordainer as parameter once Soy is consistent
+ ImmutableMap<String, Object> templateData =
+ IndexHtmlUtil.templateData(
+ gerritApi, canonicalUrl, cdnPath, faviconPath, parameterMap, urlOrdainer);
+ renderer = soySauce.renderTemplate("com.google.gerrit.httpd.raw.Index").setData(templateData);
+ } catch (URISyntaxException | RestApiException e) {
+ throw new IOException(e);
+ }
+
rsp.setCharacterEncoding(UTF_8.name());
rsp.setContentType("text/html");
rsp.setStatus(SC_OK);
try (OutputStream w = rsp.getOutputStream()) {
- w.write(indexSource);
- }
- }
-
- static String computeCanonicalPath(@Nullable String canonicalURL) throws URISyntaxException {
- if (Strings.isNullOrEmpty(canonicalURL)) {
- return "";
- }
-
- // If we serving from a sub-directory rather than root, determine the path
- // from the cannonical web URL.
- URI uri = new URI(canonicalURL);
- return uri.getPath().replaceAll("/$", "");
- }
-
- static Map<String, Object> getTemplateData(
- String canonicalURL, String cdnPath, String faviconPath) throws URISyntaxException {
- String canonicalPath = computeCanonicalPath(canonicalURL);
-
- String staticPath = "";
- if (cdnPath != null) {
- staticPath = cdnPath;
- } else if (canonicalPath != null) {
- staticPath = canonicalPath;
- }
-
- // The resource path must be typed as safe for use in a script src.
- // TODO(wyatta): Upgrade this to use an appropriate safe URL type.
- SanitizedContent sanitizedStaticPath =
- UnsafeSanitizedContentOrdainer.ordainAsSafe(
- staticPath, SanitizedContent.ContentKind.TRUSTED_RESOURCE_URI);
-
- ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
- if (canonicalPath != null) {
- data.put("canonicalPath", canonicalPath);
- }
- if (sanitizedStaticPath != null) {
- data.put("staticResourcePath", sanitizedStaticPath);
- }
- if (faviconPath != null) {
- data.put("faviconPath", faviconPath);
+ w.write(renderer.renderHtml().get().toString().getBytes(UTF_8));
}
- return data.build();
}
}
diff --git a/java/com/google/gerrit/httpd/raw/StaticModule.java b/java/com/google/gerrit/httpd/raw/StaticModule.java
index 06ac886079..0d4c67e8ee 100644
--- a/java/com/google/gerrit/httpd/raw/StaticModule.java
+++ b/java/com/google/gerrit/httpd/raw/StaticModule.java
@@ -22,6 +22,7 @@ import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.httpd.XsrfCookieFilter;
import com.google.gerrit.httpd.raw.ResourceServlet.Resource;
import com.google.gerrit.launcher.GerritLauncher;
@@ -41,7 +42,6 @@ import com.google.inject.servlet.ServletModule;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import javax.servlet.Filter;
@@ -78,11 +78,6 @@ public class StaticModule extends ServletModule {
"/groups/self",
"/settings/*",
"/Documentation/q/*");
- // TODO(dborowitz): These fragments conflict with the REST API
- // namespace, so they will need to use a different path.
- // "/groups/*",
- // "/projects/*");
- //
/**
* Paths that should be treated as static assets when serving PolyGerrit.
@@ -224,11 +219,12 @@ public class StaticModule extends ServletModule {
@Singleton
@Named(POLYGERRIT_INDEX_SERVLET)
HttpServlet getPolyGerritUiIndexServlet(
- @CanonicalWebUrl @Nullable String canonicalUrl, @GerritServerConfig Config cfg)
- throws URISyntaxException {
+ @CanonicalWebUrl @Nullable String canonicalUrl,
+ @GerritServerConfig Config cfg,
+ GerritApi gerritApi) {
String cdnPath = cfg.getString("gerrit", null, "cdnPath");
String faviconPath = cfg.getString("gerrit", null, "faviconPath");
- return new IndexServlet(canonicalUrl, cdnPath, faviconPath);
+ return new IndexServlet(canonicalUrl, cdnPath, faviconPath, gerritApi);
}
@Provides
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiMetrics.java b/java/com/google/gerrit/httpd/restapi/RestApiMetrics.java
index 562687b1d5..fc099a6b7d 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiMetrics.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiMetrics.java
@@ -25,6 +25,7 @@ import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Histogram1;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.server.logging.Metadata;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -41,19 +42,24 @@ public class RestApiMetrics {
@Inject
RestApiMetrics(MetricMaker metrics) {
- Field<String> view = Field.ofString("view", "view implementation class");
+ Field<String> viewField =
+ Field.ofString("view", Metadata.Builder::className)
+ .description("view implementation class")
+ .build();
count =
metrics.newCounter(
"http/server/rest_api/count",
new Description("REST API calls by view").setRate(),
- view);
+ viewField);
errorCount =
metrics.newCounter(
"http/server/rest_api/error_count",
new Description("REST API errors by view").setRate(),
- view,
- Field.ofInteger("error_code", "HTTP status code"));
+ viewField,
+ Field.ofInteger("error_code", Metadata.Builder::httpStatus)
+ .description("HTTP status code")
+ .build());
serverLatency =
metrics.newTimer(
@@ -61,7 +67,7 @@ public class RestApiMetrics {
new Description("REST API call latency by view")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- view);
+ viewField);
responseBytes =
metrics.newHistogram(
@@ -69,7 +75,7 @@ public class RestApiMetrics {
new Description("Size of response on network (may be gzip compressed)")
.setCumulative()
.setUnit(Units.BYTES),
- view);
+ viewField);
}
String view(ViewData viewData) {
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index c45a1189fb..88c5106e67 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -34,17 +34,14 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
-import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
-import static javax.servlet.http.HttpServletResponse.SC_CREATED;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_NOT_IMPLEMENTED;
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
-import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED;
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
@@ -67,8 +64,10 @@ import com.google.common.math.IntMath;
import com.google.common.net.HttpHeaders;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.RawInputUtil;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -105,16 +104,25 @@ import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.OptionUtil;
+import com.google.gerrit.server.RequestInfo;
+import com.google.gerrit.server.RequestListener;
import com.google.gerrit.server.audit.ExtendedHttpAuditEvent;
import com.google.gerrit.server.cache.PerThreadCache;
+import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.group.GroupAuditService;
+import com.google.gerrit.server.logging.PerformanceLogContext;
+import com.google.gerrit.server.logging.PerformanceLogger;
import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.quota.QuotaException;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.gerrit.server.restapi.project.ProjectsCollection;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.util.http.CacheHeaders;
@@ -158,6 +166,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
@@ -186,9 +195,6 @@ public class RestApiServlet extends HttpServlet {
@VisibleForTesting public static final String X_GERRIT_TRACE = "X-Gerrit-Trace";
- // HTTP 422 Unprocessable Entity.
- // TODO: Remove when HttpServletResponse.SC_UNPROCESSABLE_ENTITY is available
- private static final int SC_UNPROCESSABLE_ENTITY = 422;
private static final String X_REQUESTED_WITH = "X-Requested-With";
private static final String X_GERRIT_AUTH = "X-Gerrit-Auth";
static final ImmutableSet<String> ALLOWED_CORS_METHODS =
@@ -201,6 +207,8 @@ public class RestApiServlet extends HttpServlet {
public static final String XD_AUTHORIZATION = "access_token";
public static final String XD_CONTENT_TYPE = "$ct";
public static final String XD_METHOD = "$m";
+ public static final int SC_UNPROCESSABLE_ENTITY = 422;
+ public static final int SC_TOO_MANY_REQUESTS = 429;
private static final int HEAP_EST_SIZE = 10 * 8 * 1024; // Presize 10 blocks.
private static final String PLAIN_TEXT = "text/plain";
@@ -224,30 +232,41 @@ public class RestApiServlet extends HttpServlet {
final Provider<CurrentUser> currentUser;
final DynamicItem<WebSession> webSession;
final Provider<ParameterParser> paramParser;
+ final PluginSetContext<RequestListener> requestListeners;
final PermissionBackend permissionBackend;
final GroupAuditService auditService;
final RestApiMetrics metrics;
final Pattern allowOrigin;
final RestApiQuotaEnforcer quotaChecker;
+ final Config config;
+ final DynamicSet<PerformanceLogger> performanceLoggers;
+ final ChangeFinder changeFinder;
@Inject
Globals(
Provider<CurrentUser> currentUser,
DynamicItem<WebSession> webSession,
Provider<ParameterParser> paramParser,
+ PluginSetContext<RequestListener> requestListeners,
PermissionBackend permissionBackend,
GroupAuditService auditService,
RestApiMetrics metrics,
RestApiQuotaEnforcer quotaChecker,
- @GerritServerConfig Config cfg) {
+ @GerritServerConfig Config config,
+ DynamicSet<PerformanceLogger> performanceLoggers,
+ ChangeFinder changeFinder) {
this.currentUser = currentUser;
this.webSession = webSession;
this.paramParser = paramParser;
+ this.requestListeners = requestListeners;
this.permissionBackend = permissionBackend;
this.auditService = auditService;
this.metrics = metrics;
this.quotaChecker = quotaChecker;
- allowOrigin = makeAllowOrigin(cfg);
+ this.config = config;
+ this.performanceLoggers = performanceLoggers;
+ this.changeFinder = changeFinder;
+ allowOrigin = makeAllowOrigin(config);
}
private static Pattern makeAllowOrigin(Config cfg) {
@@ -261,6 +280,7 @@ public class RestApiServlet extends HttpServlet {
private final Globals globals;
private final Provider<RestCollection<RestResource, RestResource>> members;
+ private Optional<String> traceId = Optional.empty();
public RestApiServlet(
Globals globals, RestCollection<? extends RestResource, ? extends RestResource> members) {
@@ -286,272 +306,288 @@ public class RestApiServlet extends HttpServlet {
res.setHeader("X-Content-Type-Options", "nosniff");
int status = SC_OK;
long responseBytes = -1;
- Object result = null;
+ Response<?> response = null;
QueryParams qp = null;
Object inputRequestBody = null;
RestResource rsrc = TopLevelResource.INSTANCE;
ViewData viewData = null;
try (TraceContext traceContext = enableTracing(req, res)) {
- try (PerThreadCache ignored = PerThreadCache.create()) {
- logger.atFinest().log(
- "Received REST request: %s %s (parameters: %s)",
- req.getMethod(), req.getRequestURI(), getParameterNames(req));
- logger.atFinest().log("Calling user: %s", globals.currentUser.get().getLoggableName());
-
- if (isCorsPreflight(req)) {
- doCorsPreflight(req, res);
- return;
- }
-
- qp = ParameterParser.getQueryParams(req);
- checkCors(req, res, qp.hasXdOverride());
- if (qp.hasXdOverride()) {
- req = applyXdOverrides(req, qp);
- }
- checkUserSession(req);
+ List<IdString> path = splitPath(req);
- List<IdString> path = splitPath(req);
- RestCollection<RestResource, RestResource> rc = members.get();
- globals
- .permissionBackend
- .currentUser()
- .checkAny(GlobalPermission.fromAnnotation(rc.getClass()));
+ RequestInfo requestInfo = createRequestInfo(traceContext, requestUri(req), path);
+ globals.requestListeners.runEach(l -> l.onRequest(requestInfo));
- viewData = new ViewData(null, null);
-
- if (path.isEmpty()) {
- globals.quotaChecker.enforce(req);
- if (rc instanceof NeedsParams) {
- ((NeedsParams) rc).setParams(qp.params());
+ try (PerThreadCache ignored = PerThreadCache.create()) {
+ // It's important that the PerformanceLogContext is closed before the response is sent to
+ // the client. Only this way it is ensured that the invocation of the PerformanceLogger
+ // plugins happens before the client sees the response. This is needed for being able to
+ // test performance logging from an acceptance test (see
+ // TraceIT#performanceLoggingForRestCall()).
+ try (PerformanceLogContext performanceLogContext =
+ new PerformanceLogContext(globals.config, globals.performanceLoggers)) {
+ logger.atFinest().log(
+ "Received REST request: %s %s (parameters: %s)",
+ req.getMethod(), req.getRequestURI(), getParameterNames(req));
+ logger.atFinest().log("Calling user: %s", globals.currentUser.get().getLoggableName());
+ logger.atFinest().log(
+ "Groups: %s",
+ lazy(() -> globals.currentUser.get().getEffectiveGroups().getKnownGroups()));
+
+ if (isCorsPreflight(req)) {
+ doCorsPreflight(req, res);
+ return;
}
- if (isRead(req)) {
- viewData = new ViewData(null, rc.list());
- } else if (isPost(req)) {
- RestView<RestResource> restCollectionView =
- rc.views().get(PluginName.GERRIT, "POST_ON_COLLECTION./");
- if (restCollectionView != null) {
- viewData = new ViewData(null, restCollectionView);
- } else {
- throw methodNotAllowed(req);
- }
- } else {
- // DELETE on root collections is not supported
- throw methodNotAllowed(req);
+ qp = ParameterParser.getQueryParams(req);
+ checkCors(req, res, qp.hasXdOverride());
+ if (qp.hasXdOverride()) {
+ req = applyXdOverrides(req, qp);
}
- } else {
- IdString id = path.remove(0);
- try {
- rsrc = rc.parse(rsrc, id);
- globals.quotaChecker.enforce(rsrc, req);
- if (path.isEmpty()) {
- checkPreconditions(req);
- }
- } catch (ResourceNotFoundException e) {
- if (!path.isEmpty()) {
- throw e;
- }
- globals.quotaChecker.enforce(req);
+ checkUserSession(req);
- if (isPost(req) || isPut(req)) {
- RestView<RestResource> createView = rc.views().get(PluginName.GERRIT, "CREATE./");
- if (createView != null) {
- viewData = new ViewData(null, createView);
- status = SC_CREATED;
- path.add(id);
- } else {
- throw e;
- }
- } else if (isDelete(req)) {
- RestView<RestResource> deleteView =
- rc.views().get(PluginName.GERRIT, "DELETE_MISSING./");
- if (deleteView != null) {
- viewData = new ViewData(null, deleteView);
- status = SC_NO_CONTENT;
- path.add(id);
- } else {
- throw e;
- }
- } else {
- throw e;
- }
- }
- if (viewData.view == null) {
- viewData = view(rc, req.getMethod(), path);
- }
- }
- checkRequiresCapability(viewData);
+ RestCollection<RestResource, RestResource> rc = members.get();
+ globals
+ .permissionBackend
+ .currentUser()
+ .checkAny(GlobalPermission.fromAnnotation(rc.getClass()));
- while (viewData.view instanceof RestCollection<?, ?>) {
- @SuppressWarnings("unchecked")
- RestCollection<RestResource, RestResource> c =
- (RestCollection<RestResource, RestResource>) viewData.view;
+ viewData = new ViewData(null, null);
if (path.isEmpty()) {
+ globals.quotaChecker.enforce(req);
+ if (rc instanceof NeedsParams) {
+ ((NeedsParams) rc).setParams(qp.params());
+ }
+
if (isRead(req)) {
- viewData = new ViewData(null, c.list());
+ viewData = new ViewData(null, rc.list());
} else if (isPost(req)) {
- // TODO: Here and on other collection methods: There is a bug that binds child views
- // with pluginName="gerrit" instead of the real plugin name. This has never worked
- // correctly and should be fixed where the binding gets created (DynamicMapProvider)
- // and here.
- RestView<RestResource> restCollectionView =
- c.views().get(PluginName.GERRIT, "POST_ON_COLLECTION./");
- if (restCollectionView != null) {
- viewData = new ViewData(null, restCollectionView);
- } else {
- throw methodNotAllowed(req);
- }
- } else if (isDelete(req)) {
RestView<RestResource> restCollectionView =
- c.views().get(PluginName.GERRIT, "DELETE_ON_COLLECTION./");
+ rc.views().get(PluginName.GERRIT, "POST_ON_COLLECTION./");
if (restCollectionView != null) {
viewData = new ViewData(null, restCollectionView);
} else {
throw methodNotAllowed(req);
}
} else {
+ // DELETE on root collections is not supported
throw methodNotAllowed(req);
}
- break;
- }
- IdString id = path.remove(0);
- try {
- rsrc = c.parse(rsrc, id);
- checkPreconditions(req);
- viewData = new ViewData(null, null);
- } catch (ResourceNotFoundException e) {
- if (!path.isEmpty()) {
- throw e;
+ } else {
+ IdString id = path.remove(0);
+ try {
+ rsrc = rc.parse(rsrc, id);
+ globals.quotaChecker.enforce(rsrc, req);
+ if (path.isEmpty()) {
+ checkPreconditions(req);
+ }
+ } catch (ResourceNotFoundException e) {
+ if (!path.isEmpty()) {
+ throw e;
+ }
+ globals.quotaChecker.enforce(req);
+
+ if (isPost(req) || isPut(req)) {
+ RestView<RestResource> createView = rc.views().get(PluginName.GERRIT, "CREATE./");
+ if (createView != null) {
+ viewData = new ViewData(null, createView);
+ path.add(id);
+ } else {
+ throw e;
+ }
+ } else if (isDelete(req)) {
+ RestView<RestResource> deleteView =
+ rc.views().get(PluginName.GERRIT, "DELETE_MISSING./");
+ if (deleteView != null) {
+ viewData = new ViewData(null, deleteView);
+ path.add(id);
+ } else {
+ throw e;
+ }
+ } else {
+ throw e;
+ }
+ }
+ if (viewData.view == null) {
+ viewData = view(rc, req.getMethod(), path);
}
+ }
+ checkRequiresCapability(viewData);
- if (isPost(req) || isPut(req)) {
- RestView<RestResource> createView = c.views().get(PluginName.GERRIT, "CREATE./");
- if (createView != null) {
- viewData = new ViewData(viewData.pluginName, createView);
- status = SC_CREATED;
- path.add(id);
+ while (viewData.view instanceof RestCollection<?, ?>) {
+ @SuppressWarnings("unchecked")
+ RestCollection<RestResource, RestResource> c =
+ (RestCollection<RestResource, RestResource>) viewData.view;
+
+ if (path.isEmpty()) {
+ if (isRead(req)) {
+ viewData = new ViewData(null, c.list());
+ } else if (isPost(req)) {
+ // TODO: Here and on other collection methods: There is a bug that binds child views
+ // with pluginName="gerrit" instead of the real plugin name. This has never worked
+ // correctly and should be fixed where the binding gets created (DynamicMapProvider)
+ // and here.
+ RestView<RestResource> restCollectionView =
+ c.views().get(PluginName.GERRIT, "POST_ON_COLLECTION./");
+ if (restCollectionView != null) {
+ viewData = new ViewData(null, restCollectionView);
+ } else {
+ throw methodNotAllowed(req);
+ }
+ } else if (isDelete(req)) {
+ RestView<RestResource> restCollectionView =
+ c.views().get(PluginName.GERRIT, "DELETE_ON_COLLECTION./");
+ if (restCollectionView != null) {
+ viewData = new ViewData(null, restCollectionView);
+ } else {
+ throw methodNotAllowed(req);
+ }
} else {
+ throw methodNotAllowed(req);
+ }
+ break;
+ }
+ IdString id = path.remove(0);
+ try {
+ rsrc = c.parse(rsrc, id);
+ checkPreconditions(req);
+ viewData = new ViewData(null, null);
+ } catch (ResourceNotFoundException e) {
+ if (!path.isEmpty()) {
throw e;
}
- } else if (isDelete(req)) {
- RestView<RestResource> deleteView =
- c.views().get(PluginName.GERRIT, "DELETE_MISSING./");
- if (deleteView != null) {
- viewData = new ViewData(viewData.pluginName, deleteView);
- status = SC_NO_CONTENT;
- path.add(id);
+
+ if (isPost(req) || isPut(req)) {
+ RestView<RestResource> createView = c.views().get(PluginName.GERRIT, "CREATE./");
+ if (createView != null) {
+ viewData = new ViewData(viewData.pluginName, createView);
+ path.add(id);
+ } else {
+ throw e;
+ }
+ } else if (isDelete(req)) {
+ RestView<RestResource> deleteView =
+ c.views().get(PluginName.GERRIT, "DELETE_MISSING./");
+ if (deleteView != null) {
+ viewData = new ViewData(viewData.pluginName, deleteView);
+ path.add(id);
+ } else {
+ throw e;
+ }
} else {
throw e;
}
- } else {
- throw e;
}
+ if (viewData.view == null) {
+ viewData = view(c, req.getMethod(), path);
+ }
+ checkRequiresCapability(viewData);
}
- if (viewData.view == null) {
- viewData = view(c, req.getMethod(), path);
- }
- checkRequiresCapability(viewData);
- }
- if (notModified(req, rsrc, viewData.view)) {
- logger.atFinest().log("REST call succeeded: %d", SC_NOT_MODIFIED);
- res.sendError(SC_NOT_MODIFIED);
- return;
- }
+ if (notModified(req, rsrc, viewData.view)) {
+ logger.atFinest().log("REST call succeeded: %d", SC_NOT_MODIFIED);
+ res.sendError(SC_NOT_MODIFIED);
+ return;
+ }
- if (!globals.paramParser.get().parse(viewData.view, qp.params(), req, res)) {
- return;
- }
+ if (!globals.paramParser.get().parse(viewData.view, qp.params(), req, res)) {
+ return;
+ }
- if (viewData.view instanceof RestReadView<?> && isRead(req)) {
- result = ((RestReadView<RestResource>) viewData.view).apply(rsrc);
- } else if (viewData.view instanceof RestModifyView<?, ?>) {
- @SuppressWarnings("unchecked")
- RestModifyView<RestResource, Object> m =
- (RestModifyView<RestResource, Object>) viewData.view;
-
- Type type = inputType(m);
- inputRequestBody = parseRequest(req, type);
- result = m.apply(rsrc, inputRequestBody);
- if (inputRequestBody instanceof RawInput) {
- try (InputStream is = req.getInputStream()) {
- ServletUtils.consumeRequestBody(is);
+ if (viewData.view instanceof RestReadView<?> && isRead(req)) {
+ response = ((RestReadView<RestResource>) viewData.view).apply(rsrc);
+ } else if (viewData.view instanceof RestModifyView<?, ?>) {
+ @SuppressWarnings("unchecked")
+ RestModifyView<RestResource, Object> m =
+ (RestModifyView<RestResource, Object>) viewData.view;
+
+ Type type = inputType(m);
+ inputRequestBody = parseRequest(req, type);
+ response = m.apply(rsrc, inputRequestBody);
+ if (inputRequestBody instanceof RawInput) {
+ try (InputStream is = req.getInputStream()) {
+ ServletUtils.consumeRequestBody(is);
+ }
}
- }
- } else if (viewData.view instanceof RestCollectionCreateView<?, ?, ?>) {
- @SuppressWarnings("unchecked")
- RestCollectionCreateView<RestResource, RestResource, Object> m =
- (RestCollectionCreateView<RestResource, RestResource, Object>) viewData.view;
-
- Type type = inputType(m);
- inputRequestBody = parseRequest(req, type);
- result = m.apply(rsrc, path.get(0), inputRequestBody);
- if (inputRequestBody instanceof RawInput) {
- try (InputStream is = req.getInputStream()) {
- ServletUtils.consumeRequestBody(is);
+ } else if (viewData.view instanceof RestCollectionCreateView<?, ?, ?>) {
+ @SuppressWarnings("unchecked")
+ RestCollectionCreateView<RestResource, RestResource, Object> m =
+ (RestCollectionCreateView<RestResource, RestResource, Object>) viewData.view;
+
+ Type type = inputType(m);
+ inputRequestBody = parseRequest(req, type);
+ response = m.apply(rsrc, path.get(0), inputRequestBody);
+ if (inputRequestBody instanceof RawInput) {
+ try (InputStream is = req.getInputStream()) {
+ ServletUtils.consumeRequestBody(is);
+ }
}
- }
- } else if (viewData.view instanceof RestCollectionDeleteMissingView<?, ?, ?>) {
- @SuppressWarnings("unchecked")
- RestCollectionDeleteMissingView<RestResource, RestResource, Object> m =
- (RestCollectionDeleteMissingView<RestResource, RestResource, Object>) viewData.view;
-
- Type type = inputType(m);
- inputRequestBody = parseRequest(req, type);
- result = m.apply(rsrc, path.get(0), inputRequestBody);
- if (inputRequestBody instanceof RawInput) {
- try (InputStream is = req.getInputStream()) {
- ServletUtils.consumeRequestBody(is);
+ } else if (viewData.view instanceof RestCollectionDeleteMissingView<?, ?, ?>) {
+ @SuppressWarnings("unchecked")
+ RestCollectionDeleteMissingView<RestResource, RestResource, Object> m =
+ (RestCollectionDeleteMissingView<RestResource, RestResource, Object>) viewData.view;
+
+ Type type = inputType(m);
+ inputRequestBody = parseRequest(req, type);
+ response = m.apply(rsrc, path.get(0), inputRequestBody);
+ if (inputRequestBody instanceof RawInput) {
+ try (InputStream is = req.getInputStream()) {
+ ServletUtils.consumeRequestBody(is);
+ }
}
- }
- } else if (viewData.view instanceof RestCollectionModifyView<?, ?, ?>) {
- @SuppressWarnings("unchecked")
- RestCollectionModifyView<RestResource, RestResource, Object> m =
- (RestCollectionModifyView<RestResource, RestResource, Object>) viewData.view;
-
- Type type = inputType(m);
- inputRequestBody = parseRequest(req, type);
- result = m.apply(rsrc, inputRequestBody);
- if (inputRequestBody instanceof RawInput) {
- try (InputStream is = req.getInputStream()) {
- ServletUtils.consumeRequestBody(is);
+ } else if (viewData.view instanceof RestCollectionModifyView<?, ?, ?>) {
+ @SuppressWarnings("unchecked")
+ RestCollectionModifyView<RestResource, RestResource, Object> m =
+ (RestCollectionModifyView<RestResource, RestResource, Object>) viewData.view;
+
+ Type type = inputType(m);
+ inputRequestBody = parseRequest(req, type);
+ response = m.apply(rsrc, inputRequestBody);
+ if (inputRequestBody instanceof RawInput) {
+ try (InputStream is = req.getInputStream()) {
+ ServletUtils.consumeRequestBody(is);
+ }
}
+ } else {
+ throw new ResourceNotFoundException();
}
- } else {
- throw new ResourceNotFoundException();
- }
- if (result instanceof Response) {
- @SuppressWarnings("rawtypes")
- Response<?> r = (Response) result;
- status = r.statusCode();
- configureCaching(req, res, rsrc, viewData.view, r.caching());
- } else if (result instanceof Response.Redirect) {
- CacheHeaders.setNotCacheable(res);
- String location = ((Response.Redirect) result).location();
- res.sendRedirect(location);
- logger.atFinest().log("REST call redirected to: %s", location);
- return;
- } else if (result instanceof Response.Accepted) {
- CacheHeaders.setNotCacheable(res);
- res.setStatus(SC_ACCEPTED);
- res.setHeader(HttpHeaders.LOCATION, ((Response.Accepted) result).location());
- logger.atFinest().log("REST call succeeded: %d", SC_ACCEPTED);
- return;
- } else {
- CacheHeaders.setNotCacheable(res);
+ traceId = response.traceId();
+ traceId.ifPresent(traceId -> res.addHeader(X_GERRIT_TRACE, traceId));
+
+ if (response instanceof Response.Redirect) {
+ CacheHeaders.setNotCacheable(res);
+ String location = ((Response.Redirect) response).location();
+ res.sendRedirect(location);
+ logger.atFinest().log("REST call redirected to: %s", location);
+ return;
+ } else if (response instanceof Response.Accepted) {
+ CacheHeaders.setNotCacheable(res);
+ res.setStatus(response.statusCode());
+ res.setHeader(HttpHeaders.LOCATION, ((Response.Accepted) response).location());
+ logger.atFinest().log("REST call succeeded: %d", response.statusCode());
+ return;
+ } else if (response instanceof Response.InternalServerError) {
+ // Rethrow the exception to have exactly the same error handling as if the REST endpoint
+ // would have thrown the exception directly, instead of returning
+ // Response.InternalServerError.
+ Exception cause = ((Response.InternalServerError<?>) response).cause();
+ throw cause;
+ }
+
+ status = response.statusCode();
+ configureCaching(req, res, rsrc, viewData.view, response.caching());
+ res.setStatus(status);
+ logger.atFinest().log("REST call succeeded: %d", status);
}
- res.setStatus(status);
- logger.atFinest().log("REST call succeeded: %d", status);
- if (result != Response.none()) {
- result = Response.unwrap(result);
- if (result instanceof BinaryResult) {
- responseBytes = replyBinaryResult(req, res, (BinaryResult) result);
+ if (response != Response.none()) {
+ Object value = Response.unwrap(response);
+ if (value instanceof BinaryResult) {
+ responseBytes = replyBinaryResult(req, res, (BinaryResult) value);
} else {
- responseBytes = replyJson(req, res, false, qp.config(), result);
+ responseBytes = replyJson(req, res, false, qp.config(), value);
}
}
} catch (MalformedJsonException | JsonParseException e) {
@@ -602,21 +638,27 @@ public class RestApiServlet extends HttpServlet {
e.caching(),
e);
} catch (NotImplementedException e) {
+ logger.atSevere().withCause(e).log("Error in %s %s", req.getMethod(), uriForLogging(req));
responseBytes =
replyError(req, res, status = SC_NOT_IMPLEMENTED, messageOr(e, "Not Implemented"), e);
} catch (UpdateException e) {
Throwable t = e.getCause();
if (t instanceof LockFailureException) {
- responseBytes =
- replyError(
- req, res, status = SC_SERVICE_UNAVAILABLE, messageOr(t, "Lock failure"), e);
+ logger.atSevere().withCause(t).log("Error in %s %s", req.getMethod(), uriForLogging(req));
+ responseBytes = replyError(req, res, status = SC_SERVICE_UNAVAILABLE, "Lock failure", e);
} else {
status = SC_INTERNAL_SERVER_ERROR;
responseBytes = handleException(e, req, res);
}
} catch (QuotaException e) {
responseBytes =
- replyError(req, res, status = 429, messageOr(e, "Quota limit reached"), e.caching(), e);
+ replyError(
+ req,
+ res,
+ status = SC_TOO_MANY_REQUESTS,
+ messageOr(e, "Quota limit reached"),
+ e.caching(),
+ e);
} catch (Exception e) {
status = SC_INTERNAL_SERVER_ERROR;
responseBytes = handleException(e, req, res);
@@ -641,7 +683,7 @@ public class RestApiServlet extends HttpServlet {
qp != null ? qp.params() : ImmutableListMultimap.of(),
inputRequestBody,
status,
- result,
+ response,
rsrc,
viewData == null ? null : viewData.view));
}
@@ -1388,6 +1430,29 @@ public class RestApiServlet extends HttpServlet {
return traceContext;
}
+ private RequestInfo createRequestInfo(
+ TraceContext traceContext, String requestUri, List<IdString> path) {
+ RequestInfo.Builder requestInfo =
+ RequestInfo.builder(RequestInfo.RequestType.REST, globals.currentUser.get(), traceContext)
+ .requestUri(requestUri);
+
+ if (path.size() < 1) {
+ return requestInfo.build();
+ }
+
+ RestCollection<?, ?> rootCollection = members.get();
+ String resourceId = path.get(0).get();
+ if (rootCollection instanceof ProjectsCollection) {
+ requestInfo.project(Project.nameKey(resourceId));
+ } else if (rootCollection instanceof ChangesCollection) {
+ ChangeNotes changeNotes = globals.changeFinder.findOne(resourceId);
+ if (changeNotes != null) {
+ requestInfo.project(changeNotes.getProjectName());
+ }
+ }
+ return requestInfo.build();
+ }
+
private boolean isDelete(HttpServletRequest req) {
return "DELETE".equals(req.getMethod());
}
@@ -1430,20 +1495,25 @@ public class RestApiServlet extends HttpServlet {
}
}
- private static long handleException(
- Throwable err, HttpServletRequest req, HttpServletResponse res) throws IOException {
- String uri = req.getRequestURI();
- if (!Strings.isNullOrEmpty(req.getQueryString())) {
- uri += "?" + LogRedactUtil.redactQueryString(req.getQueryString());
- }
- logger.atSevere().withCause(err).log("Error in %s %s", req.getMethod(), uri);
+ private long handleException(Throwable err, HttpServletRequest req, HttpServletResponse res)
+ throws IOException {
+ logger.atSevere().withCause(err).log("Error in %s %s", req.getMethod(), uriForLogging(req));
if (!res.isCommitted()) {
res.reset();
+ traceId.ifPresent(traceId -> res.addHeader(X_GERRIT_TRACE, traceId));
return replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error", err);
}
return 0;
}
+ private static String uriForLogging(HttpServletRequest req) {
+ String uri = req.getRequestURI();
+ if (!Strings.isNullOrEmpty(req.getQueryString())) {
+ uri += "?" + LogRedactUtil.redactQueryString(req.getQueryString());
+ }
+ return uri;
+ }
+
public static long replyError(
HttpServletRequest req,
HttpServletResponse res,
diff --git a/java/com/google/gerrit/index/BUILD b/java/com/google/gerrit/index/BUILD
index 55c7746924..95b358195a 100644
--- a/java/com/google/gerrit/index/BUILD
+++ b/java/com/google/gerrit/index/BUILD
@@ -22,17 +22,18 @@ java_library(
":query_exception",
"//antlr3:query_parser",
"//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
+ "//java/com/google/gerrit/git",
"//java/com/google/gerrit/json",
"//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server/logging",
"//lib:guava",
+ "//lib:jgit",
"//lib/antlr:java-runtime",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/flogger:api",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/index/IndexConfig.java b/java/com/google/gerrit/index/IndexConfig.java
index b5b36f129b..29b8ea657e 100644
--- a/java/com/google/gerrit/index/IndexConfig.java
+++ b/java/com/google/gerrit/index/IndexConfig.java
@@ -17,6 +17,7 @@ package com.google.gerrit.index;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.value.AutoValue;
+import java.util.function.Consumer;
import java.util.function.IntConsumer;
import org.eclipse.jgit.lib.Config;
@@ -39,6 +40,7 @@ public abstract class IndexConfig {
setIfPresent(cfg, "maxLimit", b::maxLimit);
setIfPresent(cfg, "maxPages", b::maxPages);
setIfPresent(cfg, "maxTerms", b::maxTerms);
+ setTypeOrDefault(cfg, b::type);
return b;
}
@@ -49,11 +51,17 @@ public abstract class IndexConfig {
}
}
+ private static void setTypeOrDefault(Config cfg, Consumer<String> setter) {
+ String type = cfg != null ? cfg.getString("index", null, "type") : null;
+ setter.accept(new IndexType(type).toString());
+ }
+
public static Builder builder() {
return new AutoValue_IndexConfig.Builder()
.maxLimit(Integer.MAX_VALUE)
.maxPages(Integer.MAX_VALUE)
.maxTerms(DEFAULT_MAX_TERMS)
+ .type(IndexType.getDefault())
.separateChangeSubIndexes(false);
}
@@ -71,6 +79,10 @@ public abstract class IndexConfig {
public abstract int maxTerms();
+ public abstract Builder type(String type);
+
+ public abstract String type();
+
public abstract Builder separateChangeSubIndexes(boolean separate);
abstract IndexConfig autoBuild();
@@ -105,6 +117,9 @@ public abstract class IndexConfig {
*/
public abstract int maxTerms();
+ /** @return index type. */
+ public abstract String type();
+
/**
* @return whether different subsets of changes may be stored in different physical sub-indexes.
*/
diff --git a/java/com/google/gerrit/index/IndexType.java b/java/com/google/gerrit/index/IndexType.java
new file mode 100644
index 0000000000..ee44deb465
--- /dev/null
+++ b/java/com/google/gerrit/index/IndexType.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.index;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.common.Nullable;
+
+/**
+ * Index types supported by the secondary index.
+ *
+ * <p>The explicitly known index types are Lucene (the default) and Elasticsearch.
+ *
+ * <p>The third supported index type is any other type String value, deemed as custom. This is for
+ * configuring index types that are internal or not to be disclosed. Supporting custom index types
+ * allows to not break that case upon core implementation changes.
+ */
+public class IndexType {
+ private static final String LUCENE = "lucene";
+ private static final String ELASTICSEARCH = "elasticsearch";
+
+ private final String type;
+
+ public IndexType(@Nullable String type) {
+ this.type = type == null ? getDefault() : type.toLowerCase();
+ }
+
+ public static String getDefault() {
+ return LUCENE;
+ }
+
+ public static ImmutableSet<String> getKnownTypes() {
+ return ImmutableSet.of(LUCENE, ELASTICSEARCH);
+ }
+
+ public boolean isLucene() {
+ return type.equals(LUCENE);
+ }
+
+ public boolean isElasticsearch() {
+ return type.equals(ELASTICSEARCH);
+ }
+
+ @Override
+ public String toString() {
+ return type;
+ }
+}
diff --git a/java/com/google/gerrit/index/RefState.java b/java/com/google/gerrit/index/RefState.java
index f0e465d43d..956dcabdb8 100644
--- a/java/com/google/gerrit/index/RefState.java
+++ b/java/com/google/gerrit/index/RefState.java
@@ -23,10 +23,10 @@ import com.google.common.base.Splitter;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.git.ObjectIds;
import java.io.IOException;
import java.util.List;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -42,7 +42,7 @@ public abstract class RefState {
String s = new String(b, UTF_8);
List<String> parts = Splitter.on(':').splitToList(s);
RefState.check(parts.size() == 3 && !parts.get(0).isEmpty() && !parts.get(1).isEmpty(), s);
- result.put(new Project.NameKey(parts.get(0)), RefState.create(parts.get(1), parts.get(2)));
+ result.put(Project.nameKey(parts.get(0)), RefState.create(parts.get(1), parts.get(2)));
}
return result;
}
@@ -61,7 +61,7 @@ public abstract class RefState {
public byte[] toByteArray(Project.NameKey project) {
byte[] a = (project.toString() + ':' + ref() + ':').getBytes(UTF_8);
- byte[] b = new byte[a.length + Constants.OBJECT_ID_STRING_LENGTH];
+ byte[] b = new byte[a.length + ObjectIds.STR_LEN];
System.arraycopy(a, 0, b, 0, a.length);
id().copyTo(b, a.length);
return b;
diff --git a/java/com/google/gerrit/index/Schema.java b/java/com/google/gerrit/index/Schema.java
index f65d03e438..bab2990f6e 100644
--- a/java/com/google/gerrit/index/Schema.java
+++ b/java/com/google/gerrit/index/Schema.java
@@ -35,6 +35,7 @@ public class Schema<T> {
public static class Builder<T> {
private final List<FieldDef<T, ?>> fields = new ArrayList<>();
+ private boolean useLegacyNumericFields;
public Builder<T> add(Schema<T> schema) {
this.fields.addAll(schema.getFields().values());
@@ -53,8 +54,13 @@ public class Schema<T> {
return this;
}
+ public Builder<T> legacyNumericFields(boolean useLegacyNumericFields) {
+ this.useLegacyNumericFields = useLegacyNumericFields;
+ return this;
+ }
+
public Schema<T> build() {
- return new Schema<>(ImmutableList.copyOf(fields));
+ return new Schema<>(useLegacyNumericFields, ImmutableList.copyOf(fields));
}
}
@@ -83,14 +89,15 @@ public class Schema<T> {
private final ImmutableMap<String, FieldDef<T, ?>> fields;
private final ImmutableMap<String, FieldDef<T, ?>> storedFields;
+ private final boolean useLegacyNumericFields;
private int version;
- public Schema(Iterable<FieldDef<T, ?>> fields) {
- this(0, fields);
+ public Schema(boolean useLegacyNumericFields, Iterable<FieldDef<T, ?>> fields) {
+ this(0, useLegacyNumericFields, fields);
}
- public Schema(int version, Iterable<FieldDef<T, ?>> fields) {
+ public Schema(int version, boolean useLegacyNumericFields, Iterable<FieldDef<T, ?>> fields) {
this.version = version;
ImmutableMap.Builder<String, FieldDef<T, ?>> b = ImmutableMap.builder();
ImmutableMap.Builder<String, FieldDef<T, ?>> sb = ImmutableMap.builder();
@@ -102,12 +109,17 @@ public class Schema<T> {
}
this.fields = b.build();
this.storedFields = sb.build();
+ this.useLegacyNumericFields = useLegacyNumericFields;
}
public final int getVersion() {
return version;
}
+ public final boolean useLegacyNumericFields() {
+ return useLegacyNumericFields;
+ }
+
/**
* Get all fields in this schema.
*
diff --git a/java/com/google/gerrit/index/SchemaUtil.java b/java/com/google/gerrit/index/SchemaUtil.java
index c59f251acf..9599d6a893 100644
--- a/java/com/google/gerrit/index/SchemaUtil.java
+++ b/java/com/google/gerrit/index/SchemaUtil.java
@@ -67,12 +67,19 @@ public class SchemaUtil {
}
public static <V> Schema<V> schema(Collection<FieldDef<V, ?>> fields) {
- return new Schema<>(ImmutableList.copyOf(fields));
+ return new Schema<>(true, ImmutableList.copyOf(fields));
+ }
+
+ public static <V> Schema<V> schema(Schema<V> schema, boolean useLegacyNumericFields) {
+ return new Schema<>(
+ useLegacyNumericFields,
+ new ImmutableList.Builder<FieldDef<V, ?>>().addAll(schema.getFields().values()).build());
}
@SafeVarargs
public static <V> Schema<V> schema(Schema<V> schema, FieldDef<V, ?>... moreFields) {
return new Schema<>(
+ true,
new ImmutableList.Builder<FieldDef<V, ?>>()
.addAll(schema.getFields().values())
.addAll(ImmutableList.copyOf(moreFields))
@@ -81,7 +88,7 @@ public class SchemaUtil {
@SafeVarargs
public static <V> Schema<V> schema(FieldDef<V, ?>... fields) {
- return schema(ImmutableList.copyOf(fields));
+ return new Schema<>(true, ImmutableList.copyOf(fields));
}
public static Set<String> getPersonParts(PersonIdent person) {
diff --git a/java/com/google/gerrit/index/project/BUILD b/java/com/google/gerrit/index/project/BUILD
index 2c460fdca1..b423f84f1f 100644
--- a/java/com/google/gerrit/index/project/BUILD
+++ b/java/com/google/gerrit/index/project/BUILD
@@ -5,9 +5,9 @@ java_library(
srcs = glob(["*.java"]),
visibility = ["//visibility:public"],
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
- "//java/com/google/gerrit/reviewdb:server",
"//lib:guava",
"//lib/guice",
],
diff --git a/java/com/google/gerrit/index/project/IndexedProjectQuery.java b/java/com/google/gerrit/index/project/IndexedProjectQuery.java
index 383ba1c75d..cdfeabdab1 100644
--- a/java/com/google/gerrit/index/project/IndexedProjectQuery.java
+++ b/java/com/google/gerrit/index/project/IndexedProjectQuery.java
@@ -14,13 +14,13 @@
package com.google.gerrit.index.project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.IndexedQuery;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Project;
public class IndexedProjectQuery extends IndexedQuery<Project.NameKey, ProjectData>
implements DataSource<ProjectData> {
diff --git a/java/com/google/gerrit/index/project/ProjectData.java b/java/com/google/gerrit/index/project/ProjectData.java
index fb029acddb..2bf9a4b537 100644
--- a/java/com/google/gerrit/index/project/ProjectData.java
+++ b/java/com/google/gerrit/index/project/ProjectData.java
@@ -18,7 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
diff --git a/java/com/google/gerrit/index/project/ProjectField.java b/java/com/google/gerrit/index/project/ProjectField.java
index 119980c104..c2c8986093 100644
--- a/java/com/google/gerrit/index/project/ProjectField.java
+++ b/java/com/google/gerrit/index/project/ProjectField.java
@@ -20,11 +20,11 @@ import static com.google.gerrit.index.FieldDef.fullText;
import static com.google.gerrit.index.FieldDef.prefix;
import static com.google.gerrit.index.FieldDef.storedOnly;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.RefState;
import com.google.gerrit.index.SchemaUtil;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
/** Index schema for projects. */
public class ProjectField {
diff --git a/java/com/google/gerrit/index/project/ProjectIndex.java b/java/com/google/gerrit/index/project/ProjectIndex.java
index db7302a41a..3e99d55996 100644
--- a/java/com/google/gerrit/index/project/ProjectIndex.java
+++ b/java/com/google/gerrit/index/project/ProjectIndex.java
@@ -14,10 +14,10 @@
package com.google.gerrit.index.project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Project;
public interface ProjectIndex extends Index<Project.NameKey, ProjectData> {
diff --git a/java/com/google/gerrit/index/project/ProjectIndexCollection.java b/java/com/google/gerrit/index/project/ProjectIndexCollection.java
index 281f99214f..30227a3352 100644
--- a/java/com/google/gerrit/index/project/ProjectIndexCollection.java
+++ b/java/com/google/gerrit/index/project/ProjectIndexCollection.java
@@ -15,8 +15,8 @@
package com.google.gerrit.index.project;
import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.IndexCollection;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Singleton;
@Singleton
diff --git a/java/com/google/gerrit/index/project/ProjectIndexer.java b/java/com/google/gerrit/index/project/ProjectIndexer.java
index 1ca29f5580..bd5efeb224 100644
--- a/java/com/google/gerrit/index/project/ProjectIndexer.java
+++ b/java/com/google/gerrit/index/project/ProjectIndexer.java
@@ -14,7 +14,7 @@
package com.google.gerrit.index.project;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
public interface ProjectIndexer {
diff --git a/java/com/google/gerrit/index/query/AndSource.java b/java/com/google/gerrit/index/query/AndSource.java
index 7d817d2b44..538e11b783 100644
--- a/java/com/google/gerrit/index/query/AndSource.java
+++ b/java/com/google/gerrit/index/query/AndSource.java
@@ -78,48 +78,55 @@ public class AndSource<T> extends AndPredicate<T>
if (source == null) {
throw new StorageException("No DataSource: " + this);
}
- List<T> r = new ArrayList<>();
- T last = null;
- int nextStart = 0;
- boolean skipped = false;
- for (T data : buffer(source.read())) {
- if (!isMatchable() || match(data)) {
- r.add(data);
- } else {
- skipped = true;
- }
- last = data;
- nextStart++;
- }
- if (skipped && last != null && source instanceof Paginated) {
- // If our source is a paginated source and we skipped at
- // least one of its results, we may not have filled the full
- // limit the caller wants. Restart the source and continue.
- //
- @SuppressWarnings("unchecked")
- Paginated<T> p = (Paginated<T>) source;
- while (skipped && r.size() < p.getOptions().limit() + start) {
- skipped = false;
- ResultSet<T> next = p.restart(nextStart);
-
- for (T data : buffer(next)) {
- if (match(data)) {
- r.add(data);
- } else {
- skipped = true;
+ // ResultSets are lazy. Calling #read here first and then dealing with ResultSets only when
+ // requested allows the index to run asynchronous queries.
+ ResultSet<T> resultSet = source.read();
+ return new LazyResultSet<>(
+ () -> {
+ List<T> r = new ArrayList<>();
+ T last = null;
+ int nextStart = 0;
+ boolean skipped = false;
+ for (T data : buffer(resultSet)) {
+ if (!isMatchable() || match(data)) {
+ r.add(data);
+ } else {
+ skipped = true;
+ }
+ last = data;
+ nextStart++;
}
- nextStart++;
- }
- }
- }
- if (start >= r.size()) {
- r = ImmutableList.of();
- } else if (start > 0) {
- r = ImmutableList.copyOf(r.subList(start, r.size()));
- }
- return new ListResultSet<>(r);
+ if (skipped && last != null && source instanceof Paginated) {
+ // If our source is a paginated source and we skipped at
+ // least one of its results, we may not have filled the full
+ // limit the caller wants. Restart the source and continue.
+ //
+ @SuppressWarnings("unchecked")
+ Paginated<T> p = (Paginated<T>) source;
+ while (skipped && r.size() < p.getOptions().limit() + start) {
+ skipped = false;
+ ResultSet<T> next = p.restart(nextStart);
+
+ for (T data : buffer(next)) {
+ if (match(data)) {
+ r.add(data);
+ } else {
+ skipped = true;
+ }
+ nextStart++;
+ }
+ }
+ }
+
+ if (start >= r.size()) {
+ return ImmutableList.of();
+ } else if (start > 0) {
+ return ImmutableList.copyOf(r.subList(start, r.size()));
+ }
+ return ImmutableList.copyOf(r);
+ });
}
@Override
diff --git a/java/com/google/gerrit/index/query/LazyResultSet.java b/java/com/google/gerrit/index/query/LazyResultSet.java
new file mode 100644
index 0000000000..f3fab5f5b5
--- /dev/null
+++ b/java/com/google/gerrit/index/query/LazyResultSet.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.index.query;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import java.util.function.Supplier;
+
+/**
+ * Result set that allows for asynchronous execution of the actual query. Callers should dispatch
+ * the query and call the constructor of this class with a supplier that fetches the result and
+ * blocks on it if necessary.
+ *
+ * <p>If the execution is synchronous or the results are known a priori, consider using {@link
+ * ListResultSet}.
+ */
+public class LazyResultSet<T> implements ResultSet<T> {
+ private final Supplier<ImmutableList<T>> resultsCallback;
+
+ private boolean resultsReturned = false;
+
+ public LazyResultSet(Supplier<ImmutableList<T>> r) {
+ resultsCallback = requireNonNull(r, "results can't be null");
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return toList().iterator();
+ }
+
+ @Override
+ public ImmutableList<T> toList() {
+ if (resultsReturned) {
+ throw new IllegalStateException("Results already obtained");
+ }
+ resultsReturned = true;
+ return resultsCallback.get();
+ }
+
+ @Override
+ public void close() {}
+}
diff --git a/java/com/google/gerrit/index/query/ListResultSet.java b/java/com/google/gerrit/index/query/ListResultSet.java
index 4cf48c8f1b..9d7eadf393 100644
--- a/java/com/google/gerrit/index/query/ListResultSet.java
+++ b/java/com/google/gerrit/index/query/ListResultSet.java
@@ -14,15 +14,25 @@
package com.google.gerrit.index.query;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import java.util.Iterator;
import java.util.List;
+/**
+ * Result set for queries that run synchronously or for cases where the result is already known and
+ * we just need to pipe it back through our interfaces.
+ *
+ * <p>If your implementation benefits from asynchronous execution (i.e. dispatching a query and
+ * awaiting results only when {@link ResultSet#toList()} is called, consider using {@link
+ * LazyResultSet}.
+ */
public class ListResultSet<T> implements ResultSet<T> {
- private ImmutableList<T> items;
+ private ImmutableList<T> results;
public ListResultSet(List<T> r) {
- items = ImmutableList.copyOf(r);
+ results = ImmutableList.copyOf(requireNonNull(r, "results can't be null"));
}
@Override
@@ -32,16 +42,16 @@ public class ListResultSet<T> implements ResultSet<T> {
@Override
public ImmutableList<T> toList() {
- if (items == null) {
+ if (results == null) {
throw new IllegalStateException("Results already obtained");
}
- ImmutableList<T> r = items;
- items = null;
+ ImmutableList<T> r = results;
+ results = null;
return r;
}
@Override
public void close() {
- items = null;
+ results = null;
}
}
diff --git a/java/com/google/gerrit/index/query/QueryProcessor.java b/java/com/google/gerrit/index/query/QueryProcessor.java
index 70772455f5..9501e524e0 100644
--- a/java/com/google/gerrit/index/query/QueryProcessor.java
+++ b/java/com/google/gerrit/index/query/QueryProcessor.java
@@ -38,6 +38,7 @@ import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer1;
import com.google.gerrit.server.logging.CallerFinder;
+import com.google.gerrit.server.logging.Metadata;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -60,14 +61,15 @@ public abstract class QueryProcessor<T> {
final Timer1<String> executionTime;
Metrics(MetricMaker metricMaker) {
- Field<String> index = Field.ofString("index", "index name");
executionTime =
metricMaker.newTimer(
"query/query_latency",
new Description("Successful query latency, accumulated over the life of the process")
.setCumulative()
.setUnit(Description.Units.MILLISECONDS),
- index);
+ Field.ofString("index", Metadata.Builder::indexName)
+ .description("index name")
+ .build());
}
}
@@ -216,7 +218,7 @@ public abstract class QueryProcessor<T> {
logger.atFine().log(
"Executing %d %s index queries for %s",
- cnt, schemaDef.getName(), callerFinder.findCaller());
+ cnt, schemaDef.getName(), callerFinder.findCallerLazy());
List<QueryResult<T>> out;
try {
// Parse and rewrite all queries.
diff --git a/java/com/google/gerrit/index/query/TooManyTermsInQueryException.java b/java/com/google/gerrit/index/query/TooManyTermsInQueryException.java
new file mode 100644
index 0000000000..b0a394eb1c
--- /dev/null
+++ b/java/com/google/gerrit/index/query/TooManyTermsInQueryException.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.index.query;
+
+public class TooManyTermsInQueryException extends QueryParseException {
+ private static final long serialVersionUID = 1L;
+
+ private static final String MESSAGE = "too many terms in query";
+
+ public TooManyTermsInQueryException() {
+ super(MESSAGE);
+ }
+
+ public TooManyTermsInQueryException(Throwable why) {
+ super(MESSAGE, why);
+ }
+}
diff --git a/java/com/google/gerrit/index/query/testing/TreeSubject.java b/java/com/google/gerrit/index/query/testing/TreeSubject.java
index c60b36394a..7d2b868bd9 100644
--- a/java/com/google/gerrit/index/query/testing/TreeSubject.java
+++ b/java/com/google/gerrit/index/query/testing/TreeSubject.java
@@ -23,43 +23,46 @@ import com.google.common.truth.Subject;
import com.google.gerrit.index.query.QueryParser;
import org.antlr.runtime.tree.Tree;
-public class TreeSubject extends Subject<TreeSubject, Tree> {
+public class TreeSubject extends Subject {
public static TreeSubject assertThat(Tree actual) {
return assertAbout(TreeSubject::new).that(actual);
}
+ private final Tree tree;
+
private TreeSubject(FailureMetadata failureMetadata, Tree tree) {
super(failureMetadata, tree);
+ this.tree = tree;
}
public void hasType(int expectedType) {
isNotNull();
- check("getType()").that(typeName(actual().getType())).isEqualTo(typeName(expectedType));
+ check("getType()").that(typeName(tree.getType())).isEqualTo(typeName(expectedType));
}
public void hasText(String expectedText) {
requireNonNull(expectedText);
isNotNull();
- check("getText()").that(actual().getText()).isEqualTo(expectedText);
+ check("getText()").that(tree.getText()).isEqualTo(expectedText);
}
public void hasNoChildren() {
isNotNull();
- check("getChildCount()").that(actual().getChildCount()).isEqualTo(0);
+ check("getChildCount()").that(tree.getChildCount()).isEqualTo(0);
}
public void hasChildCount(int expectedChildCount) {
checkArgument(
expectedChildCount > 0, "expected child count must be positive: %s", expectedChildCount);
isNotNull();
- check("getChildCount()").that(actual().getChildCount()).isEqualTo(expectedChildCount);
+ check("getChildCount()").that(tree.getChildCount()).isEqualTo(expectedChildCount);
}
public TreeSubject child(int childIndex) {
isNotNull();
return check("getChild(%s)", childIndex)
.about(TreeSubject::new)
- .that(actual().getChild(childIndex));
+ .that(tree.getChild(childIndex));
}
private static String typeName(int type) {
diff --git a/java/com/google/gerrit/jgit/BUILD b/java/com/google/gerrit/jgit/BUILD
index e67ebfe1ff..1041f1fe7d 100644
--- a/java/com/google/gerrit/jgit/BUILD
+++ b/java/com/google/gerrit/jgit/BUILD
@@ -8,6 +8,6 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//lib:gson",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
],
)
diff --git a/java/com/google/gerrit/json/BUILD b/java/com/google/gerrit/json/BUILD
index d9cec456fc..439f23f2e9 100644
--- a/java/com/google/gerrit/json/BUILD
+++ b/java/com/google/gerrit/json/BUILD
@@ -6,5 +6,6 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//lib:gson",
+ "//lib/flogger:api",
],
)
diff --git a/java/com/google/gerrit/json/EnumTypeAdapterFactory.java b/java/com/google/gerrit/json/EnumTypeAdapterFactory.java
new file mode 100644
index 0000000000..21c489125b
--- /dev/null
+++ b/java/com/google/gerrit/json/EnumTypeAdapterFactory.java
@@ -0,0 +1,78 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.json;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.bind.TypeAdapters;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+
+/**
+ * A {@code TypeAdapterFactory} for enums.
+ *
+ * <p>This factory introduces a wrapper around Gson's own default enum handler to add the following
+ * special behavior: log when input which doesn't match any existing enum value is encountered.
+ */
+public class EnumTypeAdapterFactory implements TypeAdapterFactory {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Override
+ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ TypeAdapter<T> defaultEnumAdapter = TypeAdapters.ENUM_FACTORY.create(gson, typeToken);
+ if (defaultEnumAdapter == null) {
+ // Not an enum. -> Enum type adapter doesn't apply.
+ return null;
+ }
+
+ return new EnumTypeAdapter(defaultEnumAdapter, typeToken);
+ }
+
+ private static class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
+
+ private final TypeAdapter<T> defaultEnumAdapter;
+ private final TypeToken<T> typeToken;
+
+ public EnumTypeAdapter(TypeAdapter<T> defaultEnumAdapter, TypeToken<T> typeToken) {
+ this.defaultEnumAdapter = defaultEnumAdapter;
+ this.typeToken = typeToken;
+ }
+
+ @Override
+ public T read(JsonReader in) throws IOException {
+ // Still handle null values. -> Check them first.
+ if (in.peek() == JsonToken.NULL) {
+ in.nextNull();
+ return null;
+ }
+ T enumValue = defaultEnumAdapter.read(in);
+ if (enumValue == null) {
+ logger.atWarning().log("Expected an existing value for enum %s.", typeToken);
+ }
+ return enumValue;
+ }
+
+ @Override
+ public void write(JsonWriter out, T value) throws IOException {
+ defaultEnumAdapter.write(out, value);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/json/OutputFormat.java b/java/com/google/gerrit/json/OutputFormat.java
index a2d174f4cc..3e7c319aa7 100644
--- a/java/com/google/gerrit/json/OutputFormat.java
+++ b/java/com/google/gerrit/json/OutputFormat.java
@@ -55,7 +55,8 @@ public enum OutputFormat {
GsonBuilder gb =
new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
- .registerTypeAdapter(Timestamp.class, new SqlTimestampDeserializer());
+ .registerTypeAdapter(Timestamp.class, new SqlTimestampDeserializer())
+ .registerTypeAdapterFactory(new EnumTypeAdapterFactory());
if (this == OutputFormat.JSON) {
gb.setPrettyPrinting();
}
diff --git a/java/com/google/gerrit/json/SqlTimestampDeserializer.java b/java/com/google/gerrit/json/SqlTimestampDeserializer.java
index 0148226284..e1cf38210a 100644
--- a/java/com/google/gerrit/json/SqlTimestampDeserializer.java
+++ b/java/com/google/gerrit/json/SqlTimestampDeserializer.java
@@ -44,7 +44,15 @@ class SqlTimestampDeserializer implements JsonDeserializer<Timestamp>, JsonSeria
throw new JsonParseException("Expected string for timestamp type");
}
- return JavaSqlTimestampHelper.parseTimestamp(p.getAsString());
+ String input = p.getAsString();
+ if (input.trim().isEmpty()) {
+ // Magic timestamp to indicate no timestamp. (-> null object)
+ // Always create a new object as timestamps are mutable. Don't use TimeUtil.never() to not
+ // introduce an undesired dependency.
+ return new Timestamp(0);
+ }
+
+ return JavaSqlTimestampHelper.parseTimestamp(input);
}
@Override
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index 7a0430c701..deb3203038 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -63,8 +63,10 @@ import java.util.function.Function;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LegacyIntField;
import org.apache.lucene.document.LegacyLongField;
+import org.apache.lucene.document.LongPoint;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
@@ -334,15 +336,23 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> {
if (type == FieldType.INTEGER || type == FieldType.INTEGER_RANGE) {
for (Object value : values.getValues()) {
- doc.add(new LegacyIntField(name, (Integer) value, store));
+ Integer intValue = (Integer) value;
+ if (schema.useLegacyNumericFields()) {
+ doc.add(new LegacyIntField(name, intValue, store));
+ } else {
+ doc.add(new IntPoint(name, intValue));
+ if (store == Store.YES) {
+ doc.add(new StoredField(name, intValue));
+ }
+ }
}
} else if (type == FieldType.LONG) {
for (Object value : values.getValues()) {
- doc.add(new LegacyLongField(name, (Long) value, store));
+ addLongField(doc, name, store, (Long) value);
}
} else if (type == FieldType.TIMESTAMP) {
for (Object value : values.getValues()) {
- doc.add(new LegacyLongField(name, ((Timestamp) value).getTime(), store));
+ addLongField(doc, name, store, ((Timestamp) value).getTime());
}
} else if (type == FieldType.EXACT || type == FieldType.PREFIX) {
for (Object value : values.getValues()) {
@@ -361,6 +371,17 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> {
}
}
+ private void addLongField(Document doc, String name, Store store, Long longValue) {
+ if (schema.useLegacyNumericFields()) {
+ doc.add(new LegacyLongField(name, longValue, store));
+ } else {
+ doc.add(new LongPoint(name, longValue));
+ if (store == Store.YES) {
+ doc.add(new StoredField(name, longValue));
+ }
+ }
+ }
+
protected FieldBundle toFieldBundle(Document doc) {
Map<String, FieldDef<V, ?>> allFields = getSchema().getFields();
ListMultimap<String, Object> rawFields = ArrayListMultimap.create();
diff --git a/java/com/google/gerrit/lucene/BUILD b/java/com/google/gerrit/lucene/BUILD
index 40e3157b48..879e7068aa 100644
--- a/java/com/google/gerrit/lucene/BUILD
+++ b/java/com/google/gerrit/lucene/BUILD
@@ -24,23 +24,20 @@ java_library(
deps = [
":query_builder",
"//java/com/google/gerrit/common:annotations",
- "//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
- "//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
"//java/com/google/gerrit/index/project",
- "//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/proto",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/logging",
"//lib:guava",
+ "//lib:jgit",
"//lib:protobuf",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/lucene:lucene-analyzers-common",
"//lib/lucene:lucene-core-and-backward-codecs",
"//lib/lucene:lucene-misc",
diff --git a/java/com/google/gerrit/lucene/ChangeSubIndex.java b/java/com/google/gerrit/lucene/ChangeSubIndex.java
index 98424b5e1c..fd439f199f 100644
--- a/java/com/google/gerrit/lucene/ChangeSubIndex.java
+++ b/java/com/google/gerrit/lucene/ChangeSubIndex.java
@@ -15,10 +15,12 @@
package com.google.gerrit.lucene;
import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.gerrit.lucene.LuceneChangeIndex.ID2_SORT_FIELD;
import static com.google.gerrit.lucene.LuceneChangeIndex.ID_SORT_FIELD;
import static com.google.gerrit.lucene.LuceneChangeIndex.UPDATED_SORT_FIELD;
import static com.google.gerrit.server.index.change.ChangeSchemaDefinitions.NAME;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
@@ -27,7 +29,6 @@ import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndex;
@@ -99,6 +100,9 @@ public class ChangeSubIndex extends AbstractLuceneIndex<Change.Id, ChangeData>
if (f == ChangeField.LEGACY_ID) {
int v = (Integer) getOnlyElement(values.getValues());
doc.add(new NumericDocValuesField(ID_SORT_FIELD, v));
+ } else if (f == ChangeField.LEGACY_ID_STR) {
+ String v = (String) getOnlyElement(values.getValues());
+ doc.add(new NumericDocValuesField(ID2_SORT_FIELD, Integer.valueOf(v)));
} else if (f == ChangeField.UPDATED) {
long t = ((Timestamp) getOnlyElement(values.getValues())).getTime();
doc.add(new NumericDocValuesField(UPDATED_SORT_FIELD, t));
diff --git a/java/com/google/gerrit/lucene/LuceneAccountIndex.java b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
index 0b787b6e18..efd7ea363d 100644
--- a/java/com/google/gerrit/lucene/LuceneAccountIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
@@ -17,8 +17,10 @@ package com.google.gerrit.lucene;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.gerrit.server.index.account.AccountField.FULL_NAME;
import static com.google.gerrit.server.index.account.AccountField.ID;
+import static com.google.gerrit.server.index.account.AccountField.ID_STR;
import static com.google.gerrit.server.index.account.AccountField.PREFERRED_EMAIL_EXACT;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
@@ -27,7 +29,6 @@ import com.google.gerrit.index.Schema.Values;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -60,13 +61,18 @@ public class LuceneAccountIndex extends AbstractLuceneIndex<Account.Id, AccountS
private static final String FULL_NAME_SORT_FIELD = sortFieldName(FULL_NAME);
private static final String EMAIL_SORT_FIELD = sortFieldName(PREFERRED_EMAIL_EXACT);
private static final String ID_SORT_FIELD = sortFieldName(ID);
+ private static final String ID2_SORT_FIELD = sortFieldName(ID_STR);
- private static Term idTerm(AccountState as) {
- return idTerm(as.getAccount().getId());
+ private static Term idTerm(boolean useLegacyNumericFields, AccountState as) {
+ return idTerm(useLegacyNumericFields, as.account().id());
}
- private static Term idTerm(Account.Id id) {
- return QueryBuilder.intTerm(ID.getName(), id.get());
+ private static Term idTerm(boolean useLegacyNumericFields, Account.Id id) {
+ FieldDef<AccountState, ?> idField = useLegacyNumericFields ? ID : ID_STR;
+ if (useLegacyNumericFields) {
+ return QueryBuilder.intTerm(idField.getName(), id.get());
+ }
+ return QueryBuilder.stringTerm(idField.getName(), Integer.toString(id.get()));
}
private final GerritIndexWriterConfig indexWriterConfig;
@@ -110,6 +116,9 @@ public class LuceneAccountIndex extends AbstractLuceneIndex<Account.Id, AccountS
if (f == ID) {
int v = (Integer) getOnlyElement(values.getValues());
doc.add(new NumericDocValuesField(ID_SORT_FIELD, v));
+ } else if (f == ID_STR) {
+ String v = (String) getOnlyElement(values.getValues());
+ doc.add(new NumericDocValuesField(ID2_SORT_FIELD, Integer.valueOf(v)));
} else if (f == FULL_NAME) {
String value = (String) getOnlyElement(values.getValues());
doc.add(new SortedDocValuesField(FULL_NAME_SORT_FIELD, new BytesRef(value)));
@@ -123,7 +132,7 @@ public class LuceneAccountIndex extends AbstractLuceneIndex<Account.Id, AccountS
@Override
public void replace(AccountState as) {
try {
- replace(idTerm(as), toDocument(as)).get();
+ replace(idTerm(getSchema().useLegacyNumericFields(), as), toDocument(as)).get();
} catch (ExecutionException | InterruptedException e) {
throw new StorageException(e);
}
@@ -132,7 +141,7 @@ public class LuceneAccountIndex extends AbstractLuceneIndex<Account.Id, AccountS
@Override
public void delete(Account.Id key) {
try {
- delete(idTerm(key)).get();
+ delete(idTerm(getSchema().useLegacyNumericFields(), key)).get();
} catch (ExecutionException | InterruptedException e) {
throw new StorageException(e);
}
@@ -141,20 +150,29 @@ public class LuceneAccountIndex extends AbstractLuceneIndex<Account.Id, AccountS
@Override
public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts)
throws QueryParseException {
+ queryBuilder.getSchema().useLegacyNumericFields();
return new LuceneQuerySource(
- opts.filterFields(IndexUtils::accountFields), queryBuilder.toQuery(p), getSort());
+ opts.filterFields(o -> IndexUtils.accountFields(o, getSchema().useLegacyNumericFields())),
+ queryBuilder.toQuery(p),
+ getSort());
}
private Sort getSort() {
+ String idSortField = getSchema().useLegacyNumericFields() ? ID_SORT_FIELD : ID2_SORT_FIELD;
return new Sort(
new SortField(FULL_NAME_SORT_FIELD, SortField.Type.STRING, false),
new SortField(EMAIL_SORT_FIELD, SortField.Type.STRING, false),
- new SortField(ID_SORT_FIELD, SortField.Type.LONG, false));
+ new SortField(idSortField, SortField.Type.LONG, false));
}
@Override
protected AccountState fromDocument(Document doc) {
- Account.Id id = new Account.Id(doc.getField(ID.getName()).numericValue().intValue());
+ FieldDef<AccountState, ?> idField = getSchema().useLegacyNumericFields() ? ID : ID_STR;
+ Account.Id id =
+ Account.id(
+ getSchema().useLegacyNumericFields()
+ ? doc.getField(idField.getName()).numericValue().intValue()
+ : Integer.valueOf(doc.getField(idField.getName()).stringValue()));
// Use the AccountCache rather than depending on any stored fields in the document (of which
// there shouldn't be any). The most expensive part to compute anyway is the effective group
// IDs, and we don't have a good way to reindex when those change.
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 87f9396a90..e576d73f7c 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -18,13 +18,13 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.lucene.AbstractLuceneIndex.sortFieldName;
import static com.google.gerrit.server.git.QueueProvider.QueueType.INTERACTIVE;
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
+import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID_STR;
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
import static com.google.gerrit.server.index.change.ChangeIndexRewriter.CLOSED_STATUSES;
import static com.google.gerrit.server.index.change.ChangeIndexRewriter.OPEN_STATUSES;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
-import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
import com.google.common.collect.FluentIterable;
@@ -36,7 +36,16 @@ import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.converter.ChangeProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetProtoConverter;
+import com.google.gerrit.entities.converter.ProtoConverter;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.FieldBundle;
@@ -44,14 +53,6 @@ import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.ResultSet;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.converter.ChangeProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetApprovalProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetProtoConverter;
-import com.google.gerrit.reviewdb.converter.ProtoConverter;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
@@ -77,6 +78,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
+import java.util.function.Function;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexableField;
@@ -106,6 +108,7 @@ public class LuceneChangeIndex implements ChangeIndex {
static final String UPDATED_SORT_FIELD = sortFieldName(ChangeField.UPDATED);
static final String ID_SORT_FIELD = sortFieldName(ChangeField.LEGACY_ID);
+ static final String ID2_SORT_FIELD = sortFieldName(ChangeField.LEGACY_ID_STR);
private static final String CHANGES = "changes";
private static final String CHANGES_OPEN = "open";
@@ -134,12 +137,22 @@ public class LuceneChangeIndex implements ChangeIndex {
private static final String UNRESOLVED_COMMENT_COUNT_FIELD =
ChangeField.UNRESOLVED_COMMENT_COUNT.getName();
- static Term idTerm(ChangeData cd) {
- return QueryBuilder.intTerm(LEGACY_ID.getName(), cd.getId().get());
+ @FunctionalInterface
+ static interface IdTerm {
+ Term get(String name, int id);
}
- static Term idTerm(Change.Id id) {
- return QueryBuilder.intTerm(LEGACY_ID.getName(), id.get());
+ static Term idTerm(IdTerm idTerm, FieldDef<ChangeData, ?> idField, ChangeData cd) {
+ return idTerm(idTerm, idField, cd.getId());
+ }
+
+ static Term idTerm(IdTerm idTerm, FieldDef<ChangeData, ?> idField, Change.Id id) {
+ return idTerm.get(idField.getName(), id.get());
+ }
+
+ @FunctionalInterface
+ static interface ChangeIdExtractor {
+ Change.Id extract(IndexableField f);
}
private final ListeningExecutorService executor;
@@ -149,6 +162,12 @@ public class LuceneChangeIndex implements ChangeIndex {
private final ChangeSubIndex openIndex;
private final ChangeSubIndex closedIndex;
+ // TODO(davido): Remove the below fields when support for legacy numeric fields is removed.
+ private final FieldDef<ChangeData, ?> idField;
+ private final String idSortFieldName;
+ private final IdTerm idTerm;
+ private final ChangeIdExtractor extractor;
+
@Inject
LuceneChangeIndex(
@GerritServerConfig Config cfg,
@@ -183,6 +202,20 @@ public class LuceneChangeIndex implements ChangeIndex {
new ChangeSubIndex(
schema, sitePaths, dir.resolve(CHANGES_CLOSED), closedConfig, searcherFactory);
}
+
+ idField = this.schema.useLegacyNumericFields() ? LEGACY_ID : LEGACY_ID_STR;
+ idSortFieldName = schema.useLegacyNumericFields() ? ID_SORT_FIELD : ID2_SORT_FIELD;
+ idTerm =
+ (name, id) ->
+ this.schema.useLegacyNumericFields()
+ ? QueryBuilder.intTerm(name, id)
+ : QueryBuilder.stringTerm(name, Integer.toString(id));
+ extractor =
+ (f) ->
+ Change.id(
+ this.schema.useLegacyNumericFields()
+ ? f.numericValue().intValue()
+ : Integer.valueOf(f.stringValue()));
}
@Override
@@ -201,7 +234,7 @@ public class LuceneChangeIndex implements ChangeIndex {
@Override
public void replace(ChangeData cd) {
- Term id = LuceneChangeIndex.idTerm(cd);
+ Term id = LuceneChangeIndex.idTerm(idTerm, idField, cd);
// toDocument is essentially static and doesn't depend on the specific
// sub-index, so just pick one.
Document doc = openIndex.toDocument(cd);
@@ -217,10 +250,10 @@ public class LuceneChangeIndex implements ChangeIndex {
}
@Override
- public void delete(Change.Id id) {
- Term idTerm = LuceneChangeIndex.idTerm(id);
+ public void delete(Change.Id changeId) {
+ Term id = LuceneChangeIndex.idTerm(idTerm, idField, changeId);
try {
- Futures.allAsList(openIndex.delete(idTerm), closedIndex.delete(idTerm)).get();
+ Futures.allAsList(openIndex.delete(id), closedIndex.delete(id)).get();
} catch (ExecutionException | InterruptedException e) {
throw new StorageException(e);
}
@@ -256,11 +289,7 @@ public class LuceneChangeIndex implements ChangeIndex {
private Sort getSort() {
return new Sort(
new SortField(UPDATED_SORT_FIELD, SortField.Type.LONG, true),
- new SortField(ID_SORT_FIELD, SortField.Type.LONG, true));
- }
-
- public ChangeSubIndex getClosedChangesIndex() {
- return closedIndex;
+ new SortField(idSortFieldName, SortField.Type.LONG, true));
}
private class QuerySource implements ChangeDataSource {
@@ -308,7 +337,7 @@ public class LuceneChangeIndex implements ChangeIndex {
throw new StorageException("interrupted");
}
- final Set<String> fields = IndexUtils.changeFields(opts);
+ final Set<String> fields = IndexUtils.changeFields(opts, schema.useLegacyNumericFields());
return new ChangeDataResults(
executor.submit(
new Callable<List<Document>>() {
@@ -329,7 +358,7 @@ public class LuceneChangeIndex implements ChangeIndex {
public ResultSet<FieldBundle> readRaw() {
List<Document> documents;
try {
- documents = doRead(IndexUtils.changeFields(opts));
+ documents = doRead(IndexUtils.changeFields(opts, schema.useLegacyNumericFields()));
} catch (IOException e) {
throw new StorageException(e);
}
@@ -407,9 +436,8 @@ public class LuceneChangeIndex implements ChangeIndex {
List<Document> docs = future.get();
ImmutableList.Builder<ChangeData> result =
ImmutableList.builderWithExpectedSize(docs.size());
- String idFieldName = LEGACY_ID.getName();
for (Document doc : docs) {
- result.add(toChangeData(fields(doc, fields), fields, idFieldName));
+ result.add(toChangeData(fields(doc, fields), fields, idField.getName()));
}
return result.build();
} catch (InterruptedException e) {
@@ -450,10 +478,10 @@ public class LuceneChangeIndex implements ChangeIndex {
cd = changeDataFactory.create(parseProtoFrom(proto, ChangeProtoConverter.INSTANCE));
} else {
IndexableField f = Iterables.getFirst(doc.get(idFieldName), null);
- Change.Id id = new Change.Id(f.numericValue().intValue());
+
// IndexUtils#changeFields ensures either CHANGE or PROJECT is always present.
IndexableField project = doc.get(PROJECT.getName()).iterator().next();
- cd = changeDataFactory.create(new Project.NameKey(project.stringValue()), id);
+ cd = changeDataFactory.create(Project.nameKey(project.stringValue()), extractor.extract(f));
}
// Any decoding that is done here must also be done in {@link ElasticChangeIndex}.
@@ -556,7 +584,7 @@ public class LuceneChangeIndex implements ChangeIndex {
if (reviewedBy.size() == 1 && id == ChangeField.NOT_REVIEWED) {
break;
}
- accounts.add(new Account.Id(id));
+ accounts.add(Account.id(id));
}
cd.setReviewedBy(accounts);
}
diff --git a/java/com/google/gerrit/lucene/LuceneGroupIndex.java b/java/com/google/gerrit/lucene/LuceneGroupIndex.java
index 0fdef77b96..99cd40dae4 100644
--- a/java/com/google/gerrit/lucene/LuceneGroupIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneGroupIndex.java
@@ -17,6 +17,7 @@ package com.google.gerrit.lucene;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.gerrit.server.index.group.GroupField.UUID;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
@@ -25,7 +26,6 @@ import com.google.gerrit.index.Schema.Values;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
@@ -139,7 +139,7 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Int
@Override
protected InternalGroup fromDocument(Document doc) {
- AccountGroup.UUID uuid = new AccountGroup.UUID(doc.getField(UUID.getName()).stringValue());
+ AccountGroup.UUID uuid = AccountGroup.uuid(doc.getField(UUID.getName()).stringValue());
// Use the GroupCache rather than depending on any stored fields in the
// document (of which there shouldn't be any).
return groupCache.get().get(uuid).orElse(null);
diff --git a/java/com/google/gerrit/lucene/LuceneProjectIndex.java b/java/com/google/gerrit/lucene/LuceneProjectIndex.java
index 950e2060ca..97454c7ac3 100644
--- a/java/com/google/gerrit/lucene/LuceneProjectIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneProjectIndex.java
@@ -17,6 +17,7 @@ package com.google.gerrit.lucene;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.gerrit.index.project.ProjectField.NAME;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
@@ -27,7 +28,6 @@ import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.IndexUtils;
@@ -139,7 +139,7 @@ public class LuceneProjectIndex extends AbstractLuceneIndex<Project.NameKey, Pro
@Override
protected ProjectData fromDocument(Document doc) {
- Project.NameKey nameKey = new Project.NameKey(doc.getField(NAME.getName()).stringValue());
+ Project.NameKey nameKey = Project.nameKey(doc.getField(NAME.getName()).stringValue());
ProjectState projectState = projectCache.get().get(nameKey);
return projectState == null ? null : projectState.toProjectData();
}
diff --git a/java/com/google/gerrit/lucene/QueryBuilder.java b/java/com/google/gerrit/lucene/QueryBuilder.java
index 8dfc12ecf4..7d82bf54b2 100644
--- a/java/com/google/gerrit/lucene/QueryBuilder.java
+++ b/java/com/google/gerrit/lucene/QueryBuilder.java
@@ -36,6 +36,8 @@ import com.google.gerrit.index.query.TimestampRangePredicate;
import java.util.Date;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.document.IntPoint;
+import org.apache.lucene.document.LongPoint;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.LegacyNumericRangeQuery;
@@ -49,6 +51,21 @@ import org.apache.lucene.util.LegacyNumericUtils;
@SuppressWarnings("deprecation")
public class QueryBuilder<V> {
+ @FunctionalInterface
+ static interface IntTermQuery {
+ Query get(String name, int value);
+ }
+
+ @FunctionalInterface
+ static interface IntRangeQuery {
+ Query get(String name, int min, int max);
+ }
+
+ @FunctionalInterface
+ static interface LongRangeQuery {
+ Query get(String name, long min, long max);
+ }
+
static Term intTerm(String name, int value) {
BytesRefBuilder builder = new BytesRefBuilder();
LegacyNumericUtils.intToPrefixCoded(value, 0, builder);
@@ -61,12 +78,36 @@ public class QueryBuilder<V> {
return new Term(name, builder.get());
}
+ static Query intPoint(String name, int value) {
+ return IntPoint.newExactQuery(name, value);
+ }
+
private final Schema<V> schema;
private final org.apache.lucene.util.QueryBuilder queryBuilder;
+ // TODO(davido): Remove the below fields when support for legacy numeric fields is removed.
+ private final IntTermQuery intTermQuery;
+ private final IntRangeQuery intRangeTermQuery;
+ private final LongRangeQuery longRangeQuery;
+
public QueryBuilder(Schema<V> schema, Analyzer analyzer) {
this.schema = schema;
queryBuilder = new org.apache.lucene.util.QueryBuilder(analyzer);
+ intTermQuery =
+ (name, value) ->
+ this.schema.useLegacyNumericFields()
+ ? new TermQuery(intTerm(name, value))
+ : intPoint(name, value);
+ intRangeTermQuery =
+ (name, min, max) ->
+ this.schema.useLegacyNumericFields()
+ ? LegacyNumericRangeQuery.newIntRange(name, min, max, true, true)
+ : IntPoint.newRangeQuery(name, min, max);
+ longRangeQuery =
+ (name, min, max) ->
+ this.schema.useLegacyNumericFields()
+ ? LegacyNumericRangeQuery.newLongRange(name, min, max, true, true)
+ : LongPoint.newRangeQuery(name, min, max);
}
public Query toQuery(Predicate<V> p) throws QueryParseException {
@@ -169,20 +210,20 @@ public class QueryBuilder<V> {
} catch (NumberFormatException e) {
throw new QueryParseException("not an integer: " + p.getValue(), e);
}
- return new TermQuery(intTerm(p.getField().getName(), value));
+ return intTermQuery.get(p.getField().getName(), value);
}
private Query intRangeQuery(IndexPredicate<V> p) throws QueryParseException {
if (p instanceof IntegerRangePredicate) {
IntegerRangePredicate<V> r = (IntegerRangePredicate<V>) p;
+ String name = r.getField().getName();
int minimum = r.getMinimumValue();
int maximum = r.getMaximumValue();
if (minimum == maximum) {
// Just fall back to a standard integer query.
- return new TermQuery(intTerm(p.getField().getName(), minimum));
+ return intTermQuery.get(name, minimum);
}
- return LegacyNumericRangeQuery.newIntRange(
- r.getField().getName(), minimum, maximum, true, true);
+ return intRangeTermQuery.get(name, minimum, maximum);
}
throw new QueryParseException("not an integer range: " + p);
}
@@ -190,20 +231,16 @@ public class QueryBuilder<V> {
private Query timestampQuery(IndexPredicate<V> p) throws QueryParseException {
if (p instanceof TimestampRangePredicate) {
TimestampRangePredicate<V> r = (TimestampRangePredicate<V>) p;
- return LegacyNumericRangeQuery.newLongRange(
- r.getField().getName(),
- r.getMinTimestamp().getTime(),
- r.getMaxTimestamp().getTime(),
- true,
- true);
+ return longRangeQuery.get(
+ r.getField().getName(), r.getMinTimestamp().getTime(), r.getMaxTimestamp().getTime());
}
throw new QueryParseException("not a timestamp: " + p);
}
private Query notTimestamp(TimestampRangePredicate<V> r) throws QueryParseException {
if (r.getMinTimestamp().getTime() == 0) {
- return LegacyNumericRangeQuery.newLongRange(
- r.getField().getName(), r.getMaxTimestamp().getTime(), null, true, true);
+ return longRangeQuery.get(
+ r.getField().getName(), r.getMaxTimestamp().getTime(), Long.MAX_VALUE);
}
throw new QueryParseException("cannot negate: " + r);
}
@@ -245,4 +282,8 @@ public class QueryBuilder<V> {
public int toIndexTimeInMinutes(Date ts) {
return (int) (ts.getTime() / 60000);
}
+
+ public Schema<V> getSchema() {
+ return schema;
+ }
}
diff --git a/java/com/google/gerrit/lucene/WrappableSearcherManager.java b/java/com/google/gerrit/lucene/WrappableSearcherManager.java
index ba8d7dab11..4044b90d3a 100644
--- a/java/com/google/gerrit/lucene/WrappableSearcherManager.java
+++ b/java/com/google/gerrit/lucene/WrappableSearcherManager.java
@@ -177,7 +177,7 @@ final class WrappableSearcherManager extends ReferenceManager<IndexSearcher> {
* Expert: creates a searcher from the provided {@link IndexReader} using the provided {@link
* SearcherFactory}. NOTE: this decRefs incoming reader on throwing an exception.
*/
- @SuppressWarnings("resource")
+ @SuppressWarnings({"resource", "ReferenceEquality"})
public static IndexSearcher getSearcher(SearcherFactory searcherFactory, IndexReader reader)
throws IOException {
boolean success = false;
diff --git a/java/com/google/gerrit/mail/BUILD b/java/com/google/gerrit/mail/BUILD
index 6be5f0e4e4..46bc77aa56 100644
--- a/java/com/google/gerrit/mail/BUILD
+++ b/java/com/google/gerrit/mail/BUILD
@@ -6,7 +6,7 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:annotations",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//lib:guava",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
diff --git a/java/com/google/gerrit/mail/HtmlParser.java b/java/com/google/gerrit/mail/HtmlParser.java
index 265c4122de..7905a0a2c0 100644
--- a/java/com/google/gerrit/mail/HtmlParser.java
+++ b/java/com/google/gerrit/mail/HtmlParser.java
@@ -18,7 +18,7 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
diff --git a/java/com/google/gerrit/mail/MailComment.java b/java/com/google/gerrit/mail/MailComment.java
index fd8198cf32..f024f17995 100644
--- a/java/com/google/gerrit/mail/MailComment.java
+++ b/java/com/google/gerrit/mail/MailComment.java
@@ -14,7 +14,7 @@
package com.google.gerrit.mail;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import java.util.Objects;
/** A comment parsed from inbound email */
diff --git a/java/com/google/gerrit/mail/ParserUtil.java b/java/com/google/gerrit/mail/ParserUtil.java
index 6a27ac4393..4b292f3e68 100644
--- a/java/com/google/gerrit/mail/ParserUtil.java
+++ b/java/com/google/gerrit/mail/ParserUtil.java
@@ -16,7 +16,7 @@ package com.google.gerrit.mail;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import java.util.List;
import java.util.StringJoiner;
import java.util.regex.Pattern;
diff --git a/java/com/google/gerrit/mail/TextParser.java b/java/com/google/gerrit/mail/TextParser.java
index 1a635994b7..dac3deb8f5 100644
--- a/java/com/google/gerrit/mail/TextParser.java
+++ b/java/com/google/gerrit/mail/TextParser.java
@@ -18,7 +18,7 @@ import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
diff --git a/java/com/google/gerrit/metrics/BUILD b/java/com/google/gerrit/metrics/BUILD
index 786ef68bf5..3cc056bf5a 100644
--- a/java/com/google/gerrit/metrics/BUILD
+++ b/java/com/google/gerrit/metrics/BUILD
@@ -8,9 +8,12 @@ java_library(
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/lifecycle",
+ "//java/com/google/gerrit/server/logging",
"//lib:guava",
+ "//lib:jgit",
+ "//lib/auto:auto-value",
+ "//lib/auto:auto-value-annotations",
"//lib/flogger:api",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/metrics/DisabledMetricMaker.java b/java/com/google/gerrit/metrics/DisabledMetricMaker.java
index adf6a66d5e..1fb8c57e3f 100644
--- a/java/com/google/gerrit/metrics/DisabledMetricMaker.java
+++ b/java/com/google/gerrit/metrics/DisabledMetricMaker.java
@@ -79,7 +79,7 @@ public class DisabledMetricMaker extends MetricMaker {
@Override
public <F1> Timer1<F1> newTimer(String name, Description desc, Field<F1> field1) {
- return new Timer1<F1>(name) {
+ return new Timer1<F1>(name, field1) {
@Override
protected void doRecord(F1 field1, long value, TimeUnit unit) {}
@@ -91,7 +91,7 @@ public class DisabledMetricMaker extends MetricMaker {
@Override
public <F1, F2> Timer2<F1, F2> newTimer(
String name, Description desc, Field<F1> field1, Field<F2> field2) {
- return new Timer2<F1, F2>(name) {
+ return new Timer2<F1, F2>(name, field1, field2) {
@Override
protected void doRecord(F1 field1, F2 field2, long value, TimeUnit unit) {}
@@ -103,7 +103,7 @@ public class DisabledMetricMaker extends MetricMaker {
@Override
public <F1, F2, F3> Timer3<F1, F2, F3> newTimer(
String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
- return new Timer3<F1, F2, F3>(name) {
+ return new Timer3<F1, F2, F3>(name, field1, field2, field3) {
@Override
protected void doRecord(F1 field1, F2 field2, F3 field3, long value, TimeUnit unit) {}
diff --git a/java/com/google/gerrit/metrics/Field.java b/java/com/google/gerrit/metrics/Field.java
index 95eb9cff19..bdae854007 100644
--- a/java/com/google/gerrit/metrics/Field.java
+++ b/java/com/google/gerrit/metrics/Field.java
@@ -16,45 +16,36 @@ package com.google.gerrit.metrics;
import static com.google.common.base.Preconditions.checkArgument;
-import com.google.common.base.Function;
-import com.google.common.base.Functions;
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.server.logging.Metadata;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
/**
* Describes a bucketing field used by a metric.
*
* @param <T> type of field
*/
-public class Field<T> {
- /**
- * Break down metrics by boolean true/false.
- *
- * @param name field name
- * @return boolean field
- */
- public static Field<Boolean> ofBoolean(String name) {
- return ofBoolean(name, null);
+@AutoValue
+public abstract class Field<T> {
+ public static <T> BiConsumer<Metadata.Builder, T> ignoreMetadata() {
+ return (metadataBuilder, fieldValue) -> {};
}
/**
* Break down metrics by boolean true/false.
*
* @param name field name
- * @param description field description
- * @return boolean field
- */
- public static Field<Boolean> ofBoolean(String name, String description) {
- return new Field<>(name, Boolean.class, description);
- }
-
- /**
- * Break down metrics by cases of an enum.
- *
- * @param enumType type of enum
- * @param name field name
- * @return enum field
+ * @return builder for the boolean field
*/
- public static <E extends Enum<E>> Field<E> ofEnum(Class<E> enumType, String name) {
- return ofEnum(enumType, name, null);
+ public static Field.Builder<Boolean> ofBoolean(
+ String name, BiConsumer<Metadata.Builder, Boolean> metadataMapper) {
+ return new AutoValue_Field.Builder<Boolean>()
+ .valueType(Boolean.class)
+ .formatter(Object::toString)
+ .name(name)
+ .metadataMapper(metadataMapper);
}
/**
@@ -62,39 +53,17 @@ public class Field<T> {
*
* @param enumType type of enum
* @param name field name
- * @param description field description
- * @return enum field
- */
- public static <E extends Enum<E>> Field<E> ofEnum(
- Class<E> enumType, String name, String description) {
- return new Field<>(name, enumType, description);
- }
-
- /**
- * Break down metrics by string.
- *
- * <p>Each unique string will allocate a new submetric. <b>Do not use user content as a field
- * value</b> as field values are never reclaimed.
- *
- * @param name field name
- * @return string field
- */
- public static Field<String> ofString(String name) {
- return ofString(name, null);
- }
-
- /**
- * Break down metrics by string.
- *
- * <p>Each unique string will allocate a new submetric. <b>Do not use user content as a field
- * value</b> as field values are never reclaimed.
- *
- * @param name field name
- * @param description field description
- * @return string field
+ * @return builder for the enum field
*/
- public static Field<String> ofString(String name, String description) {
- return new Field<>(name, String.class, description);
+ public static <E extends Enum<E>> Field.Builder<E> ofEnum(
+ Class<E> enumType, String name, BiConsumer<Metadata.Builder, String> metadataMapper) {
+ return new AutoValue_Field.Builder<E>()
+ .valueType(enumType)
+ .formatter(Enum::name)
+ .name(name)
+ .metadataMapper(
+ (metadataBuilder, fieldValue) ->
+ metadataMapper.accept(metadataBuilder, fieldValue.name()));
}
/**
@@ -104,67 +73,68 @@ public class Field<T> {
* value</b> as field values are never reclaimed.
*
* @param name field name
- * @return integer field
+ * @return builder for the integer field
*/
- public static Field<Integer> ofInteger(String name) {
- return ofInteger(name, null);
+ public static Field.Builder<Integer> ofInteger(
+ String name, BiConsumer<Metadata.Builder, Integer> metadataMapper) {
+ return new AutoValue_Field.Builder<Integer>()
+ .valueType(Integer.class)
+ .formatter(Object::toString)
+ .name(name)
+ .metadataMapper(metadataMapper);
}
/**
- * Break down metrics by integer.
+ * Break down metrics by string.
*
- * <p>Each unique integer will allocate a new submetric. <b>Do not use user content as a field
+ * <p>Each unique string will allocate a new submetric. <b>Do not use user content as a field
* value</b> as field values are never reclaimed.
*
* @param name field name
- * @param description field description
- * @return integer field
+ * @return builder for the string field
*/
- public static Field<Integer> ofInteger(String name, String description) {
- return new Field<>(name, Integer.class, description);
- }
-
- private final String name;
- private final Class<T> keyType;
- private final Function<T, String> formatter;
- private final String description;
-
- private Field(String name, Class<T> keyType, String description) {
- checkArgument(name.matches("^[a-z_]+$"), "name must match [a-z_]");
- this.name = name;
- this.keyType = keyType;
- this.formatter = initFormatter(keyType);
- this.description = description;
+ public static Field.Builder<String> ofString(
+ String name, BiConsumer<Metadata.Builder, String> metadataMapper) {
+ return new AutoValue_Field.Builder<String>()
+ .valueType(String.class)
+ .formatter(s -> s)
+ .name(name)
+ .metadataMapper(metadataMapper);
}
/** @return name of this field within the metric. */
- public String getName() {
- return name;
- }
+ public abstract String name();
/** @return type of value used within the field. */
- public Class<T> getType() {
- return keyType;
- }
+ public abstract Class<T> valueType();
+
+ /** @return mapper that maps a field value to a field in the {@link Metadata} class. */
+ public abstract BiConsumer<Metadata.Builder, T> metadataMapper();
/** @return description text for the field explaining its range of values. */
- public String getDescription() {
- return description;
- }
+ public abstract Optional<String> description();
- public Function<T, String> formatter() {
- return formatter;
- }
+ /** @return formatter to format field values. */
+ public abstract Function<T, String> formatter();
+
+ @AutoValue.Builder
+ public abstract static class Builder<T> {
+ abstract Builder<T> name(String name);
+
+ abstract Builder<T> valueType(Class<T> type);
+
+ abstract Builder<T> formatter(Function<T, String> formatter);
+
+ abstract Builder<T> metadataMapper(BiConsumer<Metadata.Builder, T> metadataMapper);
+
+ public abstract Builder<T> description(String description);
+
+ abstract Field<T> autoBuild();
- @SuppressWarnings("unchecked")
- private static <T> Function<T, String> initFormatter(Class<T> keyType) {
- if (keyType == String.class) {
- return (Function<T, String>) Functions.<String>identity();
- } else if (keyType == Integer.class || keyType == Boolean.class) {
- return (Function<T, String>) Functions.toStringFunction();
- } else if (Enum.class.isAssignableFrom(keyType)) {
- return in -> ((Enum<?>) in).name();
+ public Field<T> build() {
+ Field<T> field = autoBuild();
+ checkArgument(field.name().matches("^[a-z_]+$"), "name must match [a-z_]");
+ return field;
}
- throw new IllegalStateException("unsupported type " + keyType.getName());
}
}
diff --git a/java/com/google/gerrit/metrics/Timer0.java b/java/com/google/gerrit/metrics/Timer0.java
index 21344884cf..d0033a41f4 100644
--- a/java/com/google/gerrit/metrics/Timer0.java
+++ b/java/com/google/gerrit/metrics/Timer0.java
@@ -18,6 +18,8 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.PerformanceLogRecord;
import java.util.concurrent.TimeUnit;
/**
@@ -68,7 +70,10 @@ public abstract class Timer0 implements RegistrationHandle {
* @param unit time unit of the value
*/
public final void record(long value, TimeUnit unit) {
- logger.atFinest().log("%s took %dms", name, unit.toMillis(value));
+ long durationMs = unit.toMillis(value);
+ LoggingContext.getInstance()
+ .addPerformanceLogRecord(() -> PerformanceLogRecord.create(name, durationMs));
+ logger.atFinest().log("%s took %dms", name, durationMs);
doRecord(value, unit);
}
diff --git a/java/com/google/gerrit/metrics/Timer1.java b/java/com/google/gerrit/metrics/Timer1.java
index 16c151ebde..a8fb1a2bb9 100644
--- a/java/com/google/gerrit/metrics/Timer1.java
+++ b/java/com/google/gerrit/metrics/Timer1.java
@@ -18,6 +18,9 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.PerformanceLogRecord;
import java.util.concurrent.TimeUnit;
/**
@@ -35,56 +38,66 @@ import java.util.concurrent.TimeUnit;
public abstract class Timer1<F1> implements RegistrationHandle {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- public static class Context extends TimerContext {
- private final Timer1<Object> timer;
- private final Object field1;
+ public static class Context<F1> extends TimerContext {
+ private final Timer1<F1> timer;
+ private final F1 fieldValue;
- @SuppressWarnings("unchecked")
- <F1> Context(Timer1<F1> timer, F1 field1) {
- this.timer = (Timer1<Object>) timer;
- this.field1 = field1;
+ Context(Timer1<F1> timer, F1 fieldValue) {
+ this.timer = timer;
+ this.fieldValue = fieldValue;
}
@Override
public void record(long elapsed) {
- timer.record(field1, elapsed, NANOSECONDS);
+ timer.record(fieldValue, elapsed, NANOSECONDS);
}
}
protected final String name;
+ protected final Field<F1> field;
- public Timer1(String name) {
+ public Timer1(String name, Field<F1> field) {
this.name = name;
+ this.field = field;
}
/**
* Begin a timer for the current block, value will be recorded when closed.
*
- * @param field1 bucket to record the timer
+ * @param fieldValue bucket to record the timer
* @return timer context
*/
- public Context start(F1 field1) {
- return new Context(this, field1);
+ public Context<F1> start(F1 fieldValue) {
+ return new Context<>(this, fieldValue);
}
/**
* Record a value in the distribution.
*
- * @param field1 bucket to record the timer
+ * @param fieldValue bucket to record the timer
* @param value value to record
* @param unit time unit of the value
*/
- public final void record(F1 field1, long value, TimeUnit unit) {
- logger.atFinest().log("%s (%s) took %dms", name, field1, unit.toMillis(value));
- doRecord(field1, value, unit);
+ public final void record(F1 fieldValue, long value, TimeUnit unit) {
+ long durationMs = unit.toMillis(value);
+
+ Metadata.Builder metadataBuilder = Metadata.builder();
+ field.metadataMapper().accept(metadataBuilder, fieldValue);
+ Metadata metadata = metadataBuilder.build();
+
+ LoggingContext.getInstance()
+ .addPerformanceLogRecord(() -> PerformanceLogRecord.create(name, durationMs, metadata));
+
+ logger.atFinest().log("%s (%s = %s) took %dms", name, field.name(), fieldValue, durationMs);
+ doRecord(fieldValue, value, unit);
}
/**
* Record a value in the distribution.
*
- * @param field1 bucket to record the timer
+ * @param fieldValue bucket to record the timer
* @param value value to record
* @param unit time unit of the value
*/
- protected abstract void doRecord(F1 field1, long value, TimeUnit unit);
+ protected abstract void doRecord(F1 fieldValue, long value, TimeUnit unit);
}
diff --git a/java/com/google/gerrit/metrics/Timer2.java b/java/com/google/gerrit/metrics/Timer2.java
index bf19448d70..8a4a793af5 100644
--- a/java/com/google/gerrit/metrics/Timer2.java
+++ b/java/com/google/gerrit/metrics/Timer2.java
@@ -18,6 +18,9 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.PerformanceLogRecord;
import java.util.concurrent.TimeUnit;
/**
@@ -36,61 +39,76 @@ import java.util.concurrent.TimeUnit;
public abstract class Timer2<F1, F2> implements RegistrationHandle {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- public static class Context extends TimerContext {
- private final Timer2<Object, Object> timer;
- private final Object field1;
- private final Object field2;
+ public static class Context<F1, F2> extends TimerContext {
+ private final Timer2<F1, F2> timer;
+ private final F1 fieldValue1;
+ private final F2 fieldValue2;
- @SuppressWarnings("unchecked")
- <F1, F2> Context(Timer2<F1, F2> timer, F1 field1, F2 field2) {
- this.timer = (Timer2<Object, Object>) timer;
- this.field1 = field1;
- this.field2 = field2;
+ Context(Timer2<F1, F2> timer, F1 fieldValue1, F2 fieldValue2) {
+ this.timer = timer;
+ this.fieldValue1 = fieldValue1;
+ this.fieldValue2 = fieldValue2;
}
@Override
public void record(long elapsed) {
- timer.record(field1, field2, elapsed, NANOSECONDS);
+ timer.record(fieldValue1, fieldValue2, elapsed, NANOSECONDS);
}
}
protected final String name;
+ protected final Field<F1> field1;
+ protected final Field<F2> field2;
- public Timer2(String name) {
+ public Timer2(String name, Field<F1> field1, Field<F2> field2) {
this.name = name;
+ this.field1 = field1;
+ this.field2 = field2;
}
/**
* Begin a timer for the current block, value will be recorded when closed.
*
- * @param field1 bucket to record the timer
- * @param field2 bucket to record the timer
+ * @param fieldValue1 bucket to record the timer
+ * @param fieldValue2 bucket to record the timer
* @return timer context
*/
- public Context start(F1 field1, F2 field2) {
- return new Context(this, field1, field2);
+ public Context<F1, F2> start(F1 fieldValue1, F2 fieldValue2) {
+ return new Context<>(this, fieldValue1, fieldValue2);
}
/**
* Record a value in the distribution.
*
- * @param field1 bucket to record the timer
- * @param field2 bucket to record the timer
+ * @param fieldValue1 bucket to record the timer
+ * @param fieldValue2 bucket to record the timer
* @param value value to record
* @param unit time unit of the value
*/
- public final void record(F1 field1, F2 field2, long value, TimeUnit unit) {
- logger.atFinest().log("%s (%s, %s) took %dms", name, field1, field2, unit.toMillis(value));
- doRecord(field1, field2, value, unit);
+ public final void record(F1 fieldValue1, F2 fieldValue2, long value, TimeUnit unit) {
+ long durationMs = unit.toMillis(value);
+
+ Metadata.Builder metadataBuilder = Metadata.builder();
+ field1.metadataMapper().accept(metadataBuilder, fieldValue1);
+ field2.metadataMapper().accept(metadataBuilder, fieldValue2);
+ Metadata metadata = metadataBuilder.build();
+
+ LoggingContext.getInstance()
+ .addPerformanceLogRecord(() -> PerformanceLogRecord.create(name, durationMs, metadata));
+
+ logger.atFinest().log(
+ "%s (%s = %s, %s = %s) took %dms",
+ name, field1.name(), fieldValue1, field2.name(), fieldValue2, durationMs);
+ doRecord(fieldValue1, fieldValue2, value, unit);
}
/**
* Record a value in the distribution.
*
- * @param field1 bucket to record the timer
- * @param field2 bucket to record the timer
+ * @param fieldValue1 bucket to record the timer
+ * @param fieldValue2 bucket to record the timer
* @param value value to record
* @param unit time unit of the value
*/
- protected abstract void doRecord(F1 field1, F2 field2, long value, TimeUnit unit);
+ protected abstract void doRecord(F1 fieldValue1, F2 fieldValue2, long value, TimeUnit unit);
}
diff --git a/java/com/google/gerrit/metrics/Timer3.java b/java/com/google/gerrit/metrics/Timer3.java
index c910eb0c85..2044da6ef1 100644
--- a/java/com/google/gerrit/metrics/Timer3.java
+++ b/java/com/google/gerrit/metrics/Timer3.java
@@ -18,6 +18,9 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.PerformanceLogRecord;
import java.util.concurrent.TimeUnit;
/**
@@ -37,67 +40,93 @@ import java.util.concurrent.TimeUnit;
public abstract class Timer3<F1, F2, F3> implements RegistrationHandle {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- public static class Context extends TimerContext {
- private final Timer3<Object, Object, Object> timer;
- private final Object field1;
- private final Object field2;
- private final Object field3;
+ public static class Context<F1, F2, F3> extends TimerContext {
+ private final Timer3<F1, F2, F3> timer;
+ private final F1 fieldValue1;
+ private final F2 fieldValue2;
+ private final F3 fieldValue3;
- @SuppressWarnings("unchecked")
- <F1, F2, F3> Context(Timer3<F1, F2, F3> timer, F1 f1, F2 f2, F3 f3) {
- this.timer = (Timer3<Object, Object, Object>) timer;
- this.field1 = f1;
- this.field2 = f2;
- this.field3 = f3;
+ Context(Timer3<F1, F2, F3> timer, F1 f1, F2 f2, F3 f3) {
+ this.timer = timer;
+ this.fieldValue1 = f1;
+ this.fieldValue2 = f2;
+ this.fieldValue3 = f3;
}
@Override
public void record(long elapsed) {
- timer.record(field1, field2, field3, elapsed, NANOSECONDS);
+ timer.record(fieldValue1, fieldValue2, fieldValue3, elapsed, NANOSECONDS);
}
}
protected final String name;
+ protected final Field<F1> field1;
+ protected final Field<F2> field2;
+ protected final Field<F3> field3;
- public Timer3(String name) {
+ public Timer3(String name, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
this.name = name;
+ this.field1 = field1;
+ this.field2 = field2;
+ this.field3 = field3;
}
/**
* Begin a timer for the current block, value will be recorded when closed.
*
- * @param field1 bucket to record the timer
- * @param field2 bucket to record the timer
- * @param field3 bucket to record the timer
+ * @param fieldValue1 bucket to record the timer
+ * @param fieldValue2 bucket to record the timer
+ * @param fieldValue3 bucket to record the timer
* @return timer context
*/
- public Context start(F1 field1, F2 field2, F3 field3) {
- return new Context(this, field1, field2, field3);
+ public Context<F1, F2, F3> start(F1 fieldValue1, F2 fieldValue2, F3 fieldValue3) {
+ return new Context<>(this, fieldValue1, fieldValue2, fieldValue3);
}
/**
* Record a value in the distribution.
*
- * @param field1 bucket to record the timer
- * @param field2 bucket to record the timer
- * @param field3 bucket to record the timer
+ * @param fieldValue1 bucket to record the timer
+ * @param fieldValue2 bucket to record the timer
+ * @param fieldValue3 bucket to record the timer
* @param value value to record
* @param unit time unit of the value
*/
- public final void record(F1 field1, F2 field2, F3 field3, long value, TimeUnit unit) {
+ public final void record(
+ F1 fieldValue1, F2 fieldValue2, F3 fieldValue3, long value, TimeUnit unit) {
+ long durationMs = unit.toMillis(value);
+
+ Metadata.Builder metadataBuilder = Metadata.builder();
+ field1.metadataMapper().accept(metadataBuilder, fieldValue1);
+ field2.metadataMapper().accept(metadataBuilder, fieldValue2);
+ field3.metadataMapper().accept(metadataBuilder, fieldValue3);
+ Metadata metadata = metadataBuilder.build();
+
+ LoggingContext.getInstance()
+ .addPerformanceLogRecord(() -> PerformanceLogRecord.create(name, durationMs, metadata));
+
logger.atFinest().log(
- "%s (%s, %s, %s) took %dms", name, field1, field2, field3, unit.toMillis(value));
- doRecord(field1, field2, field3, value, unit);
+ "%s (%s = %s, %s = %s, %s = %s) took %dms",
+ name,
+ field1.name(),
+ fieldValue1,
+ field2.name(),
+ fieldValue2,
+ field3.name(),
+ fieldValue3,
+ durationMs);
+ doRecord(fieldValue1, fieldValue2, fieldValue3, value, unit);
}
/**
* Record a value in the distribution.
*
- * @param field1 bucket to record the timer
- * @param field2 bucket to record the timer
- * @param field3 bucket to record the timer
+ * @param fieldValue1 bucket to record the timer
+ * @param fieldValue2 bucket to record the timer
+ * @param fieldValue3 bucket to record the timer
* @param value value to record
* @param unit time unit of the value
*/
- protected abstract void doRecord(F1 field1, F2 field2, F3 field3, long value, TimeUnit unit);
+ protected abstract void doRecord(
+ F1 fieldValue1, F2 fieldValue2, F3 fieldValue3, long value, TimeUnit unit);
}
diff --git a/java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl1.java b/java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl1.java
index 6d1daf4bf2..d718035f40 100644
--- a/java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl1.java
+++ b/java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl1.java
@@ -15,10 +15,10 @@
package com.google.gerrit.metrics.dropwizard;
import com.codahale.metrics.MetricRegistry;
-import com.google.common.base.Function;
import com.google.gerrit.metrics.CallbackMetric1;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
+import java.util.function.Function;
/** Optimized version of {@link BucketedCallback} for single dimension. */
class CallbackMetricImpl1<F1, V> extends BucketedCallback<V> {
diff --git a/java/com/google/gerrit/metrics/dropwizard/CounterImpl1.java b/java/com/google/gerrit/metrics/dropwizard/CounterImpl1.java
index 46434ce28b..0e554a850f 100644
--- a/java/com/google/gerrit/metrics/dropwizard/CounterImpl1.java
+++ b/java/com/google/gerrit/metrics/dropwizard/CounterImpl1.java
@@ -14,10 +14,10 @@
package com.google.gerrit.metrics.dropwizard;
-import com.google.common.base.Function;
import com.google.gerrit.metrics.Counter1;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
+import java.util.function.Function;
/** Optimized version of {@link BucketedCounter} for single dimension. */
class CounterImpl1<F1> extends BucketedCounter {
diff --git a/java/com/google/gerrit/metrics/dropwizard/CounterImplN.java b/java/com/google/gerrit/metrics/dropwizard/CounterImplN.java
index 38c31a1b9d..07afc2a92a 100644
--- a/java/com/google/gerrit/metrics/dropwizard/CounterImplN.java
+++ b/java/com/google/gerrit/metrics/dropwizard/CounterImplN.java
@@ -14,13 +14,13 @@
package com.google.gerrit.metrics.dropwizard;
-import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.metrics.Counter2;
import com.google.gerrit.metrics.Counter3;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
+import java.util.function.Function;
/** Generalized implementation of N-dimensional counter metrics. */
class CounterImplN extends BucketedCounter implements BucketedMetric {
diff --git a/java/com/google/gerrit/metrics/dropwizard/GetMetric.java b/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
index ae1e6ec04e..12dabfa59e 100644
--- a/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
+++ b/java/com/google/gerrit/metrics/dropwizard/GetMetric.java
@@ -15,6 +15,7 @@
package com.google.gerrit.metrics.dropwizard;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -36,10 +37,10 @@ class GetMetric implements RestReadView<MetricResource> {
}
@Override
- public MetricJson apply(MetricResource resource)
+ public Response<MetricJson> apply(MetricResource resource)
throws AuthException, PermissionBackendException {
permissionBackend.currentUser().check(GlobalPermission.VIEW_CACHES);
- return new MetricJson(
- resource.getMetric(), metrics.getAnnotations(resource.getName()), dataOnly);
+ return Response.ok(
+ new MetricJson(resource.getMetric(), metrics.getAnnotations(resource.getName()), dataOnly));
}
}
diff --git a/java/com/google/gerrit/metrics/dropwizard/HistogramImpl1.java b/java/com/google/gerrit/metrics/dropwizard/HistogramImpl1.java
index 3eb12faaf1..4578db1910 100644
--- a/java/com/google/gerrit/metrics/dropwizard/HistogramImpl1.java
+++ b/java/com/google/gerrit/metrics/dropwizard/HistogramImpl1.java
@@ -14,10 +14,10 @@
package com.google.gerrit.metrics.dropwizard;
-import com.google.common.base.Function;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Histogram1;
+import java.util.function.Function;
/** Optimized version of {@link BucketedHistogram} for single dimension. */
class HistogramImpl1<F1> extends BucketedHistogram implements BucketedMetric {
diff --git a/java/com/google/gerrit/metrics/dropwizard/HistogramImplN.java b/java/com/google/gerrit/metrics/dropwizard/HistogramImplN.java
index 3561c55a1d..446590cd90 100644
--- a/java/com/google/gerrit/metrics/dropwizard/HistogramImplN.java
+++ b/java/com/google/gerrit/metrics/dropwizard/HistogramImplN.java
@@ -14,13 +14,13 @@
package com.google.gerrit.metrics.dropwizard;
-import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Histogram2;
import com.google.gerrit.metrics.Histogram3;
+import java.util.function.Function;
/** Generalized implementation of N-dimensional Histogram metrics. */
class HistogramImplN extends BucketedHistogram implements BucketedMetric {
diff --git a/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java b/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
index 0c6945248c..7e472c9a68 100644
--- a/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
+++ b/java/com/google/gerrit/metrics/dropwizard/ListMetrics.java
@@ -16,6 +16,7 @@ package com.google.gerrit.metrics.dropwizard;
import com.codahale.metrics.Metric;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -50,7 +51,7 @@ class ListMetrics implements RestReadView<ConfigResource> {
}
@Override
- public Map<String, MetricJson> apply(ConfigResource resource)
+ public Response<Map<String, MetricJson>> apply(ConfigResource resource)
throws AuthException, PermissionBackendException {
permissionBackend.currentUser().check(GlobalPermission.VIEW_CACHES);
@@ -75,7 +76,7 @@ class ListMetrics implements RestReadView<ConfigResource> {
}
}
- return out;
+ return Response.ok(out);
}
private MetricJson toJson(String q, Metric m) {
diff --git a/java/com/google/gerrit/metrics/dropwizard/MetricJson.java b/java/com/google/gerrit/metrics/dropwizard/MetricJson.java
index 20f4fa33a3..d59a1d93a1 100644
--- a/java/com/google/gerrit/metrics/dropwizard/MetricJson.java
+++ b/java/com/google/gerrit/metrics/dropwizard/MetricJson.java
@@ -21,7 +21,6 @@ import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
-import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.metrics.Description;
@@ -30,6 +29,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.function.Function;
class MetricJson {
String description;
@@ -189,10 +189,10 @@ class MetricJson {
String description;
FieldJson(Field<?> field) {
- this.name = field.getName();
- this.description = field.getDescription();
+ this.name = field.name();
+ this.description = field.description().orElse(null);
this.type =
- Enum.class.isAssignableFrom(field.getType()) ? field.getType().getSimpleName() : null;
+ Enum.class.isAssignableFrom(field.valueType()) ? field.valueType().getSimpleName() : null;
}
}
}
diff --git a/java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java b/java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java
index d97e73a961..b7d535be46 100644
--- a/java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java
+++ b/java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java
@@ -14,11 +14,11 @@
package com.google.gerrit.metrics.dropwizard;
-import com.google.common.base.Function;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Timer1;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
/** Optimized version of {@link BucketedTimer} for single dimension. */
class TimerImpl1<F1> extends BucketedTimer implements BucketedMetric {
@@ -26,8 +26,9 @@ class TimerImpl1<F1> extends BucketedTimer implements BucketedMetric {
super(metrics, name, desc, field1);
}
+ @SuppressWarnings("unchecked")
Timer1<F1> timer() {
- return new Timer1<F1>(name) {
+ return new Timer1<F1>(name, (Field<F1>) fields[0]) {
@Override
protected void doRecord(F1 field1, long value, TimeUnit unit) {
total.record(value, unit);
diff --git a/java/com/google/gerrit/metrics/dropwizard/TimerImplN.java b/java/com/google/gerrit/metrics/dropwizard/TimerImplN.java
index be66009c5a..dee800e5ea 100644
--- a/java/com/google/gerrit/metrics/dropwizard/TimerImplN.java
+++ b/java/com/google/gerrit/metrics/dropwizard/TimerImplN.java
@@ -14,7 +14,6 @@
package com.google.gerrit.metrics.dropwizard;
-import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.metrics.Description;
@@ -22,6 +21,7 @@ import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Timer2;
import com.google.gerrit.metrics.Timer3;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
/** Generalized implementation of N-dimensional timer metrics. */
class TimerImplN extends BucketedTimer implements BucketedMetric {
@@ -29,8 +29,9 @@ class TimerImplN extends BucketedTimer implements BucketedMetric {
super(metrics, name, desc, fields);
}
+ @SuppressWarnings("unchecked")
<F1, F2> Timer2<F1, F2> timer2() {
- return new Timer2<F1, F2>(name) {
+ return new Timer2<F1, F2>(name, (Field<F1>) fields[0], (Field<F2>) fields[1]) {
@Override
protected void doRecord(F1 field1, F2 field2, long value, TimeUnit unit) {
total.record(value, unit);
@@ -44,8 +45,10 @@ class TimerImplN extends BucketedTimer implements BucketedMetric {
};
}
+ @SuppressWarnings("unchecked")
<F1, F2, F3> Timer3<F1, F2, F3> timer3() {
- return new Timer3<F1, F2, F3>(name) {
+ return new Timer3<F1, F2, F3>(
+ name, (Field<F1>) fields[0], (Field<F2>) fields[1], (Field<F3>) fields[2]) {
@Override
protected void doRecord(F1 field1, F2 field2, F3 field3, long value, TimeUnit unit) {
total.record(value, unit);
diff --git a/java/com/google/gerrit/metrics/proc/JGitMetricModule.java b/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
index a44f907cc7..e8611b3365 100644
--- a/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
+++ b/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
@@ -20,6 +20,7 @@ import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.logging.Metadata;
import java.util.Map;
import org.eclipse.jgit.storage.file.WindowCacheStats;
@@ -183,7 +184,7 @@ public class JGitMetricModule extends MetricModule {
+ "having most data in the cache.")
.setGauge()
.setUnit("byte"),
- Field.ofString("repository_name"));
+ Field.ofString("repository_name", Metadata.Builder::projectName).build());
metrics.newTrigger(
repoEnt,
() -> {
diff --git a/java/com/google/gerrit/metrics/proc/ProcMetricModule.java b/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
index 2c29882b65..20ac8fa908 100644
--- a/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
+++ b/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
@@ -23,6 +23,7 @@ import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.logging.Metadata;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
@@ -167,12 +168,17 @@ public class ProcMetricModule extends MetricModule {
}
private void procJvmGc(MetricMaker metrics) {
+ Field<String> gcNameField =
+ Field.ofString("gc_name", Metadata.Builder::garbageCollectorName)
+ .description("The name of the garbage collector")
+ .build();
+
CallbackMetric1<String, Long> gcCount =
metrics.newCallbackMetric(
"proc/jvm/gc/count",
Long.class,
new Description("Number of GCs").setCumulative(),
- Field.ofString("gc_name", "The name of the garbage collector"));
+ gcNameField);
CallbackMetric1<String, Long> gcTime =
metrics.newCallbackMetric(
@@ -181,7 +187,7 @@ public class ProcMetricModule extends MetricModule {
new Description("Approximate accumulated GC elapsed time")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- Field.ofString("gc_name", "The name of the garbage collector"));
+ gcNameField);
metrics.newTrigger(
gcCount,
diff --git a/java/com/google/gerrit/pgm/BUILD b/java/com/google/gerrit/pgm/BUILD
index a8690b908c..8b8f13c1df 100644
--- a/java/com/google/gerrit/pgm/BUILD
+++ b/java/com/google/gerrit/pgm/BUILD
@@ -1,17 +1,7 @@
load("@rules_java//java:defs.bzl", "java_library")
-# TODO(davido): This indirection doesn't avoid unwanted depdencies
-# in acceptance-framework and should be removed. Instead, provided_deps
-# should be used, once https://github.com/bazelbuild/bazel/issues/1402
-# is fixed.
-alias(
- name = "pgm",
- actual = ":daemon",
- visibility = ["//visibility:public"],
-)
-
java_library(
- name = "daemon",
+ name = "pgm",
srcs = glob(["**/*.java"]),
resource_strip_prefix = "resources",
resources = ["//resources/com/google/gerrit/pgm"],
@@ -20,6 +10,7 @@ java_library(
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/elasticsearch",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/gpg",
@@ -37,7 +28,6 @@ java_library(
"//java/com/google/gerrit/pgm/init",
"//java/com/google/gerrit/pgm/init/api",
"//java/com/google/gerrit/pgm/util",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server:module",
"//java/com/google/gerrit/server/api",
@@ -51,6 +41,7 @@ java_library(
"//java/com/google/gerrit/sshd",
"//lib:args4j",
"//lib:guava",
+ "//lib:jgit",
"//lib:protobuf",
"//lib:servlet-api-without-neverlink",
"//lib/auto:auto-value",
@@ -59,7 +50,6 @@ java_library(
"//lib/guice",
"//lib/guice:guice-assistedinject",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/log:jsonevent-layout",
"//lib/log:log4j",
"//lib/prolog:cafeteria",
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index a5c6dacbc4..238cf290ac 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -42,6 +42,7 @@ import com.google.gerrit.httpd.auth.oauth.OAuthModule;
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.httpd.plugins.HttpPluginModule;
import com.google.gerrit.httpd.raw.StaticModule;
+import com.google.gerrit.index.IndexType;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lucene.LuceneIndexModule;
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
@@ -83,7 +84,6 @@ import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.group.PeriodicGroupIndexer;
import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.index.OnlineUpgrader;
import com.google.gerrit.server.index.VersionManager;
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
@@ -149,8 +149,11 @@ public class Daemon extends SiteProgram {
sshd = false;
}
- @Option(name = "--slave", usage = "Support fetch only")
- private boolean slave;
+ @Option(
+ name = "--replica",
+ aliases = {"--slave"},
+ usage = "Support fetch only")
+ private boolean replica;
@Option(name = "--console-log", usage = "Log to console (not $site_path/logs)")
private boolean consoleLog;
@@ -217,8 +220,8 @@ public class Daemon extends SiteProgram {
httpd = enable;
}
- public void setSlave(boolean slave) {
- this.slave = slave;
+ public void setReplica(boolean replica) {
+ this.replica = replica;
}
@Override
@@ -243,7 +246,7 @@ public class Daemon extends SiteProgram {
}
if (httpd == null) {
- httpd = !slave;
+ httpd = !replica;
}
if (!httpd && !sshd) {
@@ -332,7 +335,7 @@ public class Daemon extends SiteProgram {
}
cfgInjector = createCfgInjector();
config = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
- initIndexType();
+ indexType = IndexModule.getIndexType(cfgInjector);
sysInjector = createSysInjector();
sysInjector.getInstance(PluginGuiceEnvironment.class).setDbCfgInjector(dbInjector, cfgInjector);
manager.add(dbInjector, cfgInjector, sysInjector);
@@ -374,8 +377,8 @@ public class Daemon extends SiteProgram {
private String myVersion() {
List<String> versionParts = new ArrayList<>();
- if (slave) {
- versionParts.add("[slave]");
+ if (replica) {
+ versionParts.add("[replica]");
}
if (headless) {
versionParts.add("[headless]");
@@ -411,7 +414,7 @@ public class Daemon extends SiteProgram {
modules.add(new GerritApiModule());
modules.add(new PluginApiModule());
- modules.add(new SearchingChangeCacheImpl.Module(slave));
+ modules.add(new SearchingChangeCacheImpl.Module(replica));
modules.add(new InternalAccountDirectory.Module());
modules.add(new DefaultPermissionBackendModule());
modules.add(new DefaultMemoryCacheModule());
@@ -463,7 +466,8 @@ public class Daemon extends SiteProgram {
new AbstractModule() {
@Override
protected void configure() {
- bind(GerritOptions.class).toInstance(new GerritOptions(headless, slave, polyGerritDev));
+ bind(GerritOptions.class)
+ .toInstance(new GerritOptions(headless, replica, polyGerritDev));
if (inMemoryTest) {
bind(String.class)
.annotatedWith(SecureStoreClassName.class)
@@ -473,7 +477,7 @@ public class Daemon extends SiteProgram {
}
});
modules.add(new GarbageCollectionModule());
- if (slave) {
+ if (replica) {
modules.add(new PeriodicGroupIndexer.Module());
} else {
modules.add(new AccountDeactivator.Module());
@@ -491,25 +495,13 @@ public class Daemon extends SiteProgram {
if (luceneModule != null) {
return luceneModule;
}
- switch (indexType) {
- case LUCENE:
- return LuceneIndexModule.latestVersion(slave);
- case ELASTICSEARCH:
- return ElasticIndexModule.latestVersion(slave);
- default:
- throw new IllegalStateException("unsupported index.type = " + indexType);
+ if (indexType.isLucene()) {
+ return LuceneIndexModule.latestVersion(replica);
}
- }
-
- private void initIndexType() {
- indexType = IndexModule.getIndexType(cfgInjector);
- switch (indexType) {
- case LUCENE:
- case ELASTICSEARCH:
- break;
- default:
- throw new IllegalStateException("unsupported index.type = " + indexType);
+ if (indexType.isElasticsearch()) {
+ return ElasticIndexModule.latestVersion(replica);
}
+ throw new IllegalStateException("unsupported index.type = " + indexType);
}
private void initSshd() {
@@ -526,12 +518,12 @@ public class Daemon extends SiteProgram {
}
modules.add(
new DefaultCommandModule(
- slave,
+ replica,
sysInjector.getInstance(DownloadConfig.class),
sysInjector.getInstance(LfsPluginAuthCommand.Module.class)));
modules.addAll(testSshModules);
- if (!slave) {
+ if (!replica) {
modules.add(new IndexCommandsModule(sysInjector));
}
return sysInjector.createChildInjector(modules);
diff --git a/java/com/google/gerrit/pgm/Init.java b/java/com/google/gerrit/pgm/Init.java
index 6d9d00318d..4e62a0f88b 100644
--- a/java/com/google/gerrit/pgm/Init.java
+++ b/java/com/google/gerrit/pgm/Init.java
@@ -37,6 +37,7 @@ import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
import com.google.gerrit.server.ioutil.HostPlatform;
import com.google.gerrit.server.securestore.SecureStoreClassName;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -155,7 +156,7 @@ public class Init extends BaseInit {
});
modules.add(new GerritServerConfigModule());
Guice.createInjector(modules).injectMembers(this);
- if (!run.flags.cfg.getBoolean("container", "slave", false)) {
+ if (!ReplicaUtil.isReplica(run.flags.cfg)) {
for (SchemaDefinitions<?> schemaDef : schemaDefs) {
if (!indexStatus.exists(schemaDef.getName())) {
reindex(schemaDef);
diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java
index 6fd542406c..966801f382 100644
--- a/java/com/google/gerrit/pgm/Reindex.java
+++ b/java/com/google/gerrit/pgm/Reindex.java
@@ -23,6 +23,7 @@ import com.google.gerrit.elasticsearch.ElasticIndexModule;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexDefinition;
+import com.google.gerrit.index.IndexType;
import com.google.gerrit.index.SiteIndexer;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lucene.LuceneIndexModule;
@@ -31,9 +32,9 @@ import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
@@ -144,19 +145,17 @@ public class Reindex extends SiteProgram {
if (changesVersion != null) {
versions.put(ChangeSchemaDefinitions.INSTANCE.getName(), changesVersion);
}
- boolean slave = globalConfig.getBoolean("container", "slave", false);
+ boolean replica = ReplicaUtil.isReplica(globalConfig);
List<Module> modules = new ArrayList<>();
Module indexModule;
- switch (IndexModule.getIndexType(dbInjector)) {
- case LUCENE:
- indexModule = LuceneIndexModule.singleVersionWithExplicitVersions(versions, threads, slave);
- break;
- case ELASTICSEARCH:
- indexModule =
- ElasticIndexModule.singleVersionWithExplicitVersions(versions, threads, slave);
- break;
- default:
- throw new IllegalStateException("unsupported index.type");
+ IndexType indexType = IndexModule.getIndexType(dbInjector);
+ if (indexType.isLucene()) {
+ indexModule = LuceneIndexModule.singleVersionWithExplicitVersions(versions, threads, replica);
+ } else if (indexType.isElasticsearch()) {
+ indexModule =
+ ElasticIndexModule.singleVersionWithExplicitVersions(versions, threads, replica);
+ } else {
+ throw new IllegalStateException("unsupported index.type = " + indexType);
}
modules.add(indexModule);
modules.add(new BatchProgramModule());
@@ -173,7 +172,7 @@ public class Reindex extends SiteProgram {
private void overrideConfig() {
// Disable auto-commit for speed; committing will happen at the end of the process.
- if (IndexModule.getIndexType(dbInjector) == IndexType.LUCENE) {
+ if (IndexModule.getIndexType(dbInjector).isLucene()) {
globalConfig.setLong("index", "changes_open", "commitWithin", -1);
globalConfig.setLong("index", "changes_closed", "commitWithin", -1);
}
diff --git a/java/com/google/gerrit/pgm/Rulec.java b/java/com/google/gerrit/pgm/Rulec.java
index aa72ae00b9..d695217ec9 100644
--- a/java/com/google/gerrit/pgm/Rulec.java
+++ b/java/com/google/gerrit/pgm/Rulec.java
@@ -14,11 +14,11 @@
package com.google.gerrit.pgm;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.pgm.rules.PrologCompiler;
import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import com.google.inject.Injector;
@@ -73,7 +73,7 @@ public class Rulec extends SiteProgram {
LinkedHashSet<Project.NameKey> names = new LinkedHashSet<>();
for (String name : projectNames) {
- names.add(new Project.NameKey(name));
+ names.add(Project.nameKey(name));
}
if (all) {
names.addAll(gitManager.list());
diff --git a/java/com/google/gerrit/pgm/http/jetty/BUILD b/java/com/google/gerrit/pgm/http/jetty/BUILD
index 9d65ded896..cd188f51c3 100644
--- a/java/com/google/gerrit/pgm/http/jetty/BUILD
+++ b/java/com/google/gerrit/pgm/http/jetty/BUILD
@@ -16,6 +16,7 @@ java_library(
"//java/com/google/gerrit/util/logging",
"//lib:gson",
"//lib:guava",
+ "//lib:jgit",
"//lib:servlet-api",
"//lib/flogger:api",
"//lib/guice",
@@ -24,7 +25,6 @@ java_library(
"//lib/jetty:jmx",
"//lib/jetty:server",
"//lib/jetty:servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/log:log4j",
],
)
diff --git a/java/com/google/gerrit/pgm/init/AccountsOnInit.java b/java/com/google/gerrit/pgm/init/AccountsOnInit.java
index ff9490527b..536ddcda2c 100644
--- a/java/com/google/gerrit/pgm/init/AccountsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/AccountsOnInit.java
@@ -14,13 +14,13 @@
package com.google.gerrit.pgm.init;
-import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
import com.google.gerrit.pgm.init.api.InitFlags;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.account.AccountProperties;
import com.google.gerrit.server.account.Accounts;
@@ -60,63 +60,59 @@ public class AccountsOnInit {
this.allUsers = allUsers.get();
}
- public void insert(Account account) throws IOException {
+ public Account insert(Account.Builder account) throws IOException {
File path = getPath();
- if (path != null) {
- try (Repository repo = new FileRepository(path);
- ObjectInserter oi = repo.newObjectInserter()) {
- PersonIdent ident =
- new PersonIdent(
- new GerritPersonIdentProvider(flags.cfg).get(), account.getRegisteredOn());
+ try (Repository repo = new FileRepository(path);
+ ObjectInserter oi = repo.newObjectInserter()) {
+ PersonIdent ident =
+ new PersonIdent(new GerritPersonIdentProvider(flags.cfg).get(), account.registeredOn());
- Config accountConfig = new Config();
- AccountProperties.writeToAccountConfig(
- InternalAccountUpdate.builder()
- .setActive(account.isActive())
- .setFullName(account.getFullName())
- .setPreferredEmail(account.getPreferredEmail())
- .setStatus(account.getStatus())
- .build(),
- accountConfig);
+ Config accountConfig = new Config();
+ AccountProperties.writeToAccountConfig(
+ InternalAccountUpdate.builder()
+ .setActive(!account.inactive())
+ .setFullName(account.fullName())
+ .setPreferredEmail(account.preferredEmail())
+ .setStatus(account.status())
+ .build(),
+ accountConfig);
- DirCache newTree = DirCache.newInCore();
- DirCacheEditor editor = newTree.editor();
- final ObjectId blobId =
- oi.insert(Constants.OBJ_BLOB, accountConfig.toText().getBytes(UTF_8));
- editor.add(
- new PathEdit(AccountProperties.ACCOUNT_CONFIG) {
- @Override
- public void apply(DirCacheEntry ent) {
- ent.setFileMode(FileMode.REGULAR_FILE);
- ent.setObjectId(blobId);
- }
- });
- editor.finish();
+ DirCache newTree = DirCache.newInCore();
+ DirCacheEditor editor = newTree.editor();
+ final ObjectId blobId = oi.insert(Constants.OBJ_BLOB, accountConfig.toText().getBytes(UTF_8));
+ editor.add(
+ new PathEdit(AccountProperties.ACCOUNT_CONFIG) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.REGULAR_FILE);
+ ent.setObjectId(blobId);
+ }
+ });
+ editor.finish();
- ObjectId treeId = newTree.writeTree(oi);
+ ObjectId treeId = newTree.writeTree(oi);
- CommitBuilder cb = new CommitBuilder();
- cb.setTreeId(treeId);
- cb.setCommitter(ident);
- cb.setAuthor(ident);
- cb.setMessage("Create Account");
- ObjectId id = oi.insert(cb);
- oi.flush();
+ CommitBuilder cb = new CommitBuilder();
+ cb.setTreeId(treeId);
+ cb.setCommitter(ident);
+ cb.setAuthor(ident);
+ cb.setMessage("Create Account");
+ ObjectId id = oi.insert(cb);
+ oi.flush();
- String refName = RefNames.refsUsers(account.getId());
- RefUpdate ru = repo.updateRef(refName);
- ru.setExpectedOldObjectId(ObjectId.zeroId());
- ru.setNewObjectId(id);
- ru.setRefLogIdent(ident);
- ru.setRefLogMessage("Create Account", false);
- Result result = ru.update();
- if (result != Result.NEW) {
- throw new IOException(
- String.format("Failed to update ref %s: %s", refName, result.name()));
- }
- account.setMetaId(id.name());
+ String refName = RefNames.refsUsers(account.id());
+ RefUpdate ru = repo.updateRef(refName);
+ ru.setExpectedOldObjectId(ObjectId.zeroId());
+ ru.setNewObjectId(id);
+ ru.setRefLogIdent(ident);
+ ru.setRefLogMessage("Create Account", false);
+ Result result = ru.update();
+ if (result != Result.NEW) {
+ throw new IOException(String.format("Failed to update ref %s: %s", refName, result.name()));
}
+ account.setMetaId(id.name()).build();
}
+ return account.build();
}
public boolean hasAnyAccount() throws IOException {
@@ -132,7 +128,10 @@ public class AccountsOnInit {
private File getPath() {
Path basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath"));
- checkArgument(basePath != null, "gerrit.basePath must be configured");
- return FileKey.resolve(basePath.resolve(allUsers).toFile(), FS.DETECTED);
+ requireNonNull(basePath, "gerrit.basePath must be configured");
+ File file = basePath.resolve(allUsers).toFile();
+ File resolvedFile = FileKey.resolve(file, FS.DETECTED);
+ requireNonNull(resolvedFile, () -> String.format("%s does not exist", file.getAbsolutePath()));
+ return resolvedFile;
}
}
diff --git a/java/com/google/gerrit/pgm/init/BUILD b/java/com/google/gerrit/pgm/init/BUILD
index eb0d49e36a..62c95268af 100644
--- a/java/com/google/gerrit/pgm/init/BUILD
+++ b/java/com/google/gerrit/pgm/init/BUILD
@@ -10,6 +10,7 @@ java_library(
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/elasticsearch",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
@@ -18,17 +19,16 @@ java_library(
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/pgm/init/api",
"//java/com/google/gerrit/pgm/util",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/ioutil",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/server/util/time",
"//lib:guava",
"//lib:h2",
+ "//lib:jgit",
"//lib/commons:validator",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/pgm/init/BaseInit.java b/java/com/google/gerrit/pgm/init/BaseInit.java
index 0bc1860fcd..62ff66a649 100644
--- a/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -23,6 +23,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Die;
import com.google.gerrit.common.IoUtil;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.IndexType;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.pgm.init.api.ConsoleUI;
@@ -412,15 +413,13 @@ public class BaseInit extends SiteProgram {
});
Injector dbInjector = createDbInjector();
- switch (IndexModule.getIndexType(dbInjector)) {
- case LUCENE:
- modules.add(new LuceneIndexModuleOnInit());
- break;
- case ELASTICSEARCH:
- modules.add(new ElasticIndexModuleOnInit());
- break;
- default:
- throw new IllegalStateException("unsupported index.type");
+ IndexType indexType = IndexModule.getIndexType(dbInjector);
+ if (indexType.isLucene()) {
+ modules.add(new LuceneIndexModuleOnInit());
+ } else if (indexType.isElasticsearch()) {
+ modules.add(new ElasticIndexModuleOnInit());
+ } else {
+ throw new IllegalStateException("unsupported index.type = " + indexType);
}
sysInjector = dbInjector.createChildInjector(modules);
}
diff --git a/java/com/google/gerrit/pgm/init/GroupsOnInit.java b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
index 273ebfb9e0..0333942e7f 100644
--- a/java/com/google/gerrit/pgm/init/GroupsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
@@ -20,11 +20,11 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
import com.google.gerrit.pgm.init.api.InitFlags;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerIdProvider;
@@ -155,7 +155,7 @@ public class GroupsOnInit {
private static InternalGroupUpdate getMemberAdditionUpdate(Account account) {
return InternalGroupUpdate.builder()
- .setMemberModification(members -> Sets.union(members, ImmutableSet.of(account.getId())))
+ .setMemberModification(members -> Sets.union(members, ImmutableSet.of(account.id())))
.build();
}
diff --git a/java/com/google/gerrit/pgm/init/InitAdminUser.java b/java/com/google/gerrit/pgm/init/InitAdminUser.java
index 27e6ce90ab..cf208ae91c 100644
--- a/java/com/google/gerrit/pgm/init/InitAdminUser.java
+++ b/java/com/google/gerrit/pgm/init/InitAdminUser.java
@@ -18,18 +18,16 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.client.AuthType;
-import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.init.api.InitFlags;
import com.google.gerrit.pgm.init.api.InitStep;
import com.google.gerrit.pgm.init.api.SequencesOnInit;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountSshKey;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.index.account.AccountIndex;
import com.google.gerrit.server.index.account.AccountIndexCollection;
@@ -49,7 +47,6 @@ import org.apache.commons.validator.routines.EmailValidator;
public class InitAdminUser implements InitStep {
private final InitFlags flags;
private final ConsoleUI ui;
- private final AllUsersNameOnInitProvider allUsers;
private final AccountsOnInit accounts;
private final VersionedAuthorizedKeysOnInit.Factory authorizedKeysFactory;
private final ExternalIdsOnInit externalIds;
@@ -62,7 +59,6 @@ public class InitAdminUser implements InitStep {
InitAdminUser(
InitFlags flags,
ConsoleUI ui,
- AllUsersNameOnInitProvider allUsers,
AccountsOnInit accounts,
VersionedAuthorizedKeysOnInit.Factory authorizedKeysFactory,
ExternalIdsOnInit externalIds,
@@ -70,7 +66,6 @@ public class InitAdminUser implements InitStep {
GroupsOnInit groupsOnInit) {
this.flags = flags;
this.ui = ui;
- this.allUsers = allUsers;
this.accounts = accounts;
this.authorizedKeysFactory = authorizedKeysFactory;
this.externalIds = externalIds;
@@ -101,7 +96,7 @@ public class InitAdminUser implements InitStep {
if (!accounts.hasAnyAccount()) {
ui.header("Gerrit Administrator");
if (ui.yesno(true, "Create administrator user")) {
- Account.Id id = new Account.Id(sequencesOnInit.nextAccountId());
+ Account.Id id = Account.id(sequencesOnInit.nextAccountId());
String username = ui.readString("admin", "username");
String name = ui.readString("Administrator", "name");
String httpPassword = ui.readString("secret", "HTTP password");
@@ -116,11 +111,9 @@ public class InitAdminUser implements InitStep {
}
externalIds.insert("Add external IDs for initial admin user", extIds);
- Account a = new Account(id, TimeUtil.nowTs());
- a.setFullName(name);
- a.setPreferredEmail(email);
- accounts.insert(a);
-
+ Account persistedAccount =
+ accounts.insert(
+ Account.builder(id, TimeUtil.nowTs()).setFullName(name).setPreferredEmail(email));
// Only two groups should exist at this point in time and hence iterating over all of them
// is cheap.
Optional<GroupReference> adminGroupReference =
@@ -132,7 +125,7 @@ public class InitAdminUser implements InitStep {
throw new NoSuchGroupException("Administrators");
}
GroupReference adminGroup = adminGroupReference.get();
- groupsOnInit.addGroupMember(adminGroup.getUUID(), a);
+ groupsOnInit.addGroupMember(adminGroup.getUUID(), persistedAccount);
if (sshKey != null) {
VersionedAuthorizedKeysOnInit authorizedKeys = authorizedKeysFactory.create(id).load();
@@ -140,7 +133,7 @@ public class InitAdminUser implements InitStep {
authorizedKeys.save("Add SSH key for initial admin user\n");
}
- AccountState as = AccountState.forAccount(new AllUsersName(allUsers.get()), a, extIds);
+ AccountState as = AccountState.forAccount(persistedAccount, extIds);
for (AccountIndex accountIndex : accountIndexCollection.getWriteIndexes()) {
accountIndex.replace(as);
}
diff --git a/java/com/google/gerrit/pgm/init/InitIndex.java b/java/com/google/gerrit/pgm/init/InitIndex.java
index 0de08f2c40..83d9261042 100644
--- a/java/com/google/gerrit/pgm/init/InitIndex.java
+++ b/java/com/google/gerrit/pgm/init/InitIndex.java
@@ -15,6 +15,7 @@
package com.google.gerrit.pgm.init;
import com.google.common.collect.Iterables;
+import com.google.gerrit.index.IndexType;
import com.google.gerrit.index.SchemaDefinitions;
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.init.api.InitFlags;
@@ -22,7 +23,6 @@ import com.google.gerrit.pgm.init.api.InitStep;
import com.google.gerrit.pgm.init.api.Section;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.index.IndexUtils;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -53,27 +53,23 @@ class InitIndex implements InitStep {
@Override
public void run() throws IOException {
- IndexType type = IndexType.LUCENE;
- if (IndexType.values().length > 1) {
- ui.header("Index");
- type = index.select("Type", "type", type);
- }
+ ui.header("Index");
+ IndexType type =
+ new IndexType(
+ index.select("Type", "type", IndexType.getDefault(), IndexType.getKnownTypes()));
- if (type == IndexType.ELASTICSEARCH) {
+ if (type.isElasticsearch()) {
Section elasticsearch = sections.get("elasticsearch", null);
elasticsearch.string("Index Prefix", "prefix", "gerrit_");
elasticsearch.string("Server", "server", "http://localhost:9200");
index.string("Result window size", "maxLimit", "10000");
}
- if ((site.isNew || isEmptySite()) && type == IndexType.LUCENE) {
+ if ((site.isNew || isEmptySite()) && type.isLucene()) {
for (SchemaDefinitions<?> def : IndexModule.ALL_SCHEMA_DEFS) {
IndexUtils.setReady(site, def.getName(), def.getLatest().getVersion(), true);
}
} else {
- if (IndexType.values().length <= 1) {
- ui.header("Index");
- }
String message =
String.format(
"\nThe index must be %sbuilt before starting Gerrit:\n"
diff --git a/java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java b/java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java
index a9c6cc80c6..acde91ff60 100644
--- a/java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java
+++ b/java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java
@@ -17,11 +17,11 @@ package com.google.gerrit.pgm.init;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
import com.google.gerrit.pgm.init.api.InitFlags;
import com.google.gerrit.pgm.init.api.VersionedMetaDataOnInit;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountSshKey;
import com.google.gerrit.server.account.AuthorizedKeys;
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
diff --git a/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java b/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
index 20e7ba24b4..5ca239e80a 100644
--- a/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
+++ b/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
@@ -16,8 +16,8 @@ package com.google.gerrit.pgm.init.api;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.project.GroupList;
@@ -43,7 +43,7 @@ public class AllProjectsConfig extends VersionedMetaDataOnInit {
super(flags, site, allProjects.get(), RefNames.REFS_CONFIG);
this.baseConfig =
ProjectConfig.Factory.getBaseConfig(
- site, new AllProjectsName(allProjects.get()), new Project.NameKey(allProjects.get()));
+ site, new AllProjectsName(allProjects.get()), Project.nameKey(allProjects.get()));
}
public Config getConfig() {
@@ -71,7 +71,7 @@ public class AllProjectsConfig extends VersionedMetaDataOnInit {
private GroupList readGroupList() throws IOException {
return GroupList.parse(
- new Project.NameKey(project),
+ Project.nameKey(project),
readUTF8(GroupList.FILE_NAME),
error ->
logger.atSevere().log(
diff --git a/java/com/google/gerrit/pgm/init/api/BUILD b/java/com/google/gerrit/pgm/init/api/BUILD
index 19203fcc48..693d319f4e 100644
--- a/java/com/google/gerrit/pgm/init/api/BUILD
+++ b/java/com/google/gerrit/pgm/init/api/BUILD
@@ -7,13 +7,12 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/exceptions",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server",
"//lib:guava",
+ "//lib:jgit",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/pgm/init/api/GitRepositoryManagerOnInit.java b/java/com/google/gerrit/pgm/init/api/GitRepositoryManagerOnInit.java
index 2f94bdbd4f..a937c4bca7 100644
--- a/java/com/google/gerrit/pgm/init/api/GitRepositoryManagerOnInit.java
+++ b/java/com/google/gerrit/pgm/init/api/GitRepositoryManagerOnInit.java
@@ -14,7 +14,7 @@
package com.google.gerrit.pgm.init.api;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/pgm/init/api/Section.java b/java/com/google/gerrit/pgm/init/api/Section.java
index 87f7aeb442..b5d35f40e6 100644
--- a/java/com/google/gerrit/pgm/init/api/Section.java
+++ b/java/com/google/gerrit/pgm/init/api/Section.java
@@ -127,8 +127,10 @@ public class Section {
public <T extends Enum<?>, E extends EnumSet<? extends T>> T select(
String title, String name, T defValue, boolean nullIfDefault) {
+ @SuppressWarnings("rawtypes")
+ Class<? extends Enum> declaringClass = defValue.getDeclaringClass();
@SuppressWarnings("unchecked")
- E allowedValues = (E) EnumSet.allOf(defValue.getClass());
+ E allowedValues = (E) EnumSet.allOf(declaringClass);
return select(title, name, defValue, allowedValues, nullIfDefault);
}
diff --git a/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java b/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
index d3d22cba69..c11230cb4f 100644
--- a/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
+++ b/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
@@ -14,7 +14,7 @@
package com.google.gerrit.pgm.init.api;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.RepoSequence;
@@ -38,7 +38,7 @@ public class SequencesOnInit {
new RepoSequence(
repoManager,
GitReferenceUpdated.DISABLED,
- new Project.NameKey(allUsersName.get()),
+ Project.nameKey(allUsersName.get()),
Sequences.NAME_ACCOUNTS,
() -> Sequences.FIRST_ACCOUNT_ID,
1);
diff --git a/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java b/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java
index 738cafdd87..f77960131a 100644
--- a/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java
+++ b/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java
@@ -14,7 +14,7 @@
package com.google.gerrit.pgm.init.api;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.meta.VersionedMetaData;
@@ -58,7 +58,7 @@ public abstract class VersionedMetaDataOnInit extends VersionedMetaData {
File path = getPath();
if (path != null) {
try (Repository repo = new FileRepository(path)) {
- load(new Project.NameKey(project), repo);
+ load(Project.nameKey(project), repo);
}
}
return this;
diff --git a/java/com/google/gerrit/pgm/rules/PrologCompiler.java b/java/com/google/gerrit/pgm/rules/PrologCompiler.java
index 2663f42f51..0a41db5828 100644
--- a/java/com/google/gerrit/pgm/rules/PrologCompiler.java
+++ b/java/com/google/gerrit/pgm/rules/PrologCompiler.java
@@ -15,7 +15,7 @@
package com.google.gerrit.pgm.rules;
import com.google.gerrit.common.Version;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.util.time.TimeUtil;
diff --git a/java/com/google/gerrit/pgm/util/BUILD b/java/com/google/gerrit/pgm/util/BUILD
index 60fb5e4faa..1b979717db 100644
--- a/java/com/google/gerrit/pgm/util/BUILD
+++ b/java/com/google/gerrit/pgm/util/BUILD
@@ -6,11 +6,11 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/metrics/dropwizard",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/cache/h2",
"//java/com/google/gerrit/server/cache/mem",
@@ -19,9 +19,9 @@ java_library(
"//java/com/google/gerrit/util/cli",
"//lib:args4j",
"//lib:guava",
+ "//lib:jgit",
"//lib/flogger:api",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/log:jsonevent-layout",
"//lib/log:log4j",
],
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 8e2a24410a..ce2b05df69 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -19,13 +19,13 @@ import static com.google.inject.Scopes.SINGLETON;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
import com.google.gerrit.extensions.common.AccountVisibility;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCacheImpl;
diff --git a/java/com/google/gerrit/pgm/util/SiteProgram.java b/java/com/google/gerrit/pgm/util/SiteProgram.java
index eed307fd47..98558fbafa 100644
--- a/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -18,6 +18,7 @@ import static com.google.gerrit.server.config.GerritServerConfigModule.getSecure
import static com.google.inject.Stage.PRODUCTION;
import com.google.gerrit.common.Die;
+import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
@@ -28,6 +29,7 @@ import com.google.gerrit.server.config.GerritRuntime;
import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.git.GitRepositoryManagerModule;
+import com.google.gerrit.server.git.SystemReaderInstaller;
import com.google.gerrit.server.schema.SchemaModule;
import com.google.gerrit.server.securestore.SecureStoreClassName;
import com.google.inject.AbstractModule;
@@ -106,6 +108,13 @@ public abstract class SiteProgram extends AbstractProgram {
});
}
+ modules.add(
+ new LifecycleModule() {
+ @Override
+ protected void configure() {
+ listener().to(SystemReaderInstaller.class);
+ }
+ });
Module configModule = new GerritServerConfigModule();
modules.add(configModule);
modules.add(
diff --git a/java/com/google/gerrit/prettify/BUILD b/java/com/google/gerrit/prettify/BUILD
index 76afbe77b8..7c1241a9d7 100644
--- a/java/com/google/gerrit/prettify/BUILD
+++ b/java/com/google/gerrit/prettify/BUILD
@@ -5,8 +5,7 @@ java_library(
srcs = glob(["common/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/reviewdb:server",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
],
)
diff --git a/java/com/google/gerrit/proto/testing/BUILD b/java/com/google/gerrit/proto/testing/BUILD
index acfa8f06ee..0e5f887241 100644
--- a/java/com/google/gerrit/proto/testing/BUILD
+++ b/java/com/google/gerrit/proto/testing/BUILD
@@ -7,7 +7,6 @@ java_library(
srcs = glob(["*.java"]),
visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/server/cache/serialize",
"//lib:guava",
"//lib/commons:lang3",
"//lib/truth",
diff --git a/java/com/google/gerrit/proto/testing/SerializedClassSubject.java b/java/com/google/gerrit/proto/testing/SerializedClassSubject.java
index 546ff89bf2..79affc6a17 100644
--- a/java/com/google/gerrit/proto/testing/SerializedClassSubject.java
+++ b/java/com/google/gerrit/proto/testing/SerializedClassSubject.java
@@ -32,7 +32,7 @@ import org.apache.commons.lang3.reflect.FieldUtils;
* Subject about classes that are serialized into persistent caches or indices.
*
* <p>Hand-written {@link com.google.gerrit.server.cache.serialize.CacheSerializer CacheSerializer}
- * and {@link com.google.gerrit.reviewdb.converter.ProtoConverter ProtoConverter} implementations
+ * and {@link com.google.gerrit.entities.converter.ProtoConverter ProtoConverter} implementations
* depend on the exact representation of the data stored in a class, so it is important to verify
* any assumptions about the structure of the serialized classes. This class contains assertions
* about serialized classes, and should be used for every class that has a custom serializer
@@ -48,7 +48,7 @@ import org.apache.commons.lang3.reflect.FieldUtils;
* the hand-written serializer. Usually, serializer implementations should be written in such a way
* that new fields are considered optional, and won't require bumping the version.
*/
-public class SerializedClassSubject extends Subject<SerializedClassSubject, Class<?>> {
+public class SerializedClassSubject extends Subject {
public static SerializedClassSubject assertThatSerializedClass(Class<?> actual) {
// This formulation fails in Eclipse 4.7.3a with "The type
// SerializedClassSubject does not define SerializedClassSubject() that is
@@ -60,20 +60,23 @@ public class SerializedClassSubject extends Subject<SerializedClassSubject, Clas
return assertAbout(factory).that(actual);
}
- private SerializedClassSubject(FailureMetadata metadata, Class<?> actual) {
- super(metadata, actual);
+ private final Class<?> clazz;
+
+ private SerializedClassSubject(FailureMetadata metadata, Class<?> clazz) {
+ super(metadata, clazz);
+ this.clazz = clazz;
}
public void isAbstract() {
isNotNull();
- if (!Modifier.isAbstract(actual().getModifiers())) {
+ if (!Modifier.isAbstract(clazz.getModifiers())) {
failWithActual(simpleFact("expected class to be abstract"));
}
}
public void isConcrete() {
isNotNull();
- if (Modifier.isAbstract(actual().getModifiers())) {
+ if (Modifier.isAbstract(clazz.getModifiers())) {
failWithActual(simpleFact("expected class to be concrete"));
}
}
@@ -82,7 +85,7 @@ public class SerializedClassSubject extends Subject<SerializedClassSubject, Clas
isConcrete();
check("fields()")
.that(
- FieldUtils.getAllFieldsList(actual()).stream()
+ FieldUtils.getAllFieldsList(clazz).stream()
.filter(f -> !Modifier.isStatic(f.getModifiers()))
.collect(toImmutableMap(Field::getName, Field::getGenericType)))
.containsExactlyEntriesIn(expectedFields);
@@ -91,9 +94,9 @@ public class SerializedClassSubject extends Subject<SerializedClassSubject, Clas
public void hasAutoValueMethods(Map<String, Type> expectedMethods) {
// Would be nice if we could check clazz is an @AutoValue, but the retention is not RUNTIME.
isAbstract();
- check("noArgumentAbstractMethodsOn(%s)", actual().getName())
+ check("noArgumentAbstractMethods()")
.that(
- Arrays.stream(actual().getDeclaredMethods())
+ Arrays.stream(clazz.getDeclaredMethods())
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.filter(m -> Modifier.isAbstract(m.getModifiers()))
.filter(m -> m.getParameters().length == 0)
@@ -103,8 +106,6 @@ public class SerializedClassSubject extends Subject<SerializedClassSubject, Clas
public void extendsClass(Type superclassType) {
isNotNull();
- check("superclass(%s)", actual().getName())
- .that(actual().getGenericSuperclass())
- .isEqualTo(superclassType);
+ check("getGenericSuperclass()").that(clazz.getGenericSuperclass()).isEqualTo(superclassType);
}
}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupById.java b/java/com/google/gerrit/reviewdb/client/AccountGroupById.java
deleted file mode 100644
index 578865c1a7..0000000000
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupById.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gwtorm.client.CompoundKey;
-import java.util.Objects;
-
-/** Membership of an {@link AccountGroup} in an {@link AccountGroup}. */
-public final class AccountGroupById {
- public static Key key(AccountGroup.Id groupId, AccountGroup.UUID includeUuid) {
- return new Key(groupId, includeUuid);
- }
-
- public static class Key extends CompoundKey<AccountGroup.Id> {
- private static final long serialVersionUID = 1L;
-
- protected AccountGroup.Id groupId;
-
- protected AccountGroup.UUID includeUUID;
-
- protected Key() {
- groupId = new AccountGroup.Id();
- includeUUID = new AccountGroup.UUID();
- }
-
- public Key(AccountGroup.Id g, AccountGroup.UUID u) {
- groupId = g;
- includeUUID = u;
- }
-
- @Override
- public AccountGroup.Id getParentKey() {
- return groupId;
- }
-
- public AccountGroup.Id getGroupId() {
- return groupId;
- }
-
- public AccountGroup.Id groupId() {
- return getParentKey();
- }
-
- public AccountGroup.UUID getIncludeUUID() {
- return includeUUID;
- }
-
- public AccountGroup.UUID includeUuid() {
- return getIncludeUUID();
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {includeUUID};
- }
- }
-
- protected Key key;
-
- protected AccountGroupById() {}
-
- public AccountGroupById(AccountGroupById.Key k) {
- key = k;
- }
-
- public AccountGroupById.Key getKey() {
- return key;
- }
-
- public AccountGroup.Id getGroupId() {
- return key.groupId;
- }
-
- public AccountGroup.UUID getIncludeUUID() {
- return key.includeUUID;
- }
-
- @Override
- public boolean equals(Object o) {
- return (o instanceof AccountGroupById) && Objects.equals(key, ((AccountGroupById) o).key);
- }
-
- @Override
- public int hashCode() {
- return key.hashCode();
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "{key=" + key + "}";
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java b/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java
deleted file mode 100644
index 308e1e1dcc..0000000000
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gwtorm.client.CompoundKey;
-import java.sql.Timestamp;
-import java.util.Objects;
-
-/** Inclusion of an {@link AccountGroup} in another {@link AccountGroup}. */
-public final class AccountGroupByIdAud {
- public static Key key(AccountGroup.Id groupId, AccountGroup.UUID includeUuid, Timestamp addedOn) {
- return new Key(groupId, includeUuid, addedOn);
- }
-
- public static class Key extends CompoundKey<AccountGroup.Id> {
- private static final long serialVersionUID = 1L;
-
- protected AccountGroup.Id groupId;
-
- protected AccountGroup.UUID includeUUID;
-
- protected Timestamp addedOn;
-
- protected Key() {
- groupId = new AccountGroup.Id();
- includeUUID = new AccountGroup.UUID();
- }
-
- public Key(AccountGroup.Id g, AccountGroup.UUID u, Timestamp t) {
- groupId = g;
- includeUUID = u;
- addedOn = t;
- }
-
- @Override
- public AccountGroup.Id getParentKey() {
- return groupId;
- }
-
- public AccountGroup.Id groupId() {
- return getParentKey();
- }
-
- public AccountGroup.UUID getIncludeUUID() {
- return includeUUID;
- }
-
- public AccountGroup.UUID includeUuid() {
- return getIncludeUUID();
- }
-
- public Timestamp getAddedOn() {
- return addedOn;
- }
-
- public Timestamp addedOn() {
- return getAddedOn();
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {includeUUID};
- }
-
- @Override
- public String toString() {
- return "Key{"
- + "groupId="
- + groupId
- + ", includeUUID="
- + includeUUID
- + ", addedOn="
- + addedOn
- + '}';
- }
- }
-
- protected Key key;
-
- protected Account.Id addedBy;
-
- @Nullable protected Account.Id removedBy;
-
- @Nullable protected Timestamp removedOn;
-
- protected AccountGroupByIdAud() {}
-
- public AccountGroupByIdAud(final AccountGroupById m, Account.Id adder, Timestamp when) {
- final AccountGroup.Id group = m.getGroupId();
- final AccountGroup.UUID include = m.getIncludeUUID();
- key = new AccountGroupByIdAud.Key(group, include, when);
- addedBy = adder;
- }
-
- public AccountGroupByIdAud(AccountGroupByIdAud.Key key, Account.Id adder) {
- this.key = key;
- addedBy = adder;
- }
-
- public AccountGroupByIdAud.Key getKey() {
- return key;
- }
-
- public AccountGroup.Id getGroupId() {
- return key.getParentKey();
- }
-
- public AccountGroup.UUID getIncludeUUID() {
- return key.getIncludeUUID();
- }
-
- public boolean isActive() {
- return removedOn == null;
- }
-
- public void removed(Account.Id deleter, Timestamp when) {
- removedBy = deleter;
- removedOn = when;
- }
-
- public Account.Id getAddedBy() {
- return addedBy;
- }
-
- public Timestamp getAddedOn() {
- return key.getAddedOn();
- }
-
- public Account.Id getRemovedBy() {
- return removedBy;
- }
-
- public Timestamp getRemovedOn() {
- return removedOn;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof AccountGroupByIdAud)) {
- return false;
- }
- AccountGroupByIdAud a = (AccountGroupByIdAud) o;
- return Objects.equals(key, a.key)
- && Objects.equals(addedBy, a.addedBy)
- && Objects.equals(removedBy, a.removedBy)
- && Objects.equals(removedOn, a.removedOn);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(key, addedBy, removedBy, removedOn);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName()
- + "{"
- + "key="
- + key
- + ", addedBy="
- + addedBy
- + ", removedBy="
- + removedBy
- + ", removedOn="
- + removedOn
- + "}";
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupMember.java b/java/com/google/gerrit/reviewdb/client/AccountGroupMember.java
deleted file mode 100644
index dfa7d24426..0000000000
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupMember.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gwtorm.client.CompoundKey;
-import java.util.Objects;
-
-/** Membership of an {@link Account} in an {@link AccountGroup}. */
-public final class AccountGroupMember {
- public static Key key(Account.Id accountId, AccountGroup.Id groupId) {
- return new Key(accountId, groupId);
- }
-
- public static class Key extends CompoundKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- protected Account.Id accountId;
-
- protected AccountGroup.Id groupId;
-
- protected Key() {
- accountId = new Account.Id();
- groupId = new AccountGroup.Id();
- }
-
- public Key(Account.Id a, AccountGroup.Id g) {
- accountId = a;
- groupId = g;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- public Account.Id accountId() {
- return getParentKey();
- }
-
- public AccountGroup.Id getAccountGroupId() {
- return groupId;
- }
-
- public AccountGroup.Id groupId() {
- return getAccountGroupId();
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {groupId};
- }
- }
-
- protected Key key;
-
- protected AccountGroupMember() {}
-
- public AccountGroupMember(AccountGroupMember.Key k) {
- key = k;
- }
-
- public AccountGroupMember.Key getKey() {
- return key;
- }
-
- public Account.Id getAccountId() {
- return key.accountId;
- }
-
- public AccountGroup.Id getAccountGroupId() {
- return key.groupId;
- }
-
- @Override
- public boolean equals(Object o) {
- return (o instanceof AccountGroupMember) && Objects.equals(key, ((AccountGroupMember) o).key);
- }
-
- @Override
- public int hashCode() {
- return key.hashCode();
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "{key=" + key + "}";
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java b/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java
deleted file mode 100644
index 5d43b4a915..0000000000
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gwtorm.client.CompoundKey;
-import java.sql.Timestamp;
-import java.util.Objects;
-
-/** Membership of an {@link Account} in an {@link AccountGroup}. */
-public final class AccountGroupMemberAudit {
- public static Key key(Account.Id accountId, AccountGroup.Id groupId, Timestamp addedOn) {
- return new Key(accountId, groupId, addedOn);
- }
-
- public static class Key extends CompoundKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- protected Account.Id accountId;
-
- protected AccountGroup.Id groupId;
-
- protected Timestamp addedOn;
-
- protected Key() {
- accountId = new Account.Id();
- groupId = new AccountGroup.Id();
- }
-
- public Key(Account.Id a, AccountGroup.Id g, Timestamp t) {
- accountId = a;
- groupId = g;
- addedOn = t;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- public Account.Id accountId() {
- return getParentKey();
- }
-
- public AccountGroup.Id getGroupId() {
- return groupId;
- }
-
- public AccountGroup.Id groupId() {
- return getGroupId();
- }
-
- public Timestamp getAddedOn() {
- return addedOn;
- }
-
- public Timestamp addedOn() {
- return getAddedOn();
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {groupId};
- }
-
- @Override
- public String toString() {
- return "Key{"
- + "groupId="
- + groupId
- + ", accountId="
- + accountId
- + ", addedOn="
- + addedOn
- + '}';
- }
- }
-
- protected Key key;
-
- protected Account.Id addedBy;
-
- @Nullable protected Account.Id removedBy;
-
- @Nullable protected Timestamp removedOn;
-
- protected AccountGroupMemberAudit() {}
-
- public AccountGroupMemberAudit(final AccountGroupMember m, Account.Id adder, Timestamp addedOn) {
- final Account.Id who = m.getAccountId();
- final AccountGroup.Id group = m.getAccountGroupId();
- key = new AccountGroupMemberAudit.Key(who, group, addedOn);
- addedBy = adder;
- }
-
- public AccountGroupMemberAudit(AccountGroupMemberAudit.Key key, Account.Id adder) {
- this.key = key;
- addedBy = adder;
- }
-
- public AccountGroupMemberAudit.Key getKey() {
- return key;
- }
-
- public AccountGroup.Id getGroupId() {
- return key.getGroupId();
- }
-
- public Account.Id getMemberId() {
- return key.getParentKey();
- }
-
- public boolean isActive() {
- return removedOn == null;
- }
-
- public void removed(Account.Id deleter, Timestamp when) {
- removedBy = deleter;
- removedOn = when;
- }
-
- public void removedLegacy() {
- removedBy = addedBy;
- removedOn = key.addedOn;
- }
-
- public Account.Id getAddedBy() {
- return addedBy;
- }
-
- public Timestamp getAddedOn() {
- return key.getAddedOn();
- }
-
- public Account.Id getRemovedBy() {
- return removedBy;
- }
-
- public Timestamp getRemovedOn() {
- return removedOn;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof AccountGroupMemberAudit)) {
- return false;
- }
- AccountGroupMemberAudit a = (AccountGroupMemberAudit) o;
- return Objects.equals(key, a.key)
- && Objects.equals(addedBy, a.addedBy)
- && Objects.equals(removedBy, a.removedBy)
- && Objects.equals(removedOn, a.removedOn);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(key, addedBy, removedBy, removedOn);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName()
- + "{"
- + "key="
- + key
- + ", addedBy="
- + addedBy
- + ", removedBy="
- + removedBy
- + ", removedOn="
- + removedOn
- + "}";
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/Branch.java b/java/com/google/gerrit/reviewdb/client/Branch.java
deleted file mode 100644
index 4ea49b7637..0000000000
--- a/java/com/google/gerrit/reviewdb/client/Branch.java
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gwtorm.client.StringKey;
-
-/** Line of development within a {@link Project}. */
-public final class Branch {
- public static NameKey nameKey(Project.NameKey projectName, String branchName) {
- return new NameKey(projectName, RefNames.fullName(branchName));
- }
-
- public static NameKey nameKey(String projectName, String branchName) {
- return nameKey(Project.nameKey(projectName), branchName);
- }
-
- /** Branch name key */
- public static class NameKey extends StringKey<Project.NameKey> {
- private static final long serialVersionUID = 1L;
-
- protected Project.NameKey projectName;
-
- protected String branchName;
-
- protected NameKey() {
- projectName = new Project.NameKey();
- }
-
- public NameKey(Project.NameKey proj, String branchName) {
- projectName = proj;
- set(branchName);
- }
-
- public NameKey(String proj, String branchName) {
- this(new Project.NameKey(proj), branchName);
- }
-
- @Override
- public String get() {
- return branchName;
- }
-
- public String branch() {
- return get();
- }
-
- @Override
- protected void set(String newValue) {
- branchName = RefNames.fullName(newValue);
- }
-
- @Override
- public Project.NameKey getParentKey() {
- return projectName;
- }
-
- public Project.NameKey project() {
- return getParentKey();
- }
-
- public String getShortName() {
- return RefNames.shortName(get());
- }
- }
-
- protected NameKey name;
- protected RevId revision;
- protected boolean canDelete;
-
- protected Branch() {}
-
- public Branch(Branch.NameKey newName) {
- name = newName;
- }
-
- public Branch.NameKey getNameKey() {
- return name;
- }
-
- public String getName() {
- return name.get();
- }
-
- public String getShortName() {
- return name.getShortName();
- }
-
- public RevId getRevision() {
- return revision;
- }
-
- public void setRevision(RevId id) {
- revision = id;
- }
-
- public boolean getCanDelete() {
- return canDelete;
- }
-
- public void setCanDelete(boolean canDelete) {
- this.canDelete = canDelete;
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/PatchLineComment.java b/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
deleted file mode 100644
index ce218c0128..0000000000
--- a/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
+++ /dev/null
@@ -1,361 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.client.Comment.Range;
-import com.google.gwtorm.client.StringKey;
-import java.sql.Timestamp;
-import java.util.Objects;
-
-/**
- * A comment left by a user on a specific line of a {@link Patch}.
- *
- * <p>New APIs should not expose this class.
- *
- * @see Comment
- */
-public final class PatchLineComment {
- public static class Key extends StringKey<Patch.Key> {
- private static final long serialVersionUID = 1L;
-
- public static Key from(Change.Id changeId, Comment.Key key) {
- return new Key(
- new Patch.Key(new PatchSet.Id(changeId, key.patchSetId), key.filename), key.uuid);
- }
-
- protected Patch.Key patchKey;
-
- protected String uuid;
-
- protected Key() {
- patchKey = new Patch.Key();
- }
-
- public Key(Patch.Key p, String uuid) {
- this.patchKey = p;
- this.uuid = uuid;
- }
-
- @Override
- public Patch.Key getParentKey() {
- return patchKey;
- }
-
- @Override
- public String get() {
- return uuid;
- }
-
- @Override
- public void set(String newValue) {
- uuid = newValue;
- }
-
- public Comment.Key asCommentKey() {
- return new Comment.Key(
- get(), getParentKey().getFileName(), getParentKey().getParentKey().get());
- }
- }
-
- public static final char STATUS_DRAFT = 'd';
- public static final char STATUS_PUBLISHED = 'P';
-
- public enum Status {
- DRAFT(STATUS_DRAFT),
-
- PUBLISHED(STATUS_PUBLISHED);
-
- private final char code;
-
- Status(char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
-
- public static Status forCode(char c) {
- for (Status s : Status.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
- }
-
- public static PatchLineComment from(
- Change.Id changeId, PatchLineComment.Status status, Comment c) {
- PatchLineComment.Key key =
- new PatchLineComment.Key(
- new Patch.Key(new PatchSet.Id(changeId, c.key.patchSetId), c.key.filename), c.key.uuid);
-
- PatchLineComment plc =
- new PatchLineComment(key, c.lineNbr, c.author.getId(), c.parentUuid, c.writtenOn);
- plc.setSide(c.side);
- plc.setMessage(c.message);
- if (c.range != null) {
- Comment.Range r = c.range;
- plc.setRange(new CommentRange(r.startLine, r.startChar, r.endLine, r.endChar));
- }
- plc.setTag(c.tag);
- plc.setRevId(new RevId(c.revId));
- plc.setStatus(status);
- plc.setRealAuthor(c.getRealAuthor().getId());
- plc.setUnresolved(c.unresolved);
- return plc;
- }
-
- protected Key key;
-
- /** Line number this comment applies to; it should display after the line. */
- protected int lineNbr;
-
- /** Who wrote this comment. */
- protected Account.Id author;
-
- /** When this comment was drafted. */
- protected Timestamp writtenOn;
-
- /** Current publication state of the comment; see {@link Status}. */
- protected char status;
-
- /** Which file is this comment; 0 is ancestor, 1 is new version. */
- protected short side;
-
- /** The text left by the user. */
- @Nullable protected String message;
-
- /** The parent of this comment, or null if this is the first comment on this line */
- @Nullable protected String parentUuid;
-
- @Nullable protected CommentRange range;
-
- @Nullable protected String tag;
-
- /** Real user that added this comment on behalf of the user recorded in {@link #author}. */
- @Nullable protected Account.Id realAuthor;
-
- /** True if this comment requires further action. */
- protected boolean unresolved;
-
- /** The RevId for the commit to which this comment is referring. */
- protected RevId revId;
-
- protected PatchLineComment() {}
-
- public PatchLineComment(
- PatchLineComment.Key id, int line, Account.Id a, String parentUuid, Timestamp when) {
- key = id;
- lineNbr = line;
- author = a;
- setParentUuid(parentUuid);
- setStatus(Status.DRAFT);
- setWrittenOn(when);
- }
-
- public PatchLineComment(PatchLineComment o) {
- key = o.key;
- lineNbr = o.lineNbr;
- author = o.author;
- realAuthor = o.realAuthor;
- writtenOn = o.writtenOn;
- status = o.status;
- side = o.side;
- message = o.message;
- parentUuid = o.parentUuid;
- revId = o.revId;
- if (o.range != null) {
- range =
- new CommentRange(
- o.range.getStartLine(),
- o.range.getStartCharacter(),
- o.range.getEndLine(),
- o.range.getEndCharacter());
- }
- }
-
- public PatchLineComment.Key getKey() {
- return key;
- }
-
- public PatchSet.Id getPatchSetId() {
- return key.getParentKey().getParentKey();
- }
-
- public int getLine() {
- return lineNbr;
- }
-
- public void setLine(int line) {
- lineNbr = line;
- }
-
- public Account.Id getAuthor() {
- return author;
- }
-
- public Account.Id getRealAuthor() {
- return realAuthor != null ? realAuthor : getAuthor();
- }
-
- public void setRealAuthor(Account.Id id) {
- // Use null for same real author, as before the column was added.
- realAuthor = Objects.equals(getAuthor(), id) ? null : id;
- }
-
- public Timestamp getWrittenOn() {
- return writtenOn;
- }
-
- public Status getStatus() {
- return Status.forCode(status);
- }
-
- public void setStatus(Status s) {
- status = s.getCode();
- }
-
- public short getSide() {
- return side;
- }
-
- public void setSide(short s) {
- side = s;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String s) {
- message = s;
- }
-
- public void setWrittenOn(Timestamp ts) {
- writtenOn = ts;
- }
-
- public String getParentUuid() {
- return parentUuid;
- }
-
- public void setParentUuid(String inReplyTo) {
- parentUuid = inReplyTo;
- }
-
- public void setRange(Range r) {
- if (r != null) {
- range =
- new CommentRange(
- r.startLine, r.startCharacter,
- r.endLine, r.endCharacter);
- } else {
- range = null;
- }
- }
-
- public void setRange(CommentRange r) {
- range = r;
- }
-
- public CommentRange getRange() {
- return range;
- }
-
- public void setRevId(RevId rev) {
- revId = rev;
- }
-
- public RevId getRevId() {
- return revId;
- }
-
- public void setTag(String tag) {
- this.tag = tag;
- }
-
- public String getTag() {
- return tag;
- }
-
- public void setUnresolved(Boolean unresolved) {
- this.unresolved = unresolved;
- }
-
- public Boolean getUnresolved() {
- return unresolved;
- }
-
- public Comment asComment(String serverId) {
- Comment c =
- new Comment(key.asCommentKey(), author, writtenOn, side, message, serverId, unresolved);
- c.setRevId(revId);
- c.setRange(range);
- c.lineNbr = lineNbr;
- c.parentUuid = parentUuid;
- c.tag = tag;
- c.setRealAuthor(getRealAuthor());
- return c;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof PatchLineComment) {
- PatchLineComment c = (PatchLineComment) o;
- return Objects.equals(key, c.getKey())
- && Objects.equals(lineNbr, c.getLine())
- && Objects.equals(author, c.getAuthor())
- && Objects.equals(writtenOn, c.getWrittenOn())
- && Objects.equals(status, c.getStatus().getCode())
- && Objects.equals(side, c.getSide())
- && Objects.equals(message, c.getMessage())
- && Objects.equals(parentUuid, c.getParentUuid())
- && Objects.equals(range, c.getRange())
- && Objects.equals(revId, c.getRevId())
- && Objects.equals(tag, c.getTag())
- && Objects.equals(unresolved, c.getUnresolved());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return key.hashCode();
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("PatchLineComment{");
- builder.append("key=").append(key).append(',');
- builder.append("lineNbr=").append(lineNbr).append(',');
- builder.append("author=").append(author.get()).append(',');
- builder.append("realAuthor=").append(realAuthor != null ? realAuthor.get() : "").append(',');
- builder.append("writtenOn=").append(writtenOn.toString()).append(',');
- builder.append("status=").append(status).append(',');
- builder.append("side=").append(side).append(',');
- builder.append("message=").append(Objects.toString(message, "")).append(',');
- builder.append("parentUuid=").append(Objects.toString(parentUuid, "")).append(',');
- builder.append("range=").append(Objects.toString(range, "")).append(',');
- builder.append("revId=").append(revId != null ? revId.get() : "").append(',');
- builder.append("tag=").append(Objects.toString(tag, "")).append(',');
- builder.append("unresolved=").append(unresolved);
- builder.append('}');
- return builder.toString();
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/PatchSet.java b/java/com/google/gerrit/reviewdb/client/PatchSet.java
deleted file mode 100644
index 684f09236b..0000000000
--- a/java/com/google/gerrit/reviewdb/client/PatchSet.java
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gwtorm.client.IntKey;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/** A single revision of a {@link Change}. */
-public final class PatchSet {
- /** Is the reference name a change reference? */
- public static boolean isChangeRef(String name) {
- return Id.fromRef(name) != null;
- }
-
- /**
- * Is the reference name a change reference?
- *
- * @deprecated use isChangeRef instead.
- */
- @Deprecated
- public static boolean isRef(String name) {
- return isChangeRef(name);
- }
-
- public static String joinGroups(List<String> groups) {
- if (groups == null) {
- throw new IllegalArgumentException("groups may not be null");
- }
- StringBuilder sb = new StringBuilder();
- boolean first = true;
- for (String g : groups) {
- if (!first) {
- sb.append(',');
- } else {
- first = false;
- }
- sb.append(g);
- }
- return sb.toString();
- }
-
- public static List<String> splitGroups(String joinedGroups) {
- if (joinedGroups == null) {
- throw new IllegalArgumentException("groups may not be null");
- }
- List<String> groups = new ArrayList<>();
- int i = 0;
- while (true) {
- int idx = joinedGroups.indexOf(',', i);
- if (idx < 0) {
- groups.add(joinedGroups.substring(i));
- break;
- }
- groups.add(joinedGroups.substring(i, idx));
- i = idx + 1;
- }
- return groups;
- }
-
- public static Id id(Change.Id changeId, int id) {
- return new Id(changeId, id);
- }
-
- public static class Id extends IntKey<Change.Id> {
- private static final long serialVersionUID = 1L;
-
- public Change.Id changeId;
-
- public int patchSetId;
-
- public Id() {
- changeId = new Change.Id();
- }
-
- public Id(Change.Id change, int id) {
- this.changeId = change;
- this.patchSetId = id;
- }
-
- @Override
- public Change.Id getParentKey() {
- return changeId;
- }
-
- public Change.Id changeId() {
- return getParentKey();
- }
-
- @Override
- public int get() {
- return patchSetId;
- }
-
- @Override
- protected void set(int newValue) {
- patchSetId = newValue;
- }
-
- public String toRefName() {
- return changeId.refPrefixBuilder().append(patchSetId).toString();
- }
-
- /** Parse a PatchSet.Id out of a string representation. */
- public static Id parse(String str) {
- final Id r = new Id();
- r.fromString(str);
- return r;
- }
-
- /** Parse a PatchSet.Id from a {@link PatchSet#getRefName()} result. */
- public static Id fromRef(String ref) {
- int cs = Change.Id.startIndex(ref);
- if (cs < 0) {
- return null;
- }
- int ce = Change.Id.nextNonDigit(ref, cs);
- int patchSetId = fromRef(ref, ce);
- if (patchSetId < 0) {
- return null;
- }
- int changeId = Integer.parseInt(ref.substring(cs, ce));
- return new PatchSet.Id(new Change.Id(changeId), patchSetId);
- }
-
- static int fromRef(String ref, int changeIdEnd) {
- // Patch set ID.
- int ps = changeIdEnd + 1;
- if (ps >= ref.length() || ref.charAt(ps) == '0') {
- return -1;
- }
- for (int i = ps; i < ref.length(); i++) {
- if (ref.charAt(i) < '0' || ref.charAt(i) > '9') {
- return -1;
- }
- }
- return Integer.parseInt(ref.substring(ps));
- }
-
- public String getId() {
- return toId(patchSetId);
- }
-
- public static String toId(int number) {
- return number == 0 ? "edit" : String.valueOf(number);
- }
- }
-
- protected Id id;
-
- @Nullable protected RevId revision;
-
- protected Account.Id uploader;
-
- /** When this patch set was first introduced onto the change. */
- protected Timestamp createdOn;
-
- /**
- * Opaque group identifier, usually assigned during creation.
- *
- * <p>This field is actually a comma-separated list of values, as in rare cases involving merge
- * commits a patch set may belong to multiple groups.
- *
- * <p>Changes on the same branch having patch sets with intersecting groups are considered
- * related, as in the "Related Changes" tab.
- */
- @Nullable protected String groups;
-
- // DELETED id = 7 (pushCertficate)
-
- /** Certificate sent with a push that created this patch set. */
- @Nullable protected String pushCertificate;
-
- /**
- * Optional user-supplied description for this patch set.
- *
- * <p>When this field is null, the description was never set on the patch set. When this field is
- * an empty string, the description was set and later cleared.
- */
- @Nullable protected String description;
-
- protected PatchSet() {}
-
- public PatchSet(PatchSet.Id k) {
- id = k;
- }
-
- public PatchSet(PatchSet src) {
- this.id = src.id;
- this.revision = src.revision;
- this.uploader = src.uploader;
- this.createdOn = src.createdOn;
- this.groups = src.groups;
- this.pushCertificate = src.pushCertificate;
- this.description = src.description;
- }
-
- public PatchSet.Id getId() {
- return id;
- }
-
- public int getPatchSetId() {
- return id.get();
- }
-
- public RevId getRevision() {
- return revision;
- }
-
- public void setRevision(RevId i) {
- revision = i;
- }
-
- public Account.Id getUploader() {
- return uploader;
- }
-
- public void setUploader(Account.Id who) {
- uploader = who;
- }
-
- public Timestamp getCreatedOn() {
- return createdOn;
- }
-
- public void setCreatedOn(Timestamp ts) {
- createdOn = ts;
- }
-
- public List<String> getGroups() {
- if (groups == null) {
- return Collections.emptyList();
- }
- return splitGroups(groups);
- }
-
- public void setGroups(List<String> groups) {
- if (groups == null) {
- groups = Collections.emptyList();
- }
- this.groups = joinGroups(groups);
- }
-
- public String getRefName() {
- return id.toRefName();
- }
-
- public String getPushCertificate() {
- return pushCertificate;
- }
-
- public void setPushCertificate(String cert) {
- pushCertificate = cert;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof PatchSet)) {
- return false;
- }
- PatchSet p = (PatchSet) o;
- return Objects.equals(id, p.id)
- && Objects.equals(revision, p.revision)
- && Objects.equals(uploader, p.uploader)
- && Objects.equals(createdOn, p.createdOn)
- && Objects.equals(groups, p.groups)
- && Objects.equals(pushCertificate, p.pushCertificate)
- && Objects.equals(description, p.description);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, revision, uploader, createdOn, groups, pushCertificate, description);
- }
-
- @Override
- public String toString() {
- return "[PatchSet " + getId().toString() + "]";
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java b/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
deleted file mode 100644
index e1c4ea9989..0000000000
--- a/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gwtorm.client.CompoundKey;
-import java.sql.Timestamp;
-import java.util.Date;
-import java.util.Objects;
-
-/** An approval (or negative approval) on a patch set. */
-public final class PatchSetApproval {
- public static Key key(PatchSet.Id patchSetId, Account.Id accountId, LabelId labelId) {
- return new Key(patchSetId, accountId, labelId);
- }
-
- public static class Key extends CompoundKey<PatchSet.Id> {
- private static final long serialVersionUID = 1L;
-
- protected PatchSet.Id patchSetId;
-
- protected Account.Id accountId;
-
- protected LabelId categoryId;
-
- protected Key() {
- patchSetId = new PatchSet.Id();
- accountId = new Account.Id();
- categoryId = new LabelId();
- }
-
- public Key(PatchSet.Id ps, Account.Id a, LabelId c) {
- this.patchSetId = ps;
- this.accountId = a;
- this.categoryId = c;
- }
-
- @Override
- public PatchSet.Id getParentKey() {
- return patchSetId;
- }
-
- public PatchSet.Id patchSetId() {
- return getParentKey();
- }
-
- public Account.Id getAccountId() {
- return accountId;
- }
-
- public Account.Id accountId() {
- return getAccountId();
- }
-
- public LabelId getLabelId() {
- return categoryId;
- }
-
- public LabelId labelId() {
- return getLabelId();
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {accountId, categoryId};
- }
- }
-
- protected Key key;
-
- /**
- * Value assigned by the user.
- *
- * <p>The precise meaning of "value" is up to each category.
- *
- * <p>In general:
- *
- * <ul>
- * <li><b>&lt; 0:</b> The approval is rejected/revoked.
- * <li><b>= 0:</b> No indication either way is provided.
- * <li><b>&gt; 0:</b> The approval is approved/positive.
- * </ul>
- *
- * and in the negative and positive direction a magnitude can be assumed.The further from 0 the
- * more assertive the approval.
- */
- protected short value;
-
- protected Timestamp granted;
-
- @Nullable protected String tag;
-
- /** Real user that made this approval on behalf of the user recorded in {@link Key#accountId}. */
- @Nullable protected Account.Id realAccountId;
-
- protected boolean postSubmit;
-
- // DELETED: id = 4 (changeOpen)
- // DELETED: id = 5 (changeSortKey)
-
- protected PatchSetApproval() {}
-
- public PatchSetApproval(PatchSetApproval.Key k, short v, Date ts) {
- key = k;
- setValue(v);
- setGranted(ts);
- }
-
- public PatchSetApproval(PatchSet.Id psId, PatchSetApproval src) {
- key = new PatchSetApproval.Key(psId, src.getAccountId(), src.getLabelId());
- value = src.getValue();
- granted = src.granted;
- realAccountId = src.realAccountId;
- tag = src.tag;
- postSubmit = src.postSubmit;
- }
-
- public PatchSetApproval(PatchSetApproval src) {
- this(src.getPatchSetId(), src);
- }
-
- public PatchSetApproval.Key getKey() {
- return key;
- }
-
- public PatchSet.Id getPatchSetId() {
- return key.patchSetId;
- }
-
- public Account.Id getAccountId() {
- return key.accountId;
- }
-
- public Account.Id getRealAccountId() {
- return realAccountId != null ? realAccountId : getAccountId();
- }
-
- public void setRealAccountId(Account.Id id) {
- // Use null for same real author, as before the column was added.
- realAccountId = Objects.equals(getAccountId(), id) ? null : id;
- }
-
- public LabelId getLabelId() {
- return key.categoryId;
- }
-
- public short getValue() {
- return value;
- }
-
- public void setValue(short v) {
- value = v;
- }
-
- public Timestamp getGranted() {
- return granted;
- }
-
- public void setGranted(Date when) {
- if (when instanceof Timestamp) {
- granted = (Timestamp) when;
- } else {
- granted = new Timestamp(when.getTime());
- }
- }
-
- public void setTag(String t) {
- tag = t;
- }
-
- public String getLabel() {
- return getLabelId().get();
- }
-
- public boolean isLegacySubmit() {
- return LabelId.LEGACY_SUBMIT_NAME.equals(getLabel());
- }
-
- public String getTag() {
- return tag;
- }
-
- public void setPostSubmit(boolean postSubmit) {
- this.postSubmit = postSubmit;
- }
-
- public boolean isPostSubmit() {
- return postSubmit;
- }
-
- @Override
- public String toString() {
- StringBuilder sb =
- new StringBuilder("[")
- .append(key)
- .append(": ")
- .append(value)
- .append(",tag:")
- .append(tag)
- .append(",realAccountId:")
- .append(realAccountId);
- if (postSubmit) {
- sb.append(",postSubmit");
- }
- return sb.append(']').toString();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof PatchSetApproval) {
- PatchSetApproval p = (PatchSetApproval) o;
- return Objects.equals(key, p.key)
- && Objects.equals(value, p.value)
- && Objects.equals(granted, p.granted)
- && Objects.equals(tag, p.tag)
- && Objects.equals(realAccountId, p.realAccountId)
- && postSubmit == p.postSubmit;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(key, value, granted, tag, realAccountId, postSubmit);
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/RevId.java b/java/com/google/gerrit/reviewdb/client/RevId.java
deleted file mode 100644
index 99b6c2ce56..0000000000
--- a/java/com/google/gerrit/reviewdb/client/RevId.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-/** A revision identifier for a file or a change. */
-public final class RevId {
- public static final int ABBREV_LEN = 7;
- public static final int LEN = 40;
-
- protected String id;
-
- protected RevId() {}
-
- public RevId(String str) {
- id = str;
- }
-
- /** @return the value of this revision id. */
- public String get() {
- return id;
- }
-
- /** @return true if this revision id has all required digits. */
- public boolean isComplete() {
- return get().length() == LEN;
- }
-
- /**
- * @return if {@link #isComplete()}, {@code this}; otherwise a new RevId with 'z' appended on the
- * end.
- */
- public RevId max() {
- if (isComplete()) {
- return this;
- }
-
- final StringBuilder revEnd = new StringBuilder(get().length() + 1);
- revEnd.append(get());
- revEnd.append('z');
- return new RevId(revEnd.toString());
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- return (o instanceof RevId) && id.equals(((RevId) o).id);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "{" + id + "}";
- }
-
- public boolean matches(String str) {
- return id.startsWith(str.toLowerCase());
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/RobotComment.java b/java/com/google/gerrit/reviewdb/client/RobotComment.java
deleted file mode 100644
index eceb0bfc8e..0000000000
--- a/java/com/google/gerrit/reviewdb/client/RobotComment.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import java.sql.Timestamp;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-public class RobotComment extends Comment {
- public String robotId;
- public String robotRunId;
- public String url;
- public Map<String, String> properties;
- public List<FixSuggestion> fixSuggestions;
-
- public RobotComment(
- Key key,
- Account.Id author,
- Timestamp writtenOn,
- short side,
- String message,
- String serverId,
- String robotId,
- String robotRunId) {
- super(key, author, writtenOn, side, message, serverId, false);
- this.robotId = robotId;
- this.robotRunId = robotRunId;
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("RobotComment{")
- .append("key=")
- .append(key)
- .append(',')
- .append("robotId=")
- .append(robotId)
- .append(',')
- .append("robotRunId=")
- .append(robotRunId)
- .append(',')
- .append("lineNbr=")
- .append(lineNbr)
- .append(',')
- .append("author=")
- .append(author.getId().get())
- .append(',')
- .append("realAuthor=")
- .append(realAuthor != null ? realAuthor.getId().get() : "")
- .append(',')
- .append("writtenOn=")
- .append(writtenOn.toString())
- .append(',')
- .append("side=")
- .append(side)
- .append(',')
- .append("message=")
- .append(Objects.toString(message, ""))
- .append(',')
- .append("parentUuid=")
- .append(Objects.toString(parentUuid, ""))
- .append(',')
- .append("range=")
- .append(Objects.toString(range, ""))
- .append(',')
- .append("revId=")
- .append(revId != null ? revId : "")
- .append(',')
- .append("tag=")
- .append(Objects.toString(tag, ""))
- .append(',')
- .append("unresolved=")
- .append(unresolved)
- .append(',')
- .append("url=")
- .append(url)
- .append(',')
- .append("properties=")
- .append(properties != null ? properties : "")
- .append("fixSuggestions=")
- .append(fixSuggestions != null ? fixSuggestions : "")
- .append('}')
- .toString();
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java b/java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java
deleted file mode 100644
index b297dfb2f5..0000000000
--- a/java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gwtorm.client.StringKey;
-
-/**
- * Defining a project/branch subscription to a project/branch project.
- *
- * <p>This means a class instance represents a repo/branch subscription to a project/branch (the
- * subscriber).
- *
- * <p>A subscriber operates a submodule in defined path.
- */
-public final class SubmoduleSubscription {
- /** Subscription key */
- public static class Key extends StringKey<Branch.NameKey> {
- private static final long serialVersionUID = 1L;
-
- /**
- * Indicates the super project, aka subscriber: the project owner of the gitlinks to the
- * submodules.
- */
- protected Branch.NameKey superProject;
-
- protected String submodulePath;
-
- protected Key() {
- superProject = new Branch.NameKey();
- }
-
- protected Key(Branch.NameKey superProject, String path) {
- this.superProject = superProject;
- this.submodulePath = path;
- }
-
- @Override
- public Branch.NameKey getParentKey() {
- return superProject;
- }
-
- @Override
- public String get() {
- return submodulePath;
- }
-
- @Override
- protected void set(String newValue) {
- this.submodulePath = newValue;
- }
- }
-
- protected Key key;
-
- protected Branch.NameKey submodule;
-
- protected SubmoduleSubscription() {}
-
- public SubmoduleSubscription(Branch.NameKey superProject, Branch.NameKey submodule, String path) {
- this.key = new Key(superProject, path);
- this.submodule = submodule;
- }
-
- public Key getKey() {
- return key;
- }
-
- public Branch.NameKey getSuperProject() {
- return key.superProject;
- }
-
- public String getPath() {
- return key.get();
- }
-
- public Branch.NameKey getSubmodule() {
- return submodule;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof SubmoduleSubscription) {
- return key.equals(((SubmoduleSubscription) o).key)
- && submodule.equals(((SubmoduleSubscription) o).submodule);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return key.hashCode();
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(getSuperProject()).append(':').append(getPath());
- sb.append(" follows ");
- sb.append(getSubmodule());
- return sb.toString();
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/converter/PatchSetProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/PatchSetProtoConverter.java
deleted file mode 100644
index 75ee80099f..0000000000
--- a/java/com/google/gerrit/reviewdb/converter/PatchSetProtoConverter.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.converter;
-
-import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.protobuf.Parser;
-import java.sql.Timestamp;
-import java.util.List;
-
-public enum PatchSetProtoConverter implements ProtoConverter<Entities.PatchSet, PatchSet> {
- INSTANCE;
-
- private final ProtoConverter<Entities.PatchSet_Id, PatchSet.Id> patchSetIdConverter =
- PatchSetIdProtoConverter.INSTANCE;
- private final ProtoConverter<Entities.RevId, RevId> revIdConverter = RevIdProtoConverter.INSTANCE;
- private final ProtoConverter<Entities.Account_Id, Account.Id> accountIdConverter =
- AccountIdProtoConverter.INSTANCE;
-
- @Override
- public Entities.PatchSet toProto(PatchSet patchSet) {
- Entities.PatchSet.Builder builder =
- Entities.PatchSet.newBuilder().setId(patchSetIdConverter.toProto(patchSet.getId()));
- RevId revision = patchSet.getRevision();
- if (revision != null) {
- builder.setRevision(revIdConverter.toProto(revision));
- }
- Account.Id uploader = patchSet.getUploader();
- if (uploader != null) {
- builder.setUploaderAccountId(accountIdConverter.toProto(uploader));
- }
- Timestamp createdOn = patchSet.getCreatedOn();
- if (createdOn != null) {
- builder.setCreatedOn(createdOn.getTime());
- }
- List<String> groups = patchSet.getGroups();
- if (!groups.isEmpty()) {
- builder.setGroups(PatchSet.joinGroups(groups));
- }
- String pushCertificate = patchSet.getPushCertificate();
- if (pushCertificate != null) {
- builder.setPushCertificate(pushCertificate);
- }
- String description = patchSet.getDescription();
- if (description != null) {
- builder.setDescription(description);
- }
- return builder.build();
- }
-
- @Override
- public PatchSet fromProto(Entities.PatchSet proto) {
- PatchSet patchSet = new PatchSet(patchSetIdConverter.fromProto(proto.getId()));
- if (proto.hasRevision()) {
- patchSet.setRevision(revIdConverter.fromProto(proto.getRevision()));
- }
- if (proto.hasUploaderAccountId()) {
- patchSet.setUploader(accountIdConverter.fromProto(proto.getUploaderAccountId()));
- }
- if (proto.hasCreatedOn()) {
- patchSet.setCreatedOn(new Timestamp(proto.getCreatedOn()));
- }
- if (proto.hasGroups()) {
- patchSet.setGroups(PatchSet.splitGroups(proto.getGroups()));
- }
- if (proto.hasPushCertificate()) {
- patchSet.setPushCertificate(proto.getPushCertificate());
- }
- if (proto.hasDescription()) {
- patchSet.setDescription(proto.getDescription());
- }
- return patchSet;
- }
-
- @Override
- public Parser<Entities.PatchSet> getParser() {
- return Entities.PatchSet.parser();
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/converter/RevIdProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/RevIdProtoConverter.java
deleted file mode 100644
index b3c998b2aa..0000000000
--- a/java/com/google/gerrit/reviewdb/converter/RevIdProtoConverter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.converter;
-
-import com.google.gerrit.proto.Entities;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.protobuf.Parser;
-
-public enum RevIdProtoConverter implements ProtoConverter<Entities.RevId, RevId> {
- INSTANCE;
-
- @Override
- public Entities.RevId toProto(RevId revId) {
- return Entities.RevId.newBuilder().setId(revId.get()).build();
- }
-
- @Override
- public RevId fromProto(Entities.RevId proto) {
- return new RevId(proto.getId());
- }
-
- @Override
- public Parser<Entities.RevId> getParser() {
- return Entities.RevId.parser();
- }
-}
diff --git a/java/com/google/gerrit/server/ApprovalCopier.java b/java/com/google/gerrit/server/ApprovalInference.java
index 09508bb9f2..566a32bf56 100644
--- a/java/com/google/gerrit/server/ApprovalCopier.java
+++ b/java/com/google/gerrit/server/ApprovalInference.java
@@ -15,170 +15,96 @@
package com.google.gerrit.server;
import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
+import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ListMultimap;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Table;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.ChangeKind;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.change.LabelNormalizer;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
-import java.util.TreeMap;
+import java.util.Map;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
/**
- * Copies approvals between patch sets.
+ * Computes approvals for a given patch set by looking at approvals applied to the given patch set
+ * and by additionally inferring approvals from the patch set's parents. The latter is done by
+ * asserting a change's kind and checking the project config for allowed forward-inference.
*
* <p>The result of a copy may either be stored, as when stamping approvals in the database at
* submit time, or refreshed on demand, as when reading approvals from the NoteDb.
*/
@Singleton
-public class ApprovalCopier {
+public class ApprovalInference {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final ProjectCache projectCache;
private final ChangeKindCache changeKindCache;
private final LabelNormalizer labelNormalizer;
- private final ChangeData.Factory changeDataFactory;
- private final PatchSetUtil psUtil;
@Inject
- ApprovalCopier(
- ProjectCache projectCache,
- ChangeKindCache changeKindCache,
- LabelNormalizer labelNormalizer,
- ChangeData.Factory changeDataFactory,
- PatchSetUtil psUtil) {
+ ApprovalInference(
+ ProjectCache projectCache, ChangeKindCache changeKindCache, LabelNormalizer labelNormalizer) {
this.projectCache = projectCache;
this.changeKindCache = changeKindCache;
this.labelNormalizer = labelNormalizer;
- this.changeDataFactory = changeDataFactory;
- this.psUtil = psUtil;
}
- Iterable<PatchSetApproval> getForPatchSet(
+ /**
+ * Returns all approvals that apply to the given patch set. Honors direct and indirect (approval
+ * on parents) approvals.
+ */
+ Iterable<PatchSetApproval> forPatchSet(
ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
- return getForPatchSet(notes, psId, rw, repoConfig, Collections.emptyList());
- }
-
- Iterable<PatchSetApproval> getForPatchSet(
- ChangeNotes notes,
- PatchSet.Id psId,
- @Nullable RevWalk rw,
- @Nullable Config repoConfig,
- Iterable<PatchSetApproval> dontCopy) {
- PatchSet ps = psUtil.get(notes, psId);
- if (ps == null) {
- return Collections.emptyList();
- }
- return getForPatchSet(notes, ps, rw, repoConfig, dontCopy);
- }
-
- private Iterable<PatchSetApproval> getForPatchSet(
- ChangeNotes notes,
- PatchSet ps,
- @Nullable RevWalk rw,
- @Nullable Config repoConfig,
- Iterable<PatchSetApproval> dontCopy) {
- requireNonNull(ps, "ps should not be null");
- ChangeData cd = changeDataFactory.create(notes);
- try {
- ProjectState project = projectCache.checkedGet(cd.change().getDest().getParentKey());
- ListMultimap<PatchSet.Id, PatchSetApproval> all = cd.approvals();
- requireNonNull(all, "all should not be null");
-
- Table<String, Account.Id, PatchSetApproval> wontCopy = HashBasedTable.create();
- for (PatchSetApproval psa : dontCopy) {
- wontCopy.put(psa.getLabel(), psa.getAccountId(), psa);
- }
-
- Table<String, Account.Id, PatchSetApproval> byUser = HashBasedTable.create();
- for (PatchSetApproval psa : all.get(ps.getId())) {
- if (!wontCopy.contains(psa.getLabel(), psa.getAccountId())) {
- byUser.put(psa.getLabel(), psa.getAccountId(), psa);
- }
- }
-
- TreeMap<Integer, PatchSet> patchSets = getPatchSets(cd);
-
- // Walk patch sets strictly less than current in descending order.
- Collection<PatchSet> allPrior =
- patchSets.descendingMap().tailMap(ps.getId().get(), false).values();
- for (PatchSet priorPs : allPrior) {
- List<PatchSetApproval> priorApprovals = all.get(priorPs.getId());
- if (priorApprovals.isEmpty()) {
- continue;
- }
-
- ChangeKind kind =
- changeKindCache.getChangeKind(
- project.getNameKey(),
- rw,
- repoConfig,
- ObjectId.fromString(priorPs.getRevision().get()),
- ObjectId.fromString(ps.getRevision().get()));
-
- for (PatchSetApproval psa : priorApprovals) {
- if (wontCopy.contains(psa.getLabel(), psa.getAccountId())) {
- continue;
- }
- if (byUser.contains(psa.getLabel(), psa.getAccountId())) {
- continue;
- }
- if (!canCopy(project, psa, ps.getId(), kind)) {
- wontCopy.put(psa.getLabel(), psa.getAccountId(), psa);
- continue;
- }
- byUser.put(psa.getLabel(), psa.getAccountId(), copy(psa, ps.getId()));
- }
- }
- return labelNormalizer.normalize(notes, byUser.values()).getNormalized();
+ ProjectState project;
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Computing labels for patch set",
+ Metadata.builder()
+ .changeId(notes.load().getChangeId().get())
+ .patchSetId(psId.get())
+ .build())) {
+ project = projectCache.checkedGet(notes.getProjectName());
+ Collection<PatchSetApproval> approvals =
+ getForPatchSetWithoutNormalization(notes, project, psId, rw, repoConfig);
+ return labelNormalizer.normalize(notes, approvals).getNormalized();
} catch (IOException e) {
throw new StorageException(e);
}
}
- private static TreeMap<Integer, PatchSet> getPatchSets(ChangeData cd) {
- Collection<PatchSet> patchSets = cd.patchSets();
- TreeMap<Integer, PatchSet> result = new TreeMap<>();
- for (PatchSet ps : patchSets) {
- result.put(ps.getId().get(), ps);
- }
- return result;
- }
-
private static boolean canCopy(
ProjectState project, PatchSetApproval psa, PatchSet.Id psId, ChangeKind kind) {
- int n = psa.getKey().getParentKey().get();
+ int n = psa.key().patchSetId().get();
checkArgument(n != psId.get());
- LabelType type = project.getLabelTypes().byLabel(psa.getLabelId());
+ LabelType type = project.getLabelTypes().byLabel(psa.labelId());
if (type == null) {
logger.atFine().log(
"approval %d on label %s of patch set %d of change %d cannot be copied"
+ " to patch set %d because the label no longer exists on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
project.getName());
return false;
@@ -186,10 +112,10 @@ public class ApprovalCopier {
logger.atFine().log(
"veto approval %s on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because the label has set copyMinScore = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
project.getName());
return true;
@@ -197,10 +123,21 @@ public class ApprovalCopier {
logger.atFine().log(
"max approval %s on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because the label has set copyMaxScore = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
+ n,
+ psa.key().patchSetId().changeId().get(),
+ psId.get(),
+ project.getName());
+ return true;
+ } else if (type.isCopyAnyScore()) {
+ logger.atFine().log(
+ "approval %d on label %s of patch set %d of change %d can be copied"
+ + " to patch set %d because the label has set copyAnyScore = true on project %s",
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
project.getName());
return true;
@@ -212,10 +149,10 @@ public class ApprovalCopier {
"approval %d on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because change kind is %s and the label has set"
+ " copyAllScoresOnMergeFirstParentUpdate = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
kind,
project.getName());
@@ -228,10 +165,10 @@ public class ApprovalCopier {
"approval %d on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because change kind is %s and the label has set"
+ " copyAllScoresIfNoCodeChange = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
kind,
project.getName());
@@ -244,10 +181,10 @@ public class ApprovalCopier {
"approval %d on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because change kind is %s and the label has set"
+ " copyAllScoresOnTrivialRebase = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
kind,
project.getName());
@@ -260,10 +197,10 @@ public class ApprovalCopier {
"approval %d on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because change kind is %s and the label has set"
+ " copyAllScoresIfNoCodeChange = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
kind,
project.getName());
@@ -274,10 +211,10 @@ public class ApprovalCopier {
"approval %d on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because change kind is %s and the label has set"
+ " copyAllScoresOnTrivialRebase = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
kind,
project.getName());
@@ -288,10 +225,10 @@ public class ApprovalCopier {
"approval %d on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because change kind is %s and the label has set"
+ " copyAllScoresOnMergeFirstParentUpdate = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
kind,
project.getName());
@@ -302,10 +239,10 @@ public class ApprovalCopier {
"approval %d on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because change kind is %s and the label has set"
+ " copyAllScoresIfNoCodeChange = true on project %s",
- psa.getValue(),
- psa.getLabel(),
+ psa.value(),
+ psa.label(),
n,
- psa.getKey().getParentKey().changeId.get(),
+ psa.key().patchSetId().changeId().get(),
psId.get(),
kind,
project.getName());
@@ -317,20 +254,80 @@ public class ApprovalCopier {
logger.atFine().log(
"approval %d on label %s of patch set %d of change %d cannot be copied"
+ " to patch set %d because change kind is %s",
- psa.getValue(),
- psa.getLabel(),
- n,
- psa.getKey().getParentKey().changeId.get(),
- psId.get(),
- kind);
+ psa.value(), psa.label(), n, psa.key().patchSetId().changeId().get(), psId.get(), kind);
return false;
}
}
- private static PatchSetApproval copy(PatchSetApproval src, PatchSet.Id psId) {
- if (src.getKey().getParentKey().equals(psId)) {
- return src;
+ private Collection<PatchSetApproval> getForPatchSetWithoutNormalization(
+ ChangeNotes notes,
+ ProjectState project,
+ PatchSet.Id psId,
+ @Nullable RevWalk rw,
+ @Nullable Config repoConfig) {
+ checkState(
+ project.getNameKey().equals(notes.getProjectName()),
+ "project must match %s, %s",
+ project.getNameKey(),
+ notes.getProjectName());
+
+ PatchSet ps = notes.load().getPatchSets().get(psId);
+ if (ps == null) {
+ return Collections.emptyList();
+ }
+
+ // Add approvals on the given patch set to the result
+ Table<String, Account.Id, PatchSetApproval> resultByUser = HashBasedTable.create();
+ ImmutableList<PatchSetApproval> approvalsForGivenPatchSet =
+ notes.load().getApprovals().get(ps.id());
+ approvalsForGivenPatchSet.forEach(psa -> resultByUser.put(psa.label(), psa.accountId(), psa));
+
+ // Bail out immediately if this is the first patch set. Return only approvals granted on the
+ // given patch set.
+ if (psId.get() == 1) {
+ return resultByUser.values();
+ }
+
+ // Call this algorithm recursively to check if the prior patch set had approvals. This has the
+ // advantage that all caches - most importantly ChangeKindCache - have values cached for what we
+ // need for this computation.
+ // The way this algorithm is written is that any approval will be copied forward by one patch
+ // set at a time if configs and change kind allow so. Once an approval is held back - for
+ // example because the patch set is a REWORK - it will not be picked up again in a future
+ // patch set.
+ Map.Entry<PatchSet.Id, PatchSet> priorPatchSet = notes.load().getPatchSets().lowerEntry(psId);
+ if (priorPatchSet == null) {
+ return resultByUser.values();
+ }
+
+ Iterable<PatchSetApproval> priorApprovals =
+ getForPatchSetWithoutNormalization(
+ notes, project, priorPatchSet.getValue().id(), rw, repoConfig);
+ if (!priorApprovals.iterator().hasNext()) {
+ return resultByUser.values();
+ }
+
+ // Add labels from the previous patch set to the result in case the label isn't already there
+ // and settings as well as change kind allow copying.
+ ChangeKind kind =
+ changeKindCache.getChangeKind(
+ project.getNameKey(),
+ rw,
+ repoConfig,
+ priorPatchSet.getValue().commitId(),
+ ps.commitId());
+ logger.atFine().log(
+ "change kind for patch set %d of change %d against prior patch set %s is %s",
+ ps.id().get(), ps.id().changeId().get(), priorPatchSet.getValue().id().changeId(), kind);
+ for (PatchSetApproval psa : priorApprovals) {
+ if (resultByUser.contains(psa.label(), psa.accountId())) {
+ continue;
+ }
+ if (!canCopy(project, psa, ps.id(), kind)) {
+ continue;
+ }
+ resultByUser.put(psa.label(), psa.accountId(), psa.copyWithPatchSet(ps.id()));
}
- return new PatchSetApproval(psId, src);
+ return resultByUser.values();
}
}
diff --git a/java/com/google/gerrit/server/ApprovalsUtil.java b/java/com/google/gerrit/server/ApprovalsUtil.java
index 135276e33f..58b601f01f 100644
--- a/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -25,20 +25,19 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
-import com.google.common.primitives.Shorts;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.PatchSetInfo;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
@@ -71,38 +70,38 @@ import org.eclipse.jgit.revwalk.RevWalk;
* for each reviewer, even if the reviewer hasn't actually given a score to the change. To mark the
* "no score" case, a dummy approval, which may live in any of the available categories, with a
* score of 0 is used.
- *
- * <p>The methods in this class only modify the gwtorm database.
*/
@Singleton
public class ApprovalsUtil {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- public static PatchSetApproval newApproval(
+ public static PatchSetApproval.Builder newApproval(
PatchSet.Id psId, CurrentUser user, LabelId labelId, int value, Date when) {
- PatchSetApproval psa =
- new PatchSetApproval(
- new PatchSetApproval.Key(psId, user.getAccountId(), labelId),
- Shorts.checkedCast(value),
- when);
- user.updateRealAccountId(psa::setRealAccountId);
- return psa;
+ PatchSetApproval.Builder b =
+ PatchSetApproval.builder()
+ .key(PatchSetApproval.key(psId, user.getAccountId(), labelId))
+ .value(value)
+ .granted(when);
+ user.updateRealAccountId(b::realAccountId);
+ return b;
}
private static Iterable<PatchSetApproval> filterApprovals(
Iterable<PatchSetApproval> psas, Account.Id accountId) {
- return Iterables.filter(psas, a -> Objects.equals(a.getAccountId(), accountId));
+ return Iterables.filter(psas, a -> Objects.equals(a.accountId(), accountId));
}
- private final ApprovalCopier copier;
+ private final ApprovalInference approvalInference;
private final PermissionBackend permissionBackend;
private final ProjectCache projectCache;
@VisibleForTesting
@Inject
public ApprovalsUtil(
- ApprovalCopier copier, PermissionBackend permissionBackend, ProjectCache projectCache) {
- this.copier = copier;
+ ApprovalInference approvalInference,
+ PermissionBackend permissionBackend,
+ ProjectCache projectCache) {
+ this.approvalInference = approvalInference;
this.permissionBackend = permissionBackend;
this.projectCache = projectCache;
}
@@ -149,7 +148,7 @@ public class ApprovalsUtil {
update,
labelTypes,
change,
- ps.getId(),
+ ps.id(),
info.getAuthor().getAccount(),
info.getCommitter().getAccount(),
wantReviewers,
@@ -209,8 +208,11 @@ public class ApprovalsUtil {
LabelId labelId = Iterables.getLast(allTypes).getLabelId();
for (Account.Id account : need) {
cells.add(
- new PatchSetApproval(
- new PatchSetApproval.Key(psId, account, labelId), (short) 0, update.getWhen()));
+ PatchSetApproval.builder()
+ .key(PatchSetApproval.key(psId, account, labelId))
+ .value(0)
+ .granted(update.getWhen())
+ .build());
update.putReviewer(account, REVIEWER);
}
return Collections.unmodifiableList(cells);
@@ -239,17 +241,28 @@ public class ApprovalsUtil {
* @param notes change notes.
* @param update change update.
* @param wantCCs accounts to CC.
+ * @param keepExistingReviewers whether provided accounts that are already reviewer should be kept
+ * as reviewer or be downgraded to CC
* @return whether a change was made.
*/
public Collection<Account.Id> addCcs(
- ChangeNotes notes, ChangeUpdate update, Collection<Account.Id> wantCCs) {
- return addCcs(update, wantCCs, notes.load().getReviewers());
+ ChangeNotes notes,
+ ChangeUpdate update,
+ Collection<Account.Id> wantCCs,
+ boolean keepExistingReviewers) {
+ return addCcs(update, wantCCs, notes.load().getReviewers(), keepExistingReviewers);
}
private Collection<Account.Id> addCcs(
- ChangeUpdate update, Collection<Account.Id> wantCCs, ReviewerSet existingReviewers) {
+ ChangeUpdate update,
+ Collection<Account.Id> wantCCs,
+ ReviewerSet existingReviewers,
+ boolean keepExistingReviewers) {
Set<Account.Id> need = new LinkedHashSet<>(wantCCs);
- need.removeAll(existingReviewers.all());
+ need.removeAll(existingReviewers.byState(CC));
+ if (keepExistingReviewers) {
+ need.removeAll(existingReviewers.byState(REVIEWER));
+ }
need.removeAll(update.getReviewers().keySet());
for (Account.Id account : need) {
update.putReviewer(account, CC);
@@ -276,10 +289,10 @@ public class ApprovalsUtil {
throws RestApiException, PermissionBackendException {
Account.Id accountId = user.getAccountId();
checkArgument(
- accountId.equals(ps.getUploader()),
+ accountId.equals(ps.uploader()),
"expected user %s to match patch set uploader %s",
accountId,
- ps.getUploader());
+ ps.uploader());
if (approvals.isEmpty()) {
return ImmutableList.of();
}
@@ -288,10 +301,10 @@ public class ApprovalsUtil {
Date ts = update.getWhen();
for (Map.Entry<String, Short> vote : approvals.entrySet()) {
LabelType lt = labelTypes.byLabel(vote.getKey());
- cells.add(newApproval(ps.getId(), user, lt.getLabelId(), vote.getValue(), ts));
+ cells.add(newApproval(ps.id(), user, lt.getLabelId(), vote.getValue(), ts).build());
}
for (PatchSetApproval psa : cells) {
- update.putApproval(psa.getLabel(), psa.getValue());
+ update.putApproval(psa.label(), psa.value());
}
return cells;
}
@@ -329,7 +342,7 @@ public class ApprovalsUtil {
public Iterable<PatchSetApproval> byPatchSet(
ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
- return copier.getForPatchSet(notes, psId, rw, repoConfig);
+ return approvalInference.forPatchSet(notes, psId, rw, repoConfig);
}
public Iterable<PatchSetApproval> byPatchSetUser(
@@ -359,8 +372,8 @@ public class ApprovalsUtil {
}
PatchSetApproval submitter = null;
for (PatchSetApproval a : approvals) {
- if (a.getPatchSetId().equals(c) && a.getValue() > 0 && a.isLegacySubmit()) {
- if (submitter == null || a.getGranted().compareTo(submitter.getGranted()) > 0) {
+ if (a.patchSetId().equals(c) && a.value() > 0 && a.isLegacySubmit()) {
+ if (submitter == null || a.granted().compareTo(submitter.granted()) > 0) {
submitter = a;
}
}
@@ -374,7 +387,7 @@ public class ApprovalsUtil {
if (!n.isEmpty()) {
boolean first = true;
for (Map.Entry<String, Short> e : n.entrySet()) {
- if (c.containsKey(e.getKey()) && c.get(e.getKey()).getValue() == e.getValue()) {
+ if (c.containsKey(e.getKey()) && c.get(e.getKey()).value() == e.getValue()) {
continue;
}
if (first) {
diff --git a/java/com/google/gerrit/server/AssigneeStatusUpdate.java b/java/com/google/gerrit/server/AssigneeStatusUpdate.java
new file mode 100644
index 0000000000..3d6242ba9e
--- /dev/null
+++ b/java/com/google/gerrit/server/AssigneeStatusUpdate.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.entities.Account;
+import java.sql.Timestamp;
+import java.util.Optional;
+
+/** Change to an assignee's status. */
+@AutoValue
+public abstract class AssigneeStatusUpdate {
+ public static AssigneeStatusUpdate create(
+ Timestamp ts, Account.Id updatedBy, Optional<Account.Id> currentAssignee) {
+ return new AutoValue_AssigneeStatusUpdate(ts, updatedBy, currentAssignee);
+ }
+
+ public abstract Timestamp date();
+
+ public abstract Account.Id updatedBy();
+
+ public abstract Optional<Account.Id> currentAssignee();
+}
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index 9e7f3dd4fc..667559527e 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -9,6 +9,11 @@ GERRIT_GLOBAL_MODULE_SRC = [
"config/GerritGlobalModule.java",
]
+TESTING_SRC = [
+ "account/externalids/testing/ExternalIdInserter.java",
+ "account/externalids/testing/ExternalIdTestUtil.java",
+]
+
java_library(
name = "constants",
srcs = CONSTANTS_SRC,
@@ -25,7 +30,7 @@ java_library(
name = "server",
srcs = glob(
["**/*.java"],
- exclude = CONSTANTS_SRC + GERRIT_GLOBAL_MODULE_SRC,
+ exclude = CONSTANTS_SRC + GERRIT_GLOBAL_MODULE_SRC + TESTING_SRC,
),
resource_strip_prefix = "resources",
resources = ["//resources/com/google/gerrit/server"],
@@ -34,6 +39,7 @@ java_library(
":constants",
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/git",
@@ -47,7 +53,6 @@ java_library(
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/prettify:server",
"//java/com/google/gerrit/proto",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server/cache/serialize",
"//java/com/google/gerrit/server/ioutil",
"//java/com/google/gerrit/server/logging",
@@ -55,7 +60,6 @@ java_library(
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/ssl",
- "//java/com/google/gwtorm",
"//java/org/apache/commons/net",
"//lib:args4j",
"//lib:autolink",
@@ -89,6 +93,8 @@ java_library(
"//lib:gson",
"//lib:guava",
"//lib:guava-retrying",
+ "//lib:jgit",
+ "//lib:jgit-archive",
"//lib:jsch",
"//lib:juniversalchardet",
"//lib:mime-util",
@@ -110,8 +116,6 @@ java_library(
"//lib/guice",
"//lib/guice:guice-assistedinject",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit.archive:jgit-archive",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/jsoup",
"//lib/log:jsonevent-layout",
"//lib/log:log4j",
@@ -138,12 +142,13 @@ java_library(
":server",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/server/git/receive",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/restapi",
"//lib:blame-cache",
"//lib:guava",
+ "//lib:jgit",
"//lib:soy",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/server/ChangeMessagesUtil.java b/java/com/google/gerrit/server/ChangeMessagesUtil.java
index 97ba8f09f4..5f00b69890 100644
--- a/java/com/google/gerrit/server/ChangeMessagesUtil.java
+++ b/java/com/google/gerrit/server/ChangeMessagesUtil.java
@@ -18,10 +18,10 @@ import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
@@ -72,10 +72,7 @@ public class ChangeMessagesUtil {
Account.Id accountId = user.isInternalUser() ? null : user.getAccountId();
ChangeMessage m =
new ChangeMessage(
- new ChangeMessage.Key(psId.getParentKey(), ChangeUtil.messageUuid()),
- accountId,
- when,
- psId);
+ ChangeMessage.key(psId.changeId(), ChangeUtil.messageUuid()), accountId, when, psId);
m.setMessage(body);
m.setTag(tag);
user.updateRealAccountId(m::setRealAuthor);
@@ -127,7 +124,7 @@ public class ChangeMessagesUtil {
ChangeMessage message, AccountLoader accountLoader) {
PatchSet.Id patchNum = message.getPatchSetId();
ChangeMessageInfo cmi = new ChangeMessageInfo();
- cmi.id = message.getKey().get();
+ cmi.id = message.getKey().uuid();
cmi.author = accountLoader.get(message.getAuthor());
cmi.date = message.getWrittenOn();
cmi.message = message.getMessage();
diff --git a/java/com/google/gerrit/server/ChangeUtil.java b/java/com/google/gerrit/server/ChangeUtil.java
index b8a00f4029..ee82a2624a 100644
--- a/java/com/google/gerrit/server/ChangeUtil.java
+++ b/java/com/google/gerrit/server/ChangeUtil.java
@@ -19,8 +19,8 @@ import static java.util.stream.Collectors.toSet;
import com.google.common.collect.Ordering;
import com.google.common.io.BaseEncoding;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.inject.Singleton;
import java.io.IOException;
import java.security.SecureRandom;
@@ -40,7 +40,7 @@ public class ChangeUtil {
private static final BaseEncoding UUID_ENCODING = BaseEncoding.base16().lowerCase();
public static final Ordering<PatchSet> PS_ID_ORDER =
- Ordering.from(comparingInt(PatchSet::getPatchSetId));
+ Ordering.from(comparingInt(PatchSet::number));
/** @return a new unique identifier for change message entities. */
public static String messageUuid() {
@@ -83,7 +83,7 @@ public class ChangeUtil {
Set<PatchSet.Id> existing =
changeRefNames
.map(PatchSet.Id::fromRef)
- .filter(psId -> psId != null && psId.getParentKey().equals(id.getParentKey()))
+ .filter(psId -> psId != null && psId.changeId().equals(id.changeId()))
.collect(toSet());
PatchSet.Id next = nextPatchSetId(id);
while (existing.contains(next)) {
@@ -103,7 +103,7 @@ public class ChangeUtil {
* @return next patch set ID for the same change, incrementing by 1.
*/
public static PatchSet.Id nextPatchSetId(PatchSet.Id id) {
- return new PatchSet.Id(id.getParentKey(), id.get() + 1);
+ return PatchSet.id(id.changeId(), id.get() + 1);
}
/**
@@ -116,7 +116,7 @@ public class ChangeUtil {
*/
public static PatchSet.Id nextPatchSetId(Repository git, PatchSet.Id id) throws IOException {
return nextPatchSetIdFromChangeRefs(
- git.getRefDatabase().getRefsByPrefix(id.getParentKey().toRefPrefix()).stream()
+ git.getRefDatabase().getRefsByPrefix(id.changeId().toRefPrefix()).stream()
.map(Ref::getName),
id);
}
diff --git a/java/com/google/gerrit/server/CmdLineParserModule.java b/java/com/google/gerrit/server/CmdLineParserModule.java
index d7f6e308b6..d943889d98 100644
--- a/java/com/google/gerrit/server/CmdLineParserModule.java
+++ b/java/com/google/gerrit/server/CmdLineParserModule.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.args4j.AccountGroupIdHandler;
import com.google.gerrit.server.args4j.AccountGroupUUIDHandler;
import com.google.gerrit.server.args4j.AccountIdHandler;
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index a5332eb284..ae4ba4b876 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -19,22 +19,20 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.Side;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -50,7 +48,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -92,7 +89,7 @@ public class CommentsUtil {
};
public static PatchSet.Id getCommentPsId(Change.Id changeId, Comment comment) {
- return new PatchSet.Id(changeId, comment.key.patchSetId);
+ return PatchSet.id(changeId, comment.key.patchSetId);
}
public static String extractMessageId(@Nullable String tag) {
@@ -131,7 +128,7 @@ public class CommentsUtil {
unresolved = false;
} else {
// Inherit unresolved value from inReplyTo comment if not specified.
- Comment.Key key = new Comment.Key(parentUuid, path, psId.patchSetId);
+ Comment.Key key = new Comment.Key(parentUuid, path, psId.get());
Optional<Comment> parent = getPublished(ctx.getNotes(), key);
if (!parent.isPresent()) {
throw new UnprocessableEntityException("Invalid parentUuid supplied for comment");
@@ -260,8 +257,7 @@ public class CommentsUtil {
return sort(comments);
}
- public void putComments(
- ChangeUpdate update, PatchLineComment.Status status, Iterable<Comment> comments) {
+ public void putComments(ChangeUpdate update, Comment.Status status, Iterable<Comment> comments) {
for (Comment c : comments) {
update.putComment(status, c);
}
@@ -306,22 +302,22 @@ public class CommentsUtil {
return sort(result);
}
- public static void setCommentRevId(Comment c, PatchListCache cache, Change change, PatchSet ps)
+ public static void setCommentCommitId(Comment c, PatchListCache cache, Change change, PatchSet ps)
throws PatchListNotAvailableException {
checkArgument(
- c.key.patchSetId == ps.getId().get(),
- "cannot set RevId for patch set %s on comment %s",
- ps.getId(),
+ c.key.patchSetId == ps.id().get(),
+ "cannot set commit ID for patch set %s on comment %s",
+ ps.id(),
c);
- if (c.revId == null) {
+ if (c.getCommitId() == null) {
if (Side.fromShort(c.side) == Side.PARENT) {
if (c.side < 0) {
- c.revId = ObjectId.toString(cache.getOldId(change, ps, -c.side));
+ c.setCommitId(cache.getOldId(change, ps, -c.side));
} else {
- c.revId = ObjectId.toString(cache.getOldId(change, ps, null));
+ c.setCommitId(cache.getOldId(change, ps, null));
}
} else {
- c.revId = ps.getRevision().get();
+ c.setCommitId(ps.commitId());
}
}
}
@@ -354,15 +350,4 @@ public class CommentsUtil {
comments.sort(COMMENT_ORDER);
return comments;
}
-
- public static Iterable<PatchLineComment> toPatchLineComments(
- Change.Id changeId, PatchLineComment.Status status, Iterable<Comment> comments) {
- return FluentIterable.from(comments).transform(c -> PatchLineComment.from(changeId, status, c));
- }
-
- public static List<Comment> toComments(
- final String serverId, Iterable<PatchLineComment> comments) {
- return COMMENT_ORDER.sortedCopy(
- FluentIterable.from(comments).transform(plc -> plc.asComment(serverId)));
- }
}
diff --git a/java/com/google/gerrit/server/CreateGroupPermissionSyncer.java b/java/com/google/gerrit/server/CreateGroupPermissionSyncer.java
index 27fb9d7a7d..996257c12f 100644
--- a/java/com/google/gerrit/server/CreateGroupPermissionSyncer.java
+++ b/java/com/google/gerrit/server/CreateGroupPermissionSyncer.java
@@ -22,8 +22,8 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.events.ChangeMergedListener;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
diff --git a/java/com/google/gerrit/server/CurrentUser.java b/java/com/google/gerrit/server/CurrentUser.java
index 03b9f54e12..75afc04ce3 100644
--- a/java/com/google/gerrit/server/CurrentUser.java
+++ b/java/com/google/gerrit/server/CurrentUser.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.inject.servlet.RequestScoped;
diff --git a/java/com/google/gerrit/server/ExceptionHook.java b/java/com/google/gerrit/server/ExceptionHook.java
new file mode 100644
index 0000000000..a0d98d2866
--- /dev/null
+++ b/java/com/google/gerrit/server/ExceptionHook.java
@@ -0,0 +1,42 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/**
+ * Allows implementors to control how certain exceptions should be handled.
+ *
+ * <p>This interface is intended to be implemented for cluster setups with multiple primary nodes to
+ * control the behavior for handling exceptions that are thrown by a lower layer that handles the
+ * consensus and synchronization between different server nodes. E.g. if an operation fails because
+ * consensus for a Git update could not be achieved (e.g. due to slow responding server nodes) this
+ * interface can be used to retry the request instead of failing it immediately.
+ */
+@ExtensionPoint
+public interface ExceptionHook {
+ /**
+ * Whether an operation should be retried if it failed with the given throwable.
+ *
+ * <p>Only affects operations that are executed with {@link
+ * com.google.gerrit.server.update.RetryHelper}.
+ *
+ * @param throwable throwable that was thrown while executing the operation
+ * @return whether the operation should be retried
+ */
+ default boolean shouldRetry(Throwable throwable) {
+ return false;
+ }
+}
diff --git a/java/com/google/gerrit/server/IdentifiedUser.java b/java/com/google/gerrit/server/IdentifiedUser.java
index e65f56257e..7cafdc046d 100644
--- a/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/java/com/google/gerrit/server/IdentifiedUser.java
@@ -22,7 +22,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.GroupBackend;
@@ -234,7 +234,7 @@ public class IdentifiedUser extends CurrentUser {
groupBackend,
enableReverseDnsLookup,
remotePeerProvider,
- state.getAccount().getId(),
+ state.account().id(),
realUser);
this.state = state;
}
@@ -323,15 +323,14 @@ public class IdentifiedUser extends CurrentUser {
*/
@Override
public Optional<String> getUserName() {
- return state().getUserName();
+ return state().userName();
}
/** @return unique name of the user for logging, never {@code null} */
@Override
public String getLoggableName() {
return getUserName()
- .orElseGet(
- () -> firstNonNull(getAccount().getPreferredEmail(), "a/" + getAccountId().get()));
+ .orElseGet(() -> firstNonNull(getAccount().preferredEmail(), "a/" + getAccountId().get()));
}
/**
@@ -340,7 +339,7 @@ public class IdentifiedUser extends CurrentUser {
* @return the account of the identified user, an empty account if the account is missing
*/
public Account getAccount() {
- return state().getAccount();
+ return state().account();
}
public boolean hasEmailAddress(String email) {
@@ -377,7 +376,7 @@ public class IdentifiedUser extends CurrentUser {
@Override
public GroupMembership getEffectiveGroups() {
if (effectiveGroups == null) {
- if (authConfig.isIdentityTrustable(state().getExternalIds())) {
+ if (authConfig.isIdentityTrustable(state().externalIds())) {
effectiveGroups = groupBackend.membershipsOf(this);
logger.atFinest().log(
"Known groups of %s: %s", getLoggableName(), lazy(effectiveGroups::getKnownGroups));
@@ -403,29 +402,29 @@ public class IdentifiedUser extends CurrentUser {
public PersonIdent newRefLogIdent(Date when, TimeZone tz) {
final Account ua = getAccount();
- String name = ua.getFullName();
+ String name = ua.fullName();
if (name == null || name.isEmpty()) {
- name = ua.getPreferredEmail();
+ name = ua.preferredEmail();
}
if (name == null || name.isEmpty()) {
name = anonymousCowardName;
}
- String user = getUserName().orElse("") + "|account-" + ua.getId().toString();
+ String user = getUserName().orElse("") + "|account-" + ua.id().toString();
return new PersonIdent(name, user + "@" + guessHost(), when, tz);
}
public PersonIdent newCommitterIdent(Date when, TimeZone tz) {
final Account ua = getAccount();
- String name = ua.getFullName();
- String email = ua.getPreferredEmail();
+ String name = ua.fullName();
+ String email = ua.preferredEmail();
if (email == null || email.isEmpty()) {
// No preferred email is configured. Use a generic identity so we
// don't leak an address the user may have given us, but doesn't
// necessarily want to publish through Git records.
//
- String user = getUserName().orElseGet(() -> "account-" + ua.getId().toString());
+ String user = getUserName().orElseGet(() -> "account-" + ua.id().toString());
String host;
if (canonicalUrl.get() != null) {
diff --git a/java/com/google/gerrit/server/PatchSetUtil.java b/java/com/google/gerrit/server/PatchSetUtil.java
index 2a78eb6c77..b53e666ab1 100644
--- a/java/com/google/gerrit/server/PatchSetUtil.java
+++ b/java/com/google/gerrit/server/PatchSetUtil.java
@@ -20,14 +20,14 @@ import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
@@ -39,6 +39,7 @@ import com.google.inject.Singleton;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@@ -89,8 +90,8 @@ public class PatchSetUtil {
PatchSet.Id psId,
ObjectId commit,
List<String> groups,
- String pushCertificate,
- String description)
+ @Nullable String pushCertificate,
+ @Nullable String description)
throws IOException {
requireNonNull(groups, "groups may not be null");
ensurePatchSetMatches(psId, update);
@@ -99,20 +100,21 @@ public class PatchSetUtil {
update.setPsDescription(description);
update.setGroups(groups);
- PatchSet ps = new PatchSet(psId);
- ps.setRevision(new RevId(commit.name()));
- ps.setUploader(update.getAccountId());
- ps.setCreatedOn(new Timestamp(update.getWhen().getTime()));
- ps.setGroups(groups);
- ps.setPushCertificate(pushCertificate);
- ps.setDescription(description);
- return ps;
+ return PatchSet.builder()
+ .id(psId)
+ .commitId(commit)
+ .uploader(update.getAccountId())
+ .createdOn(new Timestamp(update.getWhen().getTime()))
+ .groups(groups)
+ .pushCertificate(Optional.ofNullable(pushCertificate))
+ .description(Optional.ofNullable(description))
+ .build();
}
private static void ensurePatchSetMatches(PatchSet.Id psId, ChangeUpdate update) {
- Change.Id changeId = update.getChange().getId();
+ Change.Id changeId = update.getId();
checkArgument(
- psId.getParentKey().equals(changeId),
+ psId.changeId().equals(changeId),
"cannot modify patch set %s on update for change %s",
psId,
changeId);
@@ -127,11 +129,6 @@ public class PatchSetUtil {
}
}
- public void setGroups(ChangeUpdate update, PatchSet ps, List<String> groups) {
- ps.setGroups(groups);
- update.setGroups(groups);
- }
-
/** Check if the current patch set of the change is locked. */
public void checkPatchSetNotLocked(ChangeNotes notes)
throws IOException, ResourceConflictException {
@@ -155,10 +152,8 @@ public class PatchSetUtil {
ApprovalsUtil approvalsUtil = approvalsUtilProvider.get();
for (PatchSetApproval ap :
approvalsUtil.byPatchSet(notes, change.currentPatchSetId(), null, null)) {
- LabelType type = projectState.getLabelTypes(notes).byLabel(ap.getLabel());
- if (type != null
- && ap.getValue() == 1
- && type.getFunction() == LabelFunction.PATCH_SET_LOCK) {
+ LabelType type = projectState.getLabelTypes(notes).byLabel(ap.label());
+ if (type != null && ap.value() == 1 && type.getFunction() == LabelFunction.PATCH_SET_LOCK) {
return true;
}
}
@@ -169,7 +164,7 @@ public class PatchSetUtil {
public RevCommit getRevCommit(Project.NameKey project, PatchSet patchSet) throws IOException {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
- RevCommit src = rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
+ RevCommit src = rw.parseCommit(patchSet.commitId());
rw.parseBody(src);
return src;
}
diff --git a/java/com/google/gerrit/server/ProjectUtil.java b/java/com/google/gerrit/server/ProjectUtil.java
index 1db4aa3a6f..fa056b3585 100644
--- a/java/com/google/gerrit/server/ProjectUtil.java
+++ b/java/com/google/gerrit/server/ProjectUtil.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.git.GitRepositoryManager;
import java.io.IOException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -33,13 +33,14 @@ public class ProjectUtil {
* @throws RepositoryNotFoundException the repository of the branch's project does not exist.
* @throws IOException error while retrieving the branch from the repository.
*/
- public static boolean branchExists(final GitRepositoryManager repoManager, Branch.NameKey branch)
+ public static boolean branchExists(final GitRepositoryManager repoManager, BranchNameKey branch)
throws RepositoryNotFoundException, IOException {
- try (Repository repo = repoManager.openRepository(branch.getParentKey())) {
- boolean exists = repo.getRefDatabase().exactRef(branch.get()) != null;
+ try (Repository repo = repoManager.openRepository(branch.project())) {
+ boolean exists = repo.getRefDatabase().exactRef(branch.branch()) != null;
if (!exists) {
exists =
- repo.getFullBranch().equals(branch.get()) || RefNames.REFS_CONFIG.equals(branch.get());
+ repo.getFullBranch().equals(branch.branch())
+ || RefNames.REFS_CONFIG.equals(branch.branch());
}
return exists;
}
diff --git a/java/com/google/gerrit/server/PublishCommentUtil.java b/java/com/google/gerrit/server/PublishCommentUtil.java
index ad93ef008d..26539c52db 100644
--- a/java/com/google/gerrit/server/PublishCommentUtil.java
+++ b/java/com/google/gerrit/server/PublishCommentUtil.java
@@ -15,16 +15,21 @@
package com.google.gerrit.server;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.reviewdb.client.PatchLineComment.Status.PUBLISHED;
+import static com.google.gerrit.entities.Comment.Status;
import static java.util.stream.Collectors.toSet;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.extensions.validators.CommentForValidation;
+import com.google.gerrit.extensions.validators.CommentValidationFailure;
+import com.google.gerrit.extensions.validators.CommentValidator;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.update.ChangeContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -46,35 +51,55 @@ public class PublishCommentUtil {
}
public void publish(
- ChangeContext ctx, PatchSet.Id psId, Collection<Comment> drafts, @Nullable String tag) {
+ ChangeContext ctx,
+ PatchSet.Id psId,
+ Collection<Comment> draftComments,
+ @Nullable String tag) {
ChangeNotes notes = ctx.getNotes();
checkArgument(notes != null);
- if (drafts.isEmpty()) {
+ if (draftComments.isEmpty()) {
return;
}
Map<PatchSet.Id, PatchSet> patchSets =
- psUtil.getAsMap(notes, drafts.stream().map(d -> psId(notes, d)).collect(toSet()));
- for (Comment d : drafts) {
- PatchSet ps = patchSets.get(psId(notes, d));
+ psUtil.getAsMap(notes, draftComments.stream().map(d -> psId(notes, d)).collect(toSet()));
+ for (Comment draftComment : draftComments) {
+ PatchSet.Id psIdOfDraftComment = psId(notes, draftComment);
+ PatchSet ps = patchSets.get(psIdOfDraftComment);
if (ps == null) {
- throw new StorageException("patch set " + ps + " not found");
+ throw new StorageException("patch set " + psIdOfDraftComment + " not found");
}
- d.writtenOn = ctx.getWhen();
- d.tag = tag;
+ draftComment.writtenOn = ctx.getWhen();
+ draftComment.tag = tag;
// Draft may have been created by a different real user; copy the current real user. (Only
// applies to X-Gerrit-RunAs, since modifying drafts via on_behalf_of is not allowed.)
- ctx.getUser().updateRealAccountId(d::setRealAuthor);
+ ctx.getUser().updateRealAccountId(draftComment::setRealAuthor);
try {
- CommentsUtil.setCommentRevId(d, patchListCache, notes.getChange(), ps);
+ CommentsUtil.setCommentCommitId(draftComment, patchListCache, notes.getChange(), ps);
} catch (PatchListNotAvailableException e) {
throw new StorageException(e);
}
}
- commentsUtil.putComments(ctx.getUpdate(psId), PUBLISHED, drafts);
+ commentsUtil.putComments(ctx.getUpdate(psId), Status.PUBLISHED, draftComments);
}
private static PatchSet.Id psId(ChangeNotes notes, Comment c) {
- return new PatchSet.Id(notes.getChangeId(), c.key.patchSetId);
+ return PatchSet.id(notes.getChangeId(), c.key.patchSetId);
+ }
+
+ /**
+ * Helper to run the specified set of {@link CommentValidator}-s on the specified comments.
+ *
+ * @return See {@link CommentValidator#validateComments(ImmutableList)}.
+ */
+ public static ImmutableList<CommentValidationFailure> findInvalidComments(
+ PluginSetContext<CommentValidator> commentValidators,
+ ImmutableList<CommentForValidation> commentsForValidation) {
+ ImmutableList.Builder<CommentValidationFailure> commentValidationFailures =
+ new ImmutableList.Builder<>();
+ commentValidators.runEach(
+ listener ->
+ commentValidationFailures.addAll(listener.validateComments(commentsForValidation)));
+ return commentValidationFailures.build();
}
}
diff --git a/java/com/google/gerrit/server/RequestInfo.java b/java/com/google/gerrit/server/RequestInfo.java
new file mode 100644
index 0000000000..f369239d72
--- /dev/null
+++ b/java/com/google/gerrit/server/RequestInfo.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.logging.TraceContext;
+import java.util.Optional;
+
+/** Information about a request that was received from a user. */
+@AutoValue
+public abstract class RequestInfo {
+ /** Channel through which a user request was received. */
+ public enum RequestType {
+ /** request type for git push */
+ GIT_RECEIVE,
+
+ /** request type for git fetch */
+ GIT_UPLOAD,
+
+ /** request type for call to REST API */
+ REST,
+
+ /** request type for call to SSH API */
+ SSH
+ }
+
+ /**
+ * Type of the request, telling through which channel the request was coming in.
+ *
+ * <p>See {@link RequestType} for the types that are used by Gerrit core. Other request types are
+ * possible, e.g. if a plugin supports receiving requests through another channel.
+ */
+ public abstract String requestType();
+
+ /**
+ * Request URI.
+ *
+ * <p>Only set if request type is {@link RequestType#REST}.
+ *
+ * <p>Never includes the "/a" prefix.
+ */
+ public abstract Optional<String> requestUri();
+
+ /** The user that has sent the request. */
+ public abstract CurrentUser callingUser();
+
+ /** The trace context of the request. */
+ public abstract TraceContext traceContext();
+
+ /**
+ * The name of the project for which the request is being done. Only available if the request is
+ * tied to a project or change. If a project is available it's not guaranteed that it actually
+ * exists (e.g. if a user made a request for a project that doesn't exist).
+ */
+ public abstract Optional<Project.NameKey> project();
+
+ public static RequestInfo.Builder builder(
+ RequestType requestType, CurrentUser callingUser, TraceContext traceContext) {
+ return new AutoValue_RequestInfo.Builder()
+ .requestType(requestType)
+ .callingUser(callingUser)
+ .traceContext(traceContext);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder requestType(String requestType);
+
+ public Builder requestType(RequestType requestType) {
+ return requestType(requestType.name());
+ }
+
+ public abstract Builder requestUri(String requestUri);
+
+ public abstract Builder callingUser(CurrentUser callingUser);
+
+ public abstract Builder traceContext(TraceContext traceContext);
+
+ public abstract Builder project(Project.NameKey projectName);
+
+ public abstract RequestInfo build();
+ }
+}
diff --git a/java/com/google/gerrit/server/RequestListener.java b/java/com/google/gerrit/server/RequestListener.java
new file mode 100644
index 0000000000..461b91aecb
--- /dev/null
+++ b/java/com/google/gerrit/server/RequestListener.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+@ExtensionPoint
+public interface RequestListener {
+ void onRequest(RequestInfo requestInfo);
+}
diff --git a/java/com/google/gerrit/server/ReviewerSet.java b/java/com/google/gerrit/server/ReviewerSet.java
index f36e3abe48..0f6bf2927d 100644
--- a/java/com/google/gerrit/server/ReviewerSet.java
+++ b/java/com/google/gerrit/server/ReviewerSet.java
@@ -22,8 +22,8 @@ import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import java.sql.Timestamp;
@@ -44,18 +44,14 @@ public class ReviewerSet {
first = psa;
} else {
checkArgument(
- first
- .getKey()
- .getParentKey()
- .getParentKey()
- .equals(psa.getKey().getParentKey().getParentKey()),
+ first.key().patchSetId().changeId().equals(psa.key().patchSetId().changeId()),
"multiple change IDs: %s, %s",
- first.getKey(),
- psa.getKey());
+ first.key(),
+ psa.key());
}
- Account.Id id = psa.getAccountId();
- reviewers.put(REVIEWER, id, psa.getGranted());
- if (psa.getValue() != 0) {
+ Account.Id id = psa.accountId();
+ reviewers.put(REVIEWER, id, psa.granted());
+ if (psa.value() != 0) {
reviewers.remove(CC, id);
}
}
diff --git a/java/com/google/gerrit/server/ReviewerStatusUpdate.java b/java/com/google/gerrit/server/ReviewerStatusUpdate.java
index c4f3e2a24e..938d98508e 100644
--- a/java/com/google/gerrit/server/ReviewerStatusUpdate.java
+++ b/java/com/google/gerrit/server/ReviewerStatusUpdate.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import java.sql.Timestamp;
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java
index deca550483..582424063a 100644
--- a/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -31,18 +31,20 @@ import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.git.GitUpdateFailureException;
import com.google.gerrit.git.LockFailureException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndexer;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -89,7 +91,7 @@ public class StarredChangesUtil {
if (id == null) {
return null;
}
- Account.Id accountId = new Account.Id(id);
+ Account.Id accountId = Account.id(id);
String label = s.substring(p + 1);
return create(accountId, label);
}
@@ -234,7 +236,16 @@ public class StarredChangesUtil {
}
}
- public void unstarAll(Project.NameKey project, Change.Id changeId) {
+ /**
+ * Unstar the given change for all users.
+ *
+ * <p>Intended for use only when we're about to delete a change. For that reason, the change is
+ * not reindexed.
+ *
+ * @param changeId change ID.
+ * @throws IOException if an error occurred.
+ */
+ public void unstarAllForChangeDeletion(Change.Id changeId) throws IOException {
try (Repository repo = repoManager.openRepository(allUsers);
RevWalk rw = new RevWalk(repo)) {
BatchRefUpdate batchUpdate = repo.getRefDatabase().newBatchUpdate();
@@ -244,7 +255,9 @@ public class StarredChangesUtil {
for (Account.Id accountId : byChangeFromIndex(changeId).keySet()) {
String refName = RefNames.refsStarredChanges(changeId, accountId);
Ref ref = repo.getRefDatabase().exactRef(refName);
- batchUpdate.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), refName));
+ if (ref != null) {
+ batchUpdate.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), refName));
+ }
}
batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
for (ReceiveCommand command : batchUpdate.getCommands()) {
@@ -256,12 +269,9 @@ public class StarredChangesUtil {
if (command.getResult() == ReceiveCommand.Result.LOCK_FAILURE) {
throw new LockFailureException(message, batchUpdate);
}
- throw new IOException(message);
+ throw new GitUpdateFailureException(message, batchUpdate);
}
}
- indexer.index(project, changeId);
- } catch (IOException e) {
- throw new StorageException(String.format("Unstar change %d failed", changeId.get()), e);
}
}
@@ -273,7 +283,7 @@ public class StarredChangesUtil {
if (id == null) {
continue;
}
- Account.Id accountId = new Account.Id(id);
+ Account.Id accountId = Account.id(id);
builder.put(accountId, readLabels(repo, RefNames.refsStarredChanges(changeId, accountId)));
}
return builder.build();
@@ -375,7 +385,9 @@ public class StarredChangesUtil {
}
public static StarRef readLabels(Repository repo, String refName) throws IOException {
- try (TraceTimer traceTimer = TraceContext.newTimer("Read star labels from %s", refName)) {
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Read star labels", Metadata.builder().noteDbRefName(refName).build())) {
Ref ref = repo.exactRef(refName);
if (ref == null) {
return StarRef.MISSING;
@@ -449,7 +461,9 @@ public class StarredChangesUtil {
Repository repo, String refName, ObjectId oldObjectId, Collection<String> labels)
throws IOException, InvalidLabelsException {
try (TraceTimer traceTimer =
- TraceContext.newTimer("Update star labels in %s (labels=%s)", refName, labels);
+ TraceContext.newTimer(
+ "Update star labels",
+ Metadata.builder().noteDbRefName(refName).resourceCount(labels.size()).build());
RevWalk rw = new RevWalk(repo)) {
RefUpdate u = repo.updateRef(refName);
u.setExpectedOldObjectId(oldObjectId);
@@ -486,7 +500,9 @@ public class StarredChangesUtil {
return;
}
- try (TraceTimer traceTimer = TraceContext.newTimer("Delete star labels in %s", refName)) {
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Delete star labels", Metadata.builder().noteDbRefName(refName).build())) {
RefUpdate u = repo.updateRef(refName);
u.setForceUpdate(true);
u.setExpectedOldObjectId(oldObjectId);
diff --git a/java/com/google/gerrit/server/TraceRequestListener.java b/java/com/google/gerrit/server/TraceRequestListener.java
new file mode 100644
index 0000000000..20c9f57bf8
--- /dev/null
+++ b/java/com/google/gerrit/server/TraceRequestListener.java
@@ -0,0 +1,228 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.logging.RequestId;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+
+/**
+ * Request listener that sets additional logging tags and enables tracing automatically if the
+ * request matches any tracing configuration in gerrit.config (see description of
+ * 'tracing.<trace-id>' subsection in config-gerrit.txt).
+ */
+@Singleton
+public class TraceRequestListener implements RequestListener {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final Config cfg;
+ private final ImmutableList<TraceConfig> traceConfigs;
+
+ @Inject
+ TraceRequestListener(@GerritServerConfig Config cfg) {
+ this.cfg = cfg;
+ this.traceConfigs = parseTraceConfigs();
+ }
+
+ @Override
+ public void onRequest(RequestInfo requestInfo) {
+ requestInfo.project().ifPresent(p -> requestInfo.traceContext().addTag("project", p));
+ traceConfigs.stream()
+ .filter(traceConfig -> traceConfig.matches(requestInfo))
+ .forEach(
+ traceConfig ->
+ requestInfo
+ .traceContext()
+ .forceLogging()
+ .addTag(RequestId.Type.TRACE_ID, traceConfig.traceId()));
+ }
+
+ private ImmutableList<TraceConfig> parseTraceConfigs() {
+ ImmutableList.Builder<TraceConfig> traceConfigs = ImmutableList.builder();
+
+ for (String traceId : cfg.getSubsections("tracing")) {
+ try {
+ TraceConfig.Builder traceConfig = TraceConfig.builder();
+ traceConfig.traceId(traceId);
+ traceConfig.requestTypes(parseRequestTypes(traceId));
+ traceConfig.requestUriPatterns(parseRequestUriPatterns(traceId));
+ traceConfig.accountIds(parseAccounts(traceId));
+ traceConfig.projectPatterns(parseProjectPatterns(traceId));
+ traceConfigs.add(traceConfig.build());
+ } catch (ConfigInvalidException e) {
+ logger.atWarning().log("Ignoring invalid tracing configuration:\n %s", e.getMessage());
+ }
+ }
+
+ return traceConfigs.build();
+ }
+
+ private ImmutableSet<String> parseRequestTypes(String traceId) {
+ return ImmutableSet.copyOf(cfg.getStringList("tracing", traceId, "requestType"));
+ }
+
+ private ImmutableSet<Pattern> parseRequestUriPatterns(String traceId)
+ throws ConfigInvalidException {
+ return parsePatterns(traceId, "requestUriPattern");
+ }
+
+ private ImmutableSet<Account.Id> parseAccounts(String traceId) throws ConfigInvalidException {
+ ImmutableSet.Builder<Account.Id> accountIds = ImmutableSet.builder();
+ String[] accounts = cfg.getStringList("tracing", traceId, "account");
+ for (String account : accounts) {
+ Optional<Account.Id> accountId = Account.Id.tryParse(account);
+ if (!accountId.isPresent()) {
+ throw new ConfigInvalidException(
+ String.format(
+ "Invalid tracing config ('tracing.%s.account = %s'): invalid account ID",
+ traceId, account));
+ }
+ accountIds.add(accountId.get());
+ }
+ return accountIds.build();
+ }
+
+ private ImmutableSet<Pattern> parseProjectPatterns(String traceId) throws ConfigInvalidException {
+ return parsePatterns(traceId, "projectPattern");
+ }
+
+ private ImmutableSet<Pattern> parsePatterns(String traceId, String name)
+ throws ConfigInvalidException {
+ ImmutableSet.Builder<Pattern> patterns = ImmutableSet.builder();
+ String[] patternRegExs = cfg.getStringList("tracing", traceId, name);
+ for (String patternRegEx : patternRegExs) {
+ try {
+ patterns.add(Pattern.compile(patternRegEx));
+ } catch (PatternSyntaxException e) {
+ throw new ConfigInvalidException(
+ String.format(
+ "Invalid tracing config ('tracing.%s.%s = %s'): %s",
+ traceId, name, patternRegEx, e.getMessage()));
+ }
+ }
+ return patterns.build();
+ }
+
+ @AutoValue
+ abstract static class TraceConfig {
+ /** ID for the trace */
+ abstract String traceId();
+
+ /** request types that should be traced */
+ abstract ImmutableSet<String> requestTypes();
+
+ /** pattern matching request URIs */
+ abstract ImmutableSet<Pattern> requestUriPatterns();
+
+ /** accounts IDs matching calling user */
+ abstract ImmutableSet<Account.Id> accountIds();
+
+ /** pattern matching projects names */
+ abstract ImmutableSet<Pattern> projectPatterns();
+
+ static Builder builder() {
+ return new AutoValue_TraceRequestListener_TraceConfig.Builder();
+ }
+
+ /**
+ * Whether this trace config matches a given request.
+ *
+ * @param requestInfo request info
+ * @return whether this trace config matches
+ */
+ boolean matches(RequestInfo requestInfo) {
+ // If in the trace config request types are set and none of them matches, then the request is
+ // not matched.
+ if (!requestTypes().isEmpty()
+ && requestTypes().stream()
+ .noneMatch(type -> type.equalsIgnoreCase(requestInfo.requestType()))) {
+ return false;
+ }
+
+ // If in the trace config request URI patterns are set and none of them matches, then the
+ // request is not matched.
+ if (!requestUriPatterns().isEmpty()) {
+ if (!requestInfo.requestUri().isPresent()) {
+ // The request has no request URI, hence it cannot match a request URI pattern.
+ return false;
+ }
+
+ if (requestUriPatterns().stream()
+ .noneMatch(p -> p.matcher(requestInfo.requestUri().get()).matches())) {
+ return false;
+ }
+ }
+
+ // If in the trace config accounts are set and none of them matches, then the request is not
+ // matched.
+ if (!accountIds().isEmpty()) {
+ try {
+ if (accountIds().stream()
+ .noneMatch(id -> id.equals(requestInfo.callingUser().getAccountId()))) {
+ return false;
+ }
+ } catch (UnsupportedOperationException e) {
+ // The calling user is not logged in, hence it cannot match an account.
+ return false;
+ }
+ }
+
+ // If in the trace config project patterns are set and none of them matches, then the request
+ // is not matched.
+ if (!projectPatterns().isEmpty()) {
+ if (!requestInfo.project().isPresent()) {
+ // The request is not for a project, hence it cannot match a project pattern.
+ return false;
+ }
+
+ if (projectPatterns().stream()
+ .noneMatch(p -> p.matcher(requestInfo.project().get().get()).matches())) {
+ return false;
+ }
+ }
+
+ // For any match criteria (request type, request URI pattern, account, project pattern) that
+ // was specified in the trace config, at least one of the configured value matched the
+ // request.
+ return true;
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder traceId(String traceId);
+
+ abstract Builder requestTypes(ImmutableSet<String> requestTypes);
+
+ abstract Builder requestUriPatterns(ImmutableSet<Pattern> requestUriPatterns);
+
+ abstract Builder accountIds(ImmutableSet<Account.Id> accountIds);
+
+ abstract Builder projectPatterns(ImmutableSet<Pattern> projectPatterns);
+
+ abstract TraceConfig build();
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/WebLinks.java b/java/com/google/gerrit/server/WebLinks.java
index 589344c314..88b0b215ad 100644
--- a/java/com/google/gerrit/server/WebLinks.java
+++ b/java/com/google/gerrit/server/WebLinks.java
@@ -14,11 +14,14 @@
package com.google.gerrit.server;
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
import com.google.common.base.Strings;
-import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.DiffWebLinkInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -31,12 +34,10 @@ import com.google.gerrit.extensions.webui.PatchSetWebLink;
import com.google.gerrit.extensions.webui.ProjectWebLink;
import com.google.gerrit.extensions.webui.TagWebLink;
import com.google.gerrit.extensions.webui.WebLink;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import java.util.Collections;
-import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
@Singleton
public class WebLinks {
@@ -87,7 +88,7 @@ public class WebLinks {
* @param commit SHA1 of commit.
* @return Links for patch sets.
*/
- public List<WebLinkInfo> getPatchSetLinks(Project.NameKey project, String commit) {
+ public ImmutableList<WebLinkInfo> getPatchSetLinks(Project.NameKey project, String commit) {
return filterLinks(patchSetLinks, webLink -> webLink.getPatchSetWebLink(project.get(), commit));
}
@@ -96,7 +97,7 @@ public class WebLinks {
* @param revision SHA1 of the parent revision.
* @return Links for patch sets.
*/
- public List<WebLinkInfo> getParentLinks(Project.NameKey project, String revision) {
+ public ImmutableList<WebLinkInfo> getParentLinks(Project.NameKey project, String revision) {
return filterLinks(parentLinks, webLink -> webLink.getParentWebLink(project.get(), revision));
}
@@ -106,9 +107,9 @@ public class WebLinks {
* @param file File name.
* @return Links for files.
*/
- public List<WebLinkInfo> getFileLinks(String project, String revision, String file) {
+ public ImmutableList<WebLinkInfo> getFileLinks(String project, String revision, String file) {
return Patch.isMagic(file)
- ? Collections.emptyList()
+ ? ImmutableList.of()
: filterLinks(fileLinks, webLink -> webLink.getFileWebLink(project, revision, file));
}
@@ -118,14 +119,15 @@ public class WebLinks {
* @param file File name.
* @return Links for file history
*/
- public List<WebLinkInfo> getFileHistoryLinks(String project, String revision, String file) {
+ public ImmutableList<WebLinkInfo> getFileHistoryLinks(
+ String project, String revision, String file) {
if (Patch.isMagic(file)) {
- return Collections.emptyList();
+ return ImmutableList.of();
}
- return FluentIterable.from(fileHistoryLinks)
- .transform(webLink -> webLink.getFileHistoryWebLink(project, revision, file))
+ return Streams.stream(fileHistoryLinks)
+ .map(webLink -> webLink.getFileHistoryWebLink(project, revision, file))
.filter(INVALID_WEBLINK)
- .toList();
+ .collect(toImmutableList());
}
/**
@@ -138,20 +140,20 @@ public class WebLinks {
* @param fileB File name of side B.
* @return Links for file diffs.
*/
- public List<DiffWebLinkInfo> getDiffLinks(
- final String project,
- final int changeId,
- final Integer patchSetIdA,
- final String revisionA,
- final String fileA,
- final int patchSetIdB,
- final String revisionB,
- final String fileB) {
+ public ImmutableList<DiffWebLinkInfo> getDiffLinks(
+ String project,
+ int changeId,
+ Integer patchSetIdA,
+ String revisionA,
+ String fileA,
+ int patchSetIdB,
+ String revisionB,
+ String fileB) {
if (Patch.isMagic(fileA) || Patch.isMagic(fileB)) {
- return Collections.emptyList();
+ return ImmutableList.of();
}
- return FluentIterable.from(diffLinks)
- .transform(
+ return Streams.stream(diffLinks)
+ .map(
webLink ->
webLink.getDiffLink(
project,
@@ -163,14 +165,14 @@ public class WebLinks {
revisionB,
fileB))
.filter(INVALID_WEBLINK)
- .toList();
+ .collect(toImmutableList());
}
/**
* @param project Project name.
* @return Links for projects.
*/
- public List<WebLinkInfo> getProjectLinks(String project) {
+ public ImmutableList<WebLinkInfo> getProjectLinks(String project) {
return filterLinks(projectLinks, webLink -> webLink.getProjectWeblink(project));
}
@@ -179,7 +181,7 @@ public class WebLinks {
* @param branch Branch name
* @return Links for branches.
*/
- public List<WebLinkInfo> getBranchLinks(String project, String branch) {
+ public ImmutableList<WebLinkInfo> getBranchLinks(String project, String branch) {
return filterLinks(branchLinks, webLink -> webLink.getBranchWebLink(project, branch));
}
@@ -188,12 +190,15 @@ public class WebLinks {
* @param tag Tag name
* @return Links for tags.
*/
- public List<WebLinkInfo> getTagLinks(String project, String tag) {
+ public ImmutableList<WebLinkInfo> getTagLinks(String project, String tag) {
return filterLinks(tagLinks, webLink -> webLink.getTagWebLink(project, tag));
}
- private <T extends WebLink> List<WebLinkInfo> filterLinks(
+ private <T extends WebLink> ImmutableList<WebLinkInfo> filterLinks(
DynamicSet<T> links, Function<T, WebLinkInfo> transformer) {
- return FluentIterable.from(links).transform(transformer).filter(INVALID_WEBLINK).toList();
+ return Streams.stream(links)
+ .map(transformer)
+ .filter(INVALID_WEBLINK)
+ .collect(toImmutableList());
}
}
diff --git a/java/com/google/gerrit/server/account/AbstractGroupBackend.java b/java/com/google/gerrit/server/account/AbstractGroupBackend.java
index b50b003c5f..93241d6cc5 100644
--- a/java/com/google/gerrit/server/account/AbstractGroupBackend.java
+++ b/java/com/google/gerrit/server/account/AbstractGroupBackend.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
public abstract class AbstractGroupBackend implements GroupBackend {
@Override
diff --git a/java/com/google/gerrit/server/account/AbstractRealm.java b/java/com/google/gerrit/server/account/AbstractRealm.java
index e61736dca1..380001d833 100644
--- a/java/com/google/gerrit/server/account/AbstractRealm.java
+++ b/java/com/google/gerrit/server/account/AbstractRealm.java
@@ -53,7 +53,7 @@ public abstract class AbstractRealm implements Realm {
@Override
public boolean hasEmailAddress(IdentifiedUser user, String email) {
- for (ExternalId ext : user.state().getExternalIds()) {
+ for (ExternalId ext : user.state().externalIds()) {
if (email != null && email.equalsIgnoreCase(ext.email())) {
return true;
}
@@ -63,7 +63,7 @@ public abstract class AbstractRealm implements Realm {
@Override
public Set<String> getEmailAddresses(IdentifiedUser user) {
- Collection<ExternalId> ids = user.state().getExternalIds();
+ Collection<ExternalId> ids = user.state().externalIds();
Set<String> emails = Sets.newHashSetWithExpectedSize(ids.size());
for (ExternalId ext : ids) {
if (!Strings.isNullOrEmpty(ext.email())) {
diff --git a/java/com/google/gerrit/server/account/AccountCache.java b/java/com/google/gerrit/server/account/AccountCache.java
index 17493bf842..47cf25ba41 100644
--- a/java/com/google/gerrit/server/account/AccountCache.java
+++ b/java/com/google/gerrit/server/account/AccountCache.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.account;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
diff --git a/java/com/google/gerrit/server/account/AccountCacheImpl.java b/java/com/google/gerrit/server/account/AccountCacheImpl.java
index acd7bd358a..ef4e1c080c 100644
--- a/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -21,12 +21,12 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -59,7 +59,7 @@ public class AccountCacheImpl implements AccountCache {
return new CacheModule() {
@Override
protected void configure() {
- cache(BYID_NAME, Account.Id.class, new TypeLiteral<Optional<AccountState>>() {})
+ cache(BYID_NAME, Account.Id.class, new TypeLiteral<AccountState>() {})
.loader(ByIdLoader.class);
bind(AccountCacheImpl.class);
@@ -68,18 +68,15 @@ public class AccountCacheImpl implements AccountCache {
};
}
- private final AllUsersName allUsersName;
private final ExternalIds externalIds;
- private final LoadingCache<Account.Id, Optional<AccountState>> byId;
+ private final LoadingCache<Account.Id, AccountState> byId;
private final ExecutorService executor;
@Inject
AccountCacheImpl(
- AllUsersName allUsersName,
ExternalIds externalIds,
- @Named(BYID_NAME) LoadingCache<Account.Id, Optional<AccountState>> byId,
+ @Named(BYID_NAME) LoadingCache<Account.Id, AccountState> byId,
@FanOutExecutor ExecutorService executor) {
- this.allUsersName = allUsersName;
this.externalIds = externalIds;
this.byId = byId;
this.executor = executor;
@@ -88,9 +85,11 @@ public class AccountCacheImpl implements AccountCache {
@Override
public AccountState getEvenIfMissing(Account.Id accountId) {
try {
- return byId.get(accountId).orElse(missing(accountId));
+ return byId.get(accountId);
} catch (ExecutionException e) {
- logger.atWarning().withCause(e).log("Cannot load AccountState for %s", accountId);
+ if (!(e.getCause() instanceof AccountNotFoundException)) {
+ logger.atWarning().withCause(e).log("Cannot load AccountState for %s", accountId);
+ }
return missing(accountId);
}
}
@@ -98,9 +97,11 @@ public class AccountCacheImpl implements AccountCache {
@Override
public Optional<AccountState> get(Account.Id accountId) {
try {
- return byId.get(accountId);
+ return Optional.ofNullable(byId.get(accountId));
} catch (ExecutionException e) {
- logger.atWarning().withCause(e).log("Cannot load AccountState for ID %s", accountId);
+ if (!(e.getCause() instanceof AccountNotFoundException)) {
+ logger.atWarning().withCause(e).log("Cannot load AccountState for %s", accountId);
+ }
return Optional.empty();
}
}
@@ -110,10 +111,10 @@ public class AccountCacheImpl implements AccountCache {
Map<Account.Id, AccountState> accountStates = new HashMap<>(accountIds.size());
List<Callable<Optional<AccountState>>> callables = new ArrayList<>();
for (Account.Id accountId : accountIds) {
- Optional<AccountState> state = byId.getIfPresent(accountId);
+ AccountState state = byId.getIfPresent(accountId);
if (state != null) {
// The value is in-memory, so we just get the state
- state.ifPresent(s -> accountStates.put(accountId, s));
+ accountStates.put(accountId, state);
} else {
// Queue up a callable so that we can load accounts in parallel
callables.add(() -> get(accountId));
@@ -132,7 +133,7 @@ public class AccountCacheImpl implements AccountCache {
}
for (Future<Optional<AccountState>> f : futures) {
try {
- f.get().ifPresent(s -> accountStates.put(s.getAccount().getId(), s));
+ f.get().ifPresent(s -> accountStates.put(s.account().id(), s));
} catch (InterruptedException | ExecutionException e) {
logger.atSevere().withCause(e).log("Cannot load AccountState");
}
@@ -168,12 +169,12 @@ public class AccountCacheImpl implements AccountCache {
}
private AccountState missing(Account.Id accountId) {
- Account account = new Account(accountId, TimeUtil.nowTs());
+ Account.Builder account = Account.builder(accountId, TimeUtil.nowTs());
account.setActive(false);
- return AccountState.forAccount(allUsersName, account);
+ return AccountState.forAccount(account.build());
}
- static class ByIdLoader extends CacheLoader<Account.Id, Optional<AccountState>> {
+ static class ByIdLoader extends CacheLoader<Account.Id, AccountState> {
private final Accounts accounts;
@Inject
@@ -182,10 +183,23 @@ public class AccountCacheImpl implements AccountCache {
}
@Override
- public Optional<AccountState> load(Account.Id who) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading account %s", who)) {
- return accounts.get(who);
+ public AccountState load(Account.Id who) throws Exception {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading account", Metadata.builder().accountId(who.get()).build())) {
+ return accounts
+ .get(who)
+ .orElseThrow(() -> new AccountNotFoundException(who + " not found"));
}
}
}
+
+ /** Signals that the account was not found in the primary storage. */
+ private static class AccountNotFoundException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public AccountNotFoundException(String message) {
+ super(message);
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/account/AccountConfig.java b/java/com/google/gerrit/server/account/AccountConfig.java
index 06f7a08002..5a1bb8a7db 100644
--- a/java/com/google/gerrit/server/account/AccountConfig.java
+++ b/java/com/google/gerrit/server/account/AccountConfig.java
@@ -21,12 +21,12 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
import com.google.gerrit.server.account.externalids.ExternalIds;
@@ -68,7 +68,7 @@ import org.eclipse.jgit.revwalk.RevSort;
* <li>'account.config': Contains the account properties. Parsing and writing it is delegated to
* {@link AccountProperties}.
* <li>'preferences.config': Contains the preferences. Parsing and writing it is delegated to
- * {@link Preferences}.
+ * {@link StoredPreferences}.
* <li>'account.config': Contains the project watches. Parsing and writing it is delegated to
* {@link ProjectWatches}.
* </ul>
@@ -85,7 +85,7 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
private Optional<AccountProperties> loadedAccountProperties;
private Optional<ObjectId> externalIdsRev;
private ProjectWatches projectWatches;
- private Preferences preferences;
+ private StoredPreferences preferences;
private Optional<InternalAccountUpdate> accountUpdate = Optional.empty();
private List<ValidationError> validationErrors;
@@ -122,7 +122,7 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
* Returns the revision of the {@code refs/meta/external-ids} branch.
*
* <p>This revision can be used to load the external IDs of the loaded account lazily via {@link
- * ExternalIds#byAccount(com.google.gerrit.reviewdb.client.Account.Id, ObjectId)}.
+ * ExternalIds#byAccount(com.google.gerrit.entities.Account.Id, ObjectId)}.
*
* @return revision of the {@code refs/meta/external-ids} branch, {@link Optional#empty()} if no
* {@code refs/meta/external-ids} branch exists
@@ -184,14 +184,14 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
checkLoaded();
this.loadedAccountProperties =
Optional.of(
- new AccountProperties(account.getId(), account.getRegisteredOn(), new Config(), null));
+ new AccountProperties(account.id(), account.registeredOn(), new Config(), null));
this.accountUpdate =
Optional.of(
InternalAccountUpdate.builder()
.setActive(account.isActive())
- .setFullName(account.getFullName())
- .setPreferredEmail(account.getPreferredEmail())
- .setStatus(account.getStatus())
+ .setFullName(account.fullName())
+ .setPreferredEmail(account.preferredEmail())
+ .setStatus(account.status())
.build());
return this;
}
@@ -242,10 +242,10 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
projectWatches = new ProjectWatches(accountId, readConfig(ProjectWatches.WATCH_CONFIG), this);
preferences =
- new Preferences(
+ new StoredPreferences(
accountId,
- readConfig(Preferences.PREFERENCES_CONFIG),
- Preferences.readDefaultConfig(allUsersName, repo),
+ readConfig(StoredPreferences.PREFERENCES_CONFIG),
+ StoredPreferences.readDefaultConfig(allUsersName, repo),
this);
projectWatches.parse();
@@ -256,8 +256,11 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
projectWatches = new ProjectWatches(accountId, new Config(), this);
preferences =
- new Preferences(
- accountId, new Config(), Preferences.readDefaultConfig(allUsersName, repo), this);
+ new StoredPreferences(
+ accountId,
+ new Config(),
+ StoredPreferences.readDefaultConfig(allUsersName, repo),
+ this);
}
Ref externalIdsRef = repo.exactRef(RefNames.REFS_EXTERNAL_IDS);
@@ -331,7 +334,7 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
}
saveConfig(
- Preferences.PREFERENCES_CONFIG,
+ StoredPreferences.PREFERENCES_CONFIG,
preferences.saveGeneralPreferences(
accountUpdate.get().getGeneralPreferences(),
accountUpdate.get().getDiffPreferences(),
diff --git a/java/com/google/gerrit/server/account/AccountControl.java b/java/com/google/gerrit/server/account/AccountControl.java
index 4b8be810f0..f8a5c5c8a0 100644
--- a/java/com/google/gerrit/server/account/AccountControl.java
+++ b/java/com/google/gerrit/server/account/AccountControl.java
@@ -17,11 +17,11 @@ package com.google.gerrit.server.account;
import static java.util.stream.Collectors.toSet;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.AccountVisibility;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.group.SystemGroupBackend;
@@ -133,7 +133,7 @@ public class AccountControl {
new OtherUser() {
@Override
Account.Id getId() {
- return otherUser.getAccount().getId();
+ return otherUser.account().id();
}
@Override
diff --git a/java/com/google/gerrit/server/account/AccountDeactivator.java b/java/com/google/gerrit/server/account/AccountDeactivator.java
index ac02322f46..b12e585131 100644
--- a/java/com/google/gerrit/server/account/AccountDeactivator.java
+++ b/java/com/google/gerrit/server/account/AccountDeactivator.java
@@ -104,15 +104,15 @@ public class AccountDeactivator implements Runnable {
}
private boolean processAccount(AccountState accountState) {
- if (!accountState.getUserName().isPresent()) {
+ if (!accountState.userName().isPresent()) {
return false;
}
- String userName = accountState.getUserName().get();
+ String userName = accountState.userName().get();
logger.atFine().log("processing account %s", userName);
try {
- if (realm.accountBelongsToRealm(accountState.getExternalIds()) && !realm.isActive(userName)) {
- sif.deactivate(accountState.getAccount().getId());
+ if (realm.accountBelongsToRealm(accountState.externalIds()) && !realm.isActive(userName)) {
+ sif.deactivate(accountState.account().id());
logger.atInfo().log("deactivated account %s", userName);
return true;
}
@@ -121,7 +121,7 @@ public class AccountDeactivator implements Runnable {
} catch (Exception e) {
logger.atSevere().withCause(e).log(
"Error deactivating account: %s (%s) %s",
- userName, accountState.getAccount().getId(), e.getMessage());
+ userName, accountState.account().id(), e.getMessage());
}
return false;
}
diff --git a/java/com/google/gerrit/server/account/AccountDirectory.java b/java/com/google/gerrit/server/account/AccountDirectory.java
index ee9265f26a..60c1678a8f 100644
--- a/java/com/google/gerrit/server/account/AccountDirectory.java
+++ b/java/com/google/gerrit/server/account/AccountDirectory.java
@@ -45,7 +45,10 @@ public abstract class AccountDirectory {
ID,
/** The user-settable status of this account (e.g. busy, OOO, available) */
- STATUS
+ STATUS,
+
+ /** The state of the account (e.g. active or inactive) */
+ STATE
}
public abstract void fillAccountInfo(Iterable<? extends AccountInfo> in, Set<FillOptions> options)
diff --git a/java/com/google/gerrit/server/account/AccountExternalIdCreator.java b/java/com/google/gerrit/server/account/AccountExternalIdCreator.java
index 8cf4ee06f4..dedd916e67 100644
--- a/java/com/google/gerrit/server/account/AccountExternalIdCreator.java
+++ b/java/com/google/gerrit/server/account/AccountExternalIdCreator.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
import java.util.List;
diff --git a/java/com/google/gerrit/server/account/AccountLoader.java b/java/com/google/gerrit/server/account/AccountLoader.java
index 4398d9ed7f..a8e419497b 100644
--- a/java/com/google/gerrit/server/account/AccountLoader.java
+++ b/java/com/google/gerrit/server/account/AccountLoader.java
@@ -17,8 +17,9 @@ package com.google.gerrit.server.account;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.Iterables;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountDirectory.FillOptions;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.assistedinject.Assisted;
@@ -41,6 +42,7 @@ public class AccountLoader {
FillOptions.EMAIL,
FillOptions.USERNAME,
FillOptions.STATUS,
+ FillOptions.STATE,
FillOptions.AVATARS));
public interface Factory {
@@ -67,7 +69,8 @@ public class AccountLoader {
provided = new ArrayList<>();
}
- public synchronized AccountInfo get(Account.Id id) {
+ @Nullable
+ public synchronized AccountInfo get(@Nullable Account.Id id) {
if (id == null) {
return null;
}
@@ -95,7 +98,8 @@ public class AccountLoader {
fill();
}
- public AccountInfo fillOne(Account.Id id) throws PermissionBackendException {
+ @Nullable
+ public AccountInfo fillOne(@Nullable Account.Id id) throws PermissionBackendException {
AccountInfo info = get(id);
fill();
return info;
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index c52b8bc43f..7a5b1aae5d 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.account;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
import com.google.common.annotations.VisibleForTesting;
@@ -26,11 +27,11 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate.AccountUpdater;
@@ -154,7 +155,7 @@ public class AccountManager {
}
// Account exists
- Optional<Account> act = updateAccountActiveStatus(who, accountState.get().getAccount());
+ Optional<Account> act = updateAccountActiveStatus(who, accountState.get().account());
if (!act.isPresent()) {
// The account was deleted since we checked for it last time. This should never happen
// since we don't support deletion of accounts.
@@ -199,18 +200,18 @@ public class AccountManager {
if (authRequest.isActive()) {
try {
- setInactiveFlag.activate(account.getId());
+ setInactiveFlag.activate(account.id());
} catch (Exception e) {
- throw new AccountException("Unable to activate account " + account.getId(), e);
+ throw new AccountException("Unable to activate account " + account.id(), e);
}
} else {
try {
- setInactiveFlag.deactivate(account.getId());
+ setInactiveFlag.deactivate(account.id());
} catch (Exception e) {
- throw new AccountException("Unable to deactivate account " + account.getId(), e);
+ throw new AccountException("Unable to deactivate account " + account.id(), e);
}
}
- return byIdCache.get(account.getId()).map(AccountState::getAccount);
+ return byIdCache.get(account.id()).map(AccountState::account);
}
private boolean shouldUpdateActiveStatus(AuthRequest authRequest) {
@@ -233,13 +234,13 @@ public class AccountManager {
checkEmailNotUsed(extId.accountId(), extIdWithNewEmail);
accountUpdates.add(u -> u.replaceExternalId(extId, extIdWithNewEmail));
- if (oldEmail != null && oldEmail.equals(user.getAccount().getPreferredEmail())) {
+ if (oldEmail != null && oldEmail.equals(user.getAccount().preferredEmail())) {
accountUpdates.add(u -> u.setPreferredEmail(newEmail));
}
}
if (!Strings.isNullOrEmpty(who.getDisplayName())
- && !Objects.equals(user.getAccount().getFullName(), who.getDisplayName())) {
+ && !Objects.equals(user.getAccount().fullName(), who.getDisplayName())) {
accountUpdates.add(a -> a.setFullName(who.getDisplayName()));
}
@@ -269,7 +270,7 @@ public class AccountManager {
private AuthResult create(AuthRequest who)
throws AccountException, IOException, ConfigInvalidException {
- Account.Id newId = new Account.Id(sequences.nextAccountId());
+ Account.Id newId = Account.id(sequences.nextAccountId());
logger.atFine().log("Assigning new Id %s to account", newId);
ExternalId extId =
@@ -333,7 +334,7 @@ public class AccountManager {
addGroupMember(adminGroupUuid, user);
}
- realm.onCreateAccount(who, accountState.getAccount());
+ realm.onCreateAccount(who, accountState.account());
return new AuthResult(newId, extId.key(), true);
}
@@ -422,7 +423,7 @@ public class AccountManager {
to,
(a, u) -> {
u.addExternalId(newExtId);
- if (who.getEmailAddress() != null && a.getAccount().getPreferredEmail() == null) {
+ if (who.getEmailAddress() != null && a.account().preferredEmail() == null) {
u.setPreferredEmail(who.getEmailAddress());
}
});
@@ -450,8 +451,10 @@ public class AccountManager {
"Delete External IDs on Update Link",
to,
(a, u) -> {
- Collection<ExternalId> filteredExtIdsByScheme =
- a.getExternalIds(who.getExternalIdKey().scheme());
+ Set<ExternalId> filteredExtIdsByScheme =
+ a.externalIds().stream()
+ .filter(e -> e.key().isScheme(who.getExternalIdKey().scheme()))
+ .collect(toImmutableSet());
if (filteredExtIdsByScheme.isEmpty()) {
return;
}
@@ -513,9 +516,9 @@ public class AccountManager {
from,
(a, u) -> {
u.deleteExternalIds(extIds);
- if (a.getAccount().getPreferredEmail() != null
+ if (a.account().preferredEmail() != null
&& extIds.stream()
- .anyMatch(e -> a.getAccount().getPreferredEmail().equals(e.email()))) {
+ .anyMatch(e -> a.account().preferredEmail().equals(e.email()))) {
u.setPreferredEmail(null);
}
});
diff --git a/java/com/google/gerrit/server/account/AccountProperties.java b/java/com/google/gerrit/server/account/AccountProperties.java
index 6fcf56dce8..4f29b2512e 100644
--- a/java/com/google/gerrit/server/account/AccountProperties.java
+++ b/java/com/google/gerrit/server/account/AccountProperties.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.account;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.sql.Timestamp;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
@@ -88,15 +88,16 @@ public class AccountProperties {
}
private void parse() {
- account = new Account(accountId, registeredOn);
- account.setActive(accountConfig.getBoolean(ACCOUNT, null, KEY_ACTIVE, true));
- account.setFullName(get(accountConfig, KEY_FULL_NAME));
+ Account.Builder accountBuilder = Account.builder(accountId, registeredOn);
+ accountBuilder.setActive(accountConfig.getBoolean(ACCOUNT, null, KEY_ACTIVE, true));
+ accountBuilder.setFullName(get(accountConfig, KEY_FULL_NAME));
String preferredEmail = get(accountConfig, KEY_PREFERRED_EMAIL);
- account.setPreferredEmail(preferredEmail);
+ accountBuilder.setPreferredEmail(preferredEmail);
- account.setStatus(get(accountConfig, KEY_STATUS));
- account.setMetaId(metaId != null ? metaId.name() : null);
+ accountBuilder.setStatus(get(accountConfig, KEY_STATUS));
+ accountBuilder.setMetaId(metaId != null ? metaId.name() : null);
+ account = accountBuilder.build();
}
Config save(InternalAccountUpdate accountUpdate) {
diff --git a/java/com/google/gerrit/server/account/AccountResolver.java b/java/com/google/gerrit/server/account/AccountResolver.java
index f9945b5046..988d8713fa 100644
--- a/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/java/com/google/gerrit/server/account/AccountResolver.java
@@ -26,9 +26,9 @@ import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.index.Schema;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.externalids.ExternalId;
@@ -113,9 +113,9 @@ public class AccountResolver {
}
private static String formatForException(Result result, AccountState state) {
- return state.getAccount().getId()
+ return state.account().id()
+ ": "
- + state.getAccount().getNameEmail(result.accountResolver().anonymousCowardName);
+ + state.account().getNameEmail(result.accountResolver().anonymousCowardName);
}
public static boolean isSelf(String input) {
@@ -135,7 +135,7 @@ public class AccountResolver {
}
private ImmutableList<AccountState> canonicalize(List<AccountState> list) {
- TreeSet<AccountState> set = new TreeSet<>(comparing(a -> a.getAccount().getId().get()));
+ TreeSet<AccountState> set = new TreeSet<>(comparing(a -> a.account().id().get()));
set.addAll(requireNonNull(list));
return ImmutableList.copyOf(set);
}
@@ -160,7 +160,7 @@ public class AccountResolver {
}
public ImmutableSet<Account.Id> asIdSet() {
- return list.stream().map(a -> a.getAccount().getId()).collect(toImmutableSet());
+ return list.stream().map(a -> a.account().id()).collect(toImmutableSet());
}
public AccountState asUnique() throws UnresolvableAccountException {
@@ -192,7 +192,7 @@ public class AccountResolver {
return self.get().asIdentifiedUser();
}
return userFactory.runAs(
- null, list.get(0).getAccount().getId(), requireNonNull(caller).getRealUser());
+ null, list.get(0).account().id(), requireNonNull(caller).getRealUser());
}
@VisibleForTesting
@@ -349,7 +349,7 @@ public class AccountResolver {
String name = nameOrEmail.substring(0, lt - 1);
ImmutableList<AccountState> nameMatches =
allMatches.stream()
- .filter(a -> name.equals(a.getAccount().getFullName()))
+ .filter(a -> name.equals(a.account().fullName()))
.collect(toImmutableList());
return !nameMatches.isEmpty() ? nameMatches.stream() : allMatches.stream();
}
@@ -558,7 +558,7 @@ public class AccountResolver {
}
private Predicate<AccountState> accountActivityPredicate() {
- return (AccountState accountState) -> accountState.getAccount().isActive();
+ return (AccountState accountState) -> accountState.account().isActive();
}
@VisibleForTesting
diff --git a/java/com/google/gerrit/server/account/AccountResource.java b/java/com/google/gerrit/server/account/AccountResource.java
index d09dff51d8..4fb69bd989 100644
--- a/java/com/google/gerrit/server/account/AccountResource.java
+++ b/java/com/google/gerrit/server/account/AccountResource.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.ChangeResource;
import com.google.inject.TypeLiteral;
diff --git a/java/com/google/gerrit/server/account/AccountSshKey.java b/java/com/google/gerrit/server/account/AccountSshKey.java
index f13258572e..d2f8775904 100644
--- a/java/com/google/gerrit/server/account/AccountSshKey.java
+++ b/java/com/google/gerrit/server/account/AccountSshKey.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.account;
import com.google.auto.value.AutoValue;
import com.google.common.base.Splitter;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.util.List;
/** An SSH key approved for use by an {@link Account}. */
diff --git a/java/com/google/gerrit/server/account/AccountState.java b/java/com/google/gerrit/server/account/AccountState.java
index 1854dc10e5..a270a76d16 100644
--- a/java/com/google/gerrit/server/account/AccountState.java
+++ b/java/com/google/gerrit/server/account/AccountState.java
@@ -14,34 +14,23 @@
package com.google.gerrit.server.account;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
-
-import com.google.common.base.Function;
+import com.google.auto.value.AutoValue;
import com.google.common.base.MoreObjects;
-import com.google.common.base.Strings;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.CurrentUser.PropertyKey;
-import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIds;
-import com.google.gerrit.server.config.AllUsersName;
import java.io.IOException;
import java.util.Collection;
import java.util.Optional;
-import org.apache.commons.codec.DecoderException;
import org.eclipse.jgit.lib.ObjectId;
/**
@@ -51,25 +40,19 @@ import org.eclipse.jgit.lib.ObjectId;
* <p>Most callers should not construct AccountStates directly but rather lookup accounts via the
* account cache (see {@link AccountCache#get(Account.Id)}).
*/
-public class AccountState {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- public static final Function<AccountState, Account.Id> ACCOUNT_ID_FUNCTION =
- a -> a.getAccount().getId();
-
+@AutoValue
+public abstract class AccountState {
/**
* Creates an AccountState from the given account config.
*
- * @param allUsersName the name of the All-Users repository
* @param externalIds class to access external IDs
* @param accountConfig the account config, must already be loaded
* @return the account state, {@link Optional#empty()} if the account doesn't exist
* @throws IOException if accessing the external IDs fails
*/
public static Optional<AccountState> fromAccountConfig(
- AllUsersName allUsersName, ExternalIds externalIds, AccountConfig accountConfig)
- throws IOException {
- return fromAccountConfig(allUsersName, externalIds, accountConfig, null);
+ ExternalIds externalIds, AccountConfig accountConfig) throws IOException {
+ return fromAccountConfig(externalIds, accountConfig, null);
}
/**
@@ -82,7 +65,6 @@ public class AccountState {
* updated the revision of the external IDs branch in account config is outdated. Hence after
* updating external IDs the external ID notes must be provided.
*
- * @param allUsersName the name of the All-Users repository
* @param externalIds class to access external IDs
* @param accountConfig the account config, must already be loaded
* @param extIdNotes external ID notes, must already be loaded, may be {@code null}
@@ -90,10 +72,7 @@ public class AccountState {
* @throws IOException if accessing the external IDs fails
*/
public static Optional<AccountState> fromAccountConfig(
- AllUsersName allUsersName,
- ExternalIds externalIds,
- AccountConfig accountConfig,
- @Nullable ExternalIdNotes extIdNotes)
+ ExternalIds externalIds, AccountConfig accountConfig, @Nullable ExternalIdNotes extIdNotes)
throws IOException {
if (!accountConfig.getLoadedAccount().isPresent()) {
return Optional.empty();
@@ -106,22 +85,25 @@ public class AccountState {
: accountConfig.getExternalIdsRev();
ImmutableSet<ExternalId> extIds =
extIdsRev.isPresent()
- ? ImmutableSet.copyOf(externalIds.byAccount(account.getId(), extIdsRev.get()))
+ ? ImmutableSet.copyOf(externalIds.byAccount(account.id(), extIdsRev.get()))
: ImmutableSet.of();
// Don't leak references to AccountConfig into the AccountState, since it holds a reference to
// an open Repository instance.
ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches =
accountConfig.getProjectWatches();
- GeneralPreferencesInfo generalPreferences = accountConfig.getGeneralPreferences();
- DiffPreferencesInfo diffPreferences = accountConfig.getDiffPreferences();
- EditPreferencesInfo editPreferences = accountConfig.getEditPreferences();
+ Preferences.General generalPreferences =
+ Preferences.General.fromInfo(accountConfig.getGeneralPreferences());
+ Preferences.Diff diffPreferences =
+ Preferences.Diff.fromInfo(accountConfig.getDiffPreferences());
+ Preferences.Edit editPreferences =
+ Preferences.Edit.fromInfo(accountConfig.getEditPreferences());
return Optional.of(
- new AccountState(
- allUsersName,
+ new AutoValue_AccountState(
account,
extIds,
+ ExternalId.getUserName(extIds),
projectWatches,
generalPreferences,
diffPreferences,
@@ -132,71 +114,35 @@ public class AccountState {
* Creates an AccountState for a given account with no external IDs, no project watches and
* default preferences.
*
- * @param allUsersName the name of the All-Users repository
* @param account the account
* @return the account state
*/
- public static AccountState forAccount(AllUsersName allUsersName, Account account) {
- return forAccount(allUsersName, account, ImmutableSet.of());
+ public static AccountState forAccount(Account account) {
+ return forAccount(account, ImmutableSet.of());
}
/**
* Creates an AccountState for a given account with no project watches and default preferences.
*
- * @param allUsersName the name of the All-Users repository
* @param account the account
* @param extIds the external IDs
* @return the account state
*/
- public static AccountState forAccount(
- AllUsersName allUsersName, Account account, Collection<ExternalId> extIds) {
- return new AccountState(
- allUsersName,
+ public static AccountState forAccount(Account account, Collection<ExternalId> extIds) {
+ return new AutoValue_AccountState(
account,
ImmutableSet.copyOf(extIds),
+ ExternalId.getUserName(extIds),
ImmutableMap.of(),
- GeneralPreferencesInfo.defaults(),
- DiffPreferencesInfo.defaults(),
- EditPreferencesInfo.defaults());
- }
-
- private final AllUsersName allUsersName;
- private final Account account;
- private final ImmutableSet<ExternalId> externalIds;
- private final Optional<String> userName;
- private final ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches;
- private final GeneralPreferencesInfo generalPreferences;
- private final DiffPreferencesInfo diffPreferences;
- private final EditPreferencesInfo editPreferences;
- private Cache<IdentifiedUser.PropertyKey<Object>, Object> properties;
-
- private AccountState(
- AllUsersName allUsersName,
- Account account,
- ImmutableSet<ExternalId> externalIds,
- ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches,
- GeneralPreferencesInfo generalPreferences,
- DiffPreferencesInfo diffPreferences,
- EditPreferencesInfo editPreferences) {
- this.allUsersName = allUsersName;
- this.account = account;
- this.externalIds = externalIds;
- this.userName = ExternalId.getUserName(externalIds);
- this.projectWatches = projectWatches;
- this.generalPreferences = generalPreferences;
- this.diffPreferences = diffPreferences;
- this.editPreferences = editPreferences;
- }
-
- public AllUsersName getAllUsersNameForIndexing() {
- return allUsersName;
+ Preferences.General.fromInfo(GeneralPreferencesInfo.defaults()),
+ Preferences.Diff.fromInfo(DiffPreferencesInfo.defaults()),
+ Preferences.Edit.fromInfo(EditPreferencesInfo.defaults()));
}
/** Get the cached account metadata. */
- public Account getAccount() {
- return account;
- }
-
+ public abstract Account account();
+ /** The external identities that identify the account holder. */
+ public abstract ImmutableSet<ExternalId> externalIds();
/**
* Get the username, if one has been declared for this user.
*
@@ -205,122 +151,36 @@ public class AccountState {
* @return the username, {@link Optional#empty()} if the user has no username, or if the username
* is empty
*/
- public Optional<String> getUserName() {
- return userName;
- }
-
- public boolean checkPassword(@Nullable String password, String username) {
- if (password == null) {
- return false;
- }
- for (ExternalId id : getExternalIds()) {
- // Only process the "username:$USER" entry, which is unique.
- if (!id.isScheme(SCHEME_USERNAME) || !username.equals(id.key().id())) {
- continue;
- }
-
- String hashedStr = id.password();
- if (!Strings.isNullOrEmpty(hashedStr)) {
- try {
- return HashedPassword.decode(hashedStr).checkPassword(password);
- } catch (DecoderException e) {
- logger.atSevere().log("DecoderException for user %s: %s ", username, e.getMessage());
- return false;
- }
- }
- }
- return false;
- }
-
- /** The external identities that identify the account holder. */
- public ImmutableSet<ExternalId> getExternalIds() {
- return externalIds;
- }
-
- /** The external identities that identify the account holder that match the given scheme. */
- public ImmutableSet<ExternalId> getExternalIds(String scheme) {
- return externalIds.stream().filter(e -> e.key().isScheme(scheme)).collect(toImmutableSet());
- }
-
+ public abstract Optional<String> userName();
/** The project watches of the account. */
- public ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> getProjectWatches() {
- return projectWatches;
- }
+ public abstract ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches();
+ /** The general preferences of the account. */
/** The general preferences of the account. */
- public GeneralPreferencesInfo getGeneralPreferences() {
- return generalPreferences;
+ public GeneralPreferencesInfo generalPreferences() {
+ return immutableGeneralPreferences().toInfo();
}
/** The diff preferences of the account. */
- public DiffPreferencesInfo getDiffPreferences() {
- return diffPreferences;
+ public DiffPreferencesInfo diffPreferences() {
+ return immutableDiffPreferences().toInfo();
}
/** The edit preferences of the account. */
- public EditPreferencesInfo getEditPreferences() {
- return editPreferences;
- }
-
- /**
- * Lookup a previously stored property.
- *
- * <p>All properties are automatically cleared when the account cache invalidates the {@code
- * AccountState}. This method is thread-safe.
- *
- * @param key unique property key.
- * @return previously stored value, or {@code null}.
- */
- @Nullable
- public <T> T get(PropertyKey<T> key) {
- Cache<PropertyKey<Object>, Object> p = properties(false);
- if (p != null) {
- @SuppressWarnings("unchecked")
- T value = (T) p.getIfPresent(key);
- return value;
- }
- return null;
- }
-
- /**
- * Store a property for later retrieval.
- *
- * <p>This method is thread-safe.
- *
- * @param key unique property key.
- * @param value value to store; or {@code null} to clear the value.
- */
- public <T> void put(PropertyKey<T> key, @Nullable T value) {
- Cache<PropertyKey<Object>, Object> p = properties(value != null);
- if (p != null) {
- @SuppressWarnings("unchecked")
- PropertyKey<Object> k = (PropertyKey<Object>) key;
- if (value != null) {
- p.put(k, value);
- } else {
- p.invalidate(k);
- }
- }
- }
-
- private synchronized Cache<PropertyKey<Object>, Object> properties(boolean allocate) {
- if (properties == null && allocate) {
- properties =
- CacheBuilder.newBuilder()
- .concurrencyLevel(1)
- .initialCapacity(16)
- // Use weakKeys to ensure plugins that garbage collect will also
- // eventually release data held in any still live AccountState.
- .weakKeys()
- .build();
- }
- return properties;
+ public EditPreferencesInfo editPreferences() {
+ return immutableEditPreferences().toInfo();
}
@Override
- public String toString() {
+ public final String toString() {
MoreObjects.ToStringHelper h = MoreObjects.toStringHelper(this);
- h.addValue(getAccount().getId());
+ h.addValue(account().id());
return h.toString();
}
+
+ protected abstract Preferences.General immutableGeneralPreferences();
+
+ protected abstract Preferences.Diff immutableDiffPreferences();
+
+ protected abstract Preferences.Edit immutableEditPreferences();
}
diff --git a/java/com/google/gerrit/server/account/Accounts.java b/java/com/google/gerrit/server/account/Accounts.java
index f3758bf2c4..81366318c7 100644
--- a/java/com/google/gerrit/server/account/Accounts.java
+++ b/java/com/google/gerrit/server/account/Accounts.java
@@ -19,8 +19,8 @@ import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -134,9 +134,7 @@ public class Accounts {
private Optional<AccountState> read(Repository allUsersRepository, Account.Id accountId)
throws IOException, ConfigInvalidException {
return AccountState.fromAccountConfig(
- allUsersName,
- externalIds,
- new AccountConfig(accountId, allUsersName, allUsersRepository).load());
+ externalIds, new AccountConfig(accountId, allUsersName, allUsersRepository).load());
}
public static Stream<Account.Id> readUserRefs(Repository repo) throws IOException {
diff --git a/java/com/google/gerrit/server/account/AccountsConsistencyChecker.java b/java/com/google/gerrit/server/account/AccountsConsistencyChecker.java
index b43d86c731..db350c6fe4 100644
--- a/java/com/google/gerrit/server/account/AccountsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/account/AccountsConsistencyChecker.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -35,14 +35,14 @@ public class AccountsConsistencyChecker {
List<ConsistencyProblemInfo> problems = new ArrayList<>();
for (AccountState accountState : accounts.all()) {
- Account account = accountState.getAccount();
- if (account.getPreferredEmail() != null) {
- if (accountState.getExternalIds().stream()
- .noneMatch(e -> account.getPreferredEmail().equals(e.email()))) {
+ Account account = accountState.account();
+ if (account.preferredEmail() != null) {
+ if (accountState.externalIds().stream()
+ .noneMatch(e -> account.preferredEmail().equals(e.email()))) {
addError(
String.format(
"Account '%s' has no external ID for its preferred email '%s'",
- account.getId().get(), account.getPreferredEmail()),
+ account.id().get(), account.preferredEmail()),
problems);
}
}
diff --git a/java/com/google/gerrit/server/account/AccountsUpdate.java b/java/com/google/gerrit/server/account/AccountsUpdate.java
index 20a1c978f1..1caee58937 100644
--- a/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -24,11 +24,11 @@ import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Runnables;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
@@ -88,9 +88,9 @@ import org.eclipse.jgit.lib.Repository;
* The timestamp of the first commit on a user branch denotes the registration date. The initial
* commit on the user branch may be empty (since having an 'account.config' is optional). See {@link
* AccountConfig} for details of the 'account.config' file format. In addition the user branch can
- * contain a 'preferences.config' config file to store preferences (see {@link Preferences}) and a
- * 'watch.config' config file to store project watches (see {@link ProjectWatches}). External IDs
- * are stored separately in the {@code refs/meta/external-ids} notes branch (see {@link
+ * contain a 'preferences.config' config file to store preferences (see {@link StoredPreferences})
+ * and a 'watch.config' config file to store project watches (see {@link ProjectWatches}). External
+ * IDs are stored separately in the {@code refs/meta/external-ids} notes branch (see {@link
* ExternalIdNotes}).
*
* <p>On updating an account the account is evicted from the account cache and reindexed. The
@@ -321,7 +321,7 @@ public class AccountsUpdate {
AccountConfig accountConfig = read(r, accountId);
Account account =
accountConfig.getNewAccount(new Timestamp(committerIdent.getWhen().getTime()));
- AccountState accountState = AccountState.forAccount(allUsersName, account);
+ AccountState accountState = AccountState.forAccount(account);
InternalAccountUpdate.Builder updateBuilder = InternalAccountUpdate.builder();
updater.update(accountState, updateBuilder);
@@ -330,7 +330,7 @@ public class AccountsUpdate {
ExternalIdNotes extIdNotes =
createExternalIdNotes(r, accountConfig.getExternalIdsRev(), accountId, update);
UpdatedAccount updatedAccounts =
- new UpdatedAccount(allUsersName, externalIds, message, accountConfig, extIdNotes);
+ new UpdatedAccount(externalIds, message, accountConfig, extIdNotes);
updatedAccounts.setCreated(true);
return updatedAccounts;
})
@@ -377,7 +377,7 @@ public class AccountsUpdate {
r -> {
AccountConfig accountConfig = read(r, accountId);
Optional<AccountState> account =
- AccountState.fromAccountConfig(allUsersName, externalIds, accountConfig);
+ AccountState.fromAccountConfig(externalIds, accountConfig);
if (!account.isPresent()) {
return null;
}
@@ -390,7 +390,7 @@ public class AccountsUpdate {
ExternalIdNotes extIdNotes =
createExternalIdNotes(r, accountConfig.getExternalIdsRev(), accountId, update);
UpdatedAccount updatedAccounts =
- new UpdatedAccount(allUsersName, externalIds, message, accountConfig, extIdNotes);
+ new UpdatedAccount(externalIds, message, accountConfig, extIdNotes);
return updatedAccounts;
});
}
@@ -561,7 +561,6 @@ public class AccountsUpdate {
}
private static class UpdatedAccount {
- private final AllUsersName allUsersName;
private final ExternalIds externalIds;
private final String message;
private final AccountConfig accountConfig;
@@ -570,13 +569,11 @@ public class AccountsUpdate {
private boolean created;
private UpdatedAccount(
- AllUsersName allUsersName,
ExternalIds externalIds,
String message,
AccountConfig accountConfig,
ExternalIdNotes extIdNotes) {
checkState(!Strings.isNullOrEmpty(message), "message for account update must be set");
- this.allUsersName = requireNonNull(allUsersName);
this.externalIds = requireNonNull(externalIds);
this.message = requireNonNull(message);
this.accountConfig = requireNonNull(accountConfig);
@@ -592,8 +589,7 @@ public class AccountsUpdate {
}
public AccountState getAccount() throws IOException {
- return AccountState.fromAccountConfig(allUsersName, externalIds, accountConfig, extIdNotes)
- .get();
+ return AccountState.fromAccountConfig(externalIds, accountConfig, extIdNotes).get();
}
public ExternalIdNotes getExternalIdNotes() {
diff --git a/java/com/google/gerrit/server/account/AuthResult.java b/java/com/google/gerrit/server/account/AuthResult.java
index 2b1bc96f20..1f898275a4 100644
--- a/java/com/google/gerrit/server/account/AuthResult.java
+++ b/java/com/google/gerrit/server/account/AuthResult.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
/** Result from {@link AccountManager#authenticate(AuthRequest)}. */
diff --git a/java/com/google/gerrit/server/account/AuthorizedKeys.java b/java/com/google/gerrit/server/account/AuthorizedKeys.java
index b392c181e9..203ac5c588 100644
--- a/java/com/google/gerrit/server/account/AuthorizedKeys.java
+++ b/java/com/google/gerrit/server/account/AuthorizedKeys.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.account;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
diff --git a/java/com/google/gerrit/server/account/CreateGroupArgs.java b/java/com/google/gerrit/server/account/CreateGroupArgs.java
index 5bcb84b1b6..2a764ccd6d 100644
--- a/java/com/google/gerrit/server/account/CreateGroupArgs.java
+++ b/java/com/google/gerrit/server/account/CreateGroupArgs.java
@@ -14,12 +14,13 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.util.Collection;
public class CreateGroupArgs {
private AccountGroup.NameKey groupName;
+ public AccountGroup.UUID uuid;
public String groupDescription;
public boolean visibleToAll;
public AccountGroup.UUID ownerGroupUuid;
@@ -34,7 +35,7 @@ public class CreateGroupArgs {
}
public void setGroupName(String n) {
- groupName = n != null ? new AccountGroup.NameKey(n) : null;
+ groupName = n != null ? AccountGroup.nameKey(n) : null;
}
public void setGroupName(AccountGroup.NameKey n) {
diff --git a/java/com/google/gerrit/server/account/DefaultRealm.java b/java/com/google/gerrit/server/account/DefaultRealm.java
index 33de2d2d0d..329825f4d1 100644
--- a/java/com/google/gerrit/server/account/DefaultRealm.java
+++ b/java/com/google/gerrit/server/account/DefaultRealm.java
@@ -16,10 +16,10 @@ package com.google.gerrit.server.account;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.client.AuthType;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.Inject;
import com.google.inject.Provider;
diff --git a/java/com/google/gerrit/server/account/DestinationList.java b/java/com/google/gerrit/server/account/DestinationList.java
index 04e710a755..15c1e25af0 100644
--- a/java/com/google/gerrit/server/account/DestinationList.java
+++ b/java/com/google/gerrit/server/account/DestinationList.java
@@ -18,8 +18,8 @@ import com.google.common.collect.Lists;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.git.meta.TabFile;
import java.io.IOException;
@@ -28,10 +28,10 @@ import java.util.Set;
public class DestinationList extends TabFile {
public static final String DIR_NAME = "destinations";
- private SetMultimap<String, Branch.NameKey> destinations =
+ private SetMultimap<String, BranchNameKey> destinations =
MultimapBuilder.hashKeys().hashSetValues().build();
- public Set<Branch.NameKey> getDestinations(String label) {
+ public Set<BranchNameKey> getDestinations(String label) {
return destinations.get(label);
}
@@ -40,21 +40,21 @@ public class DestinationList extends TabFile {
}
String asText(String label) {
- Set<Branch.NameKey> dests = destinations.get(label);
+ Set<BranchNameKey> dests = destinations.get(label);
if (dests == null) {
return null;
}
List<Row> rows = Lists.newArrayListWithCapacity(dests.size());
- for (Branch.NameKey dest : sort(dests)) {
- rows.add(new Row(dest.get(), dest.getParentKey().get()));
+ for (BranchNameKey dest : sort(dests)) {
+ rows.add(new Row(dest.branch(), dest.project().get()));
}
return asText("Ref", "Project", rows);
}
- private static Set<Branch.NameKey> toSet(List<Row> destRows) {
- Set<Branch.NameKey> dests = Sets.newHashSetWithExpectedSize(destRows.size());
+ private static Set<BranchNameKey> toSet(List<Row> destRows) {
+ Set<BranchNameKey> dests = Sets.newHashSetWithExpectedSize(destRows.size());
for (Row row : destRows) {
- dests.add(new Branch.NameKey(new Project.NameKey(row.right), row.left));
+ dests.add(BranchNameKey.create(Project.nameKey(row.right), row.left));
}
return dests;
}
diff --git a/java/com/google/gerrit/server/account/Emails.java b/java/com/google/gerrit/server/account/Emails.java
index 426d6ea7d9..76c22cf75a 100644
--- a/java/com/google/gerrit/server/account/Emails.java
+++ b/java/com/google/gerrit/server/account/Emails.java
@@ -14,14 +14,17 @@
package com.google.gerrit.server.account;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Streams;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.SetMultimap;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.UserIdentity;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.query.account.InternalAccountQuery;
@@ -32,6 +35,11 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import org.eclipse.jgit.lib.PersonIdent;
/** Class to access accounts by email. */
@Singleton
@@ -65,15 +73,20 @@ public class Emails {
* have no external ID for the preferred email. Having accounts with a preferred email that does
* not exist as external ID is an inconsistency, but existing functionality relies on still
* getting those accounts, which is why they are included. Accounts by preferred email are fetched
- * from the account index.
+ * from the account index as a fallback for email addresses that could not be resolved using
+ * {@link ExternalIds}.
*
* @see #getAccountsFor(String...)
*/
public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException {
- return Streams.concat(
- externalIds.byEmail(email).stream().map(ExternalId::accountId),
- executeIndexQuery(() -> queryProvider.get().byPreferredEmail(email).stream())
- .map(a -> a.getAccount().getId()))
+ ImmutableSet<Account.Id> accounts =
+ externalIds.byEmail(email).stream().map(ExternalId::accountId).collect(toImmutableSet());
+ if (!accounts.isEmpty()) {
+ return accounts;
+ }
+
+ return executeIndexQuery(() -> queryProvider.get().byPreferredEmail(email).stream())
+ .map(a -> a.account().id())
.collect(toImmutableSet());
}
@@ -84,12 +97,18 @@ public class Emails {
*/
public ImmutableSetMultimap<String, Account.Id> getAccountsFor(String... emails)
throws IOException {
- ImmutableSetMultimap.Builder<String, Account.Id> builder = ImmutableSetMultimap.builder();
+ SetMultimap<String, Account.Id> result =
+ MultimapBuilder.hashKeys(emails.length).hashSetValues(1).build();
externalIds.byEmails(emails).entries().stream()
- .forEach(e -> builder.put(e.getKey(), e.getValue().accountId()));
- executeIndexQuery(() -> queryProvider.get().byPreferredEmail(emails).entries().stream())
- .forEach(e -> builder.put(e.getKey(), e.getValue().getAccount().getId()));
- return builder.build();
+ .forEach(e -> result.put(e.getKey(), e.getValue().accountId()));
+ List<String> emailsToBackfill =
+ Arrays.stream(emails).filter(e -> !result.containsKey(e)).collect(toImmutableList());
+ if (!emailsToBackfill.isEmpty()) {
+ executeIndexQuery(
+ () -> queryProvider.get().byPreferredEmail(emailsToBackfill).entries().stream())
+ .forEach(e -> result.put(e.getKey(), e.getValue().account().id()));
+ }
+ return ImmutableSetMultimap.copyOf(result);
}
/**
@@ -102,6 +121,24 @@ public class Emails {
return externalIds.byEmail(email).stream().map(ExternalId::accountId).collect(toImmutableSet());
}
+ public UserIdentity toUserIdentity(PersonIdent who) throws IOException {
+ UserIdentity u = new UserIdentity();
+ u.setName(who.getName());
+ u.setEmail(who.getEmailAddress());
+ u.setDate(new Timestamp(who.getWhen().getTime()));
+ u.setTimeZone(who.getTimeZoneOffset());
+
+ // If only one account has access to this email address, select it
+ // as the identity of the user.
+ //
+ Set<Account.Id> a = getAccountFor(u.getEmail());
+ if (a.size() == 1) {
+ u.setAccount(a.iterator().next());
+ }
+
+ return u;
+ }
+
private <T> T executeIndexQuery(Action<T> action) {
try {
return retryHelper.execute(
diff --git a/java/com/google/gerrit/server/account/FakeRealm.java b/java/com/google/gerrit/server/account/FakeRealm.java
index a53f64ec34..30274e0fed 100644
--- a/java/com/google/gerrit/server/account/FakeRealm.java
+++ b/java/com/google/gerrit/server/account/FakeRealm.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gerrit.reviewdb.client.Account;
/** Fake implementation of {@link Realm} that does not communicate. */
public class FakeRealm extends AbstractRealm {
diff --git a/java/com/google/gerrit/server/account/GroupBackend.java b/java/com/google/gerrit/server/account/GroupBackend.java
index 2d46260235..3a874bb1fa 100644
--- a/java/com/google/gerrit/server/account/GroupBackend.java
+++ b/java/com/google/gerrit/server/account/GroupBackend.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.account;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.project.ProjectState;
import java.util.Collection;
diff --git a/java/com/google/gerrit/server/account/GroupCache.java b/java/com/google/gerrit/server/account/GroupCache.java
index 8133d9ce41..90d3aa95ea 100644
--- a/java/com/google/gerrit/server/account/GroupCache.java
+++ b/java/com/google/gerrit/server/account/GroupCache.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import java.util.Optional;
diff --git a/java/com/google/gerrit/server/account/GroupCacheImpl.java b/java/com/google/gerrit/server/account/GroupCacheImpl.java
index c85e2dfb39..fe22028e77 100644
--- a/java/com/google/gerrit/server/account/GroupCacheImpl.java
+++ b/java/com/google/gerrit/server/account/GroupCacheImpl.java
@@ -17,10 +17,11 @@ package com.google.gerrit.server.account;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.Groups;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.query.group.InternalGroupQuery;
@@ -149,7 +150,9 @@ public class GroupCacheImpl implements GroupCache {
@Override
public Optional<InternalGroup> load(AccountGroup.Id key) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading group %s by ID", key)) {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading group by ID", Metadata.builder().groupId(key.get()).build())) {
return groupQueryProvider.get().byId(key);
}
}
@@ -165,8 +168,10 @@ public class GroupCacheImpl implements GroupCache {
@Override
public Optional<InternalGroup> load(String name) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading group '%s' by name", name)) {
- return groupQueryProvider.get().byName(new AccountGroup.NameKey(name));
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading group by name", Metadata.builder().groupName(name).build())) {
+ return groupQueryProvider.get().byName(AccountGroup.nameKey(name));
}
}
}
@@ -181,8 +186,10 @@ public class GroupCacheImpl implements GroupCache {
@Override
public Optional<InternalGroup> load(String uuid) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading group %s by UUID", uuid)) {
- return groups.getGroup(new AccountGroup.UUID(uuid));
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading group by UUID", Metadata.builder().groupUuid(uuid).build())) {
+ return groups.getGroup(AccountGroup.uuid(uuid));
}
}
}
diff --git a/java/com/google/gerrit/server/account/GroupControl.java b/java/com/google/gerrit/server/account/GroupControl.java
index b3e6739c2d..2228525ed7 100644
--- a/java/com/google/gerrit/server/account/GroupControl.java
+++ b/java/com/google/gerrit/server/account/GroupControl.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.account;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/account/GroupIncludeCache.java b/java/com/google/gerrit/server/account/GroupIncludeCache.java
index 612730bae5..65476190fe 100644
--- a/java/com/google/gerrit/server/account/GroupIncludeCache.java
+++ b/java/com/google/gerrit/server/account/GroupIncludeCache.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.util.Collection;
/** Tracks group inclusions in memory for efficient access. */
diff --git a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
index c27d6c30c5..7883b114b5 100644
--- a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
+++ b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -22,11 +22,12 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.Groups;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.query.group.InternalGroupQuery;
@@ -152,7 +153,9 @@ public class GroupIncludeCacheImpl implements GroupIncludeCache {
@Override
public ImmutableSet<AccountGroup.UUID> load(Account.Id memberId) {
- try (TraceTimer timer = TraceContext.newTimer("Loading groups with member %s", memberId)) {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading groups with member", Metadata.builder().accountId(memberId.get()).build())) {
return groupQueryProvider.get().byMember(memberId).stream()
.map(InternalGroup::getGroupUUID)
.collect(toImmutableSet());
@@ -171,7 +174,9 @@ public class GroupIncludeCacheImpl implements GroupIncludeCache {
@Override
public ImmutableList<AccountGroup.UUID> load(AccountGroup.UUID key) {
- try (TraceTimer timer = TraceContext.newTimer("Loading parent groups of %s", key)) {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading parent groups", Metadata.builder().groupUuid(key.get()).build())) {
return groupQueryProvider.get().bySubgroup(key).stream()
.map(InternalGroup::getGroupUUID)
.collect(toImmutableList());
diff --git a/java/com/google/gerrit/server/account/GroupMembers.java b/java/com/google/gerrit/server/account/GroupMembers.java
index d7e97ba551..c2b935b25d 100644
--- a/java/com/google/gerrit/server/account/GroupMembers.java
+++ b/java/com/google/gerrit/server/account/GroupMembers.java
@@ -19,9 +19,9 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.InternalGroupDescription;
import com.google.gerrit.server.group.SystemGroupBackend;
@@ -131,7 +131,7 @@ public class GroupMembers {
.filter(groupControl::canSeeMember)
.map(accountCache::get)
.flatMap(Streams::stream)
- .map(AccountState::getAccount)
+ .map(AccountState::account)
.collect(toImmutableSet());
Set<Account> indirectMembers = new HashSet<>();
diff --git a/java/com/google/gerrit/server/account/GroupMembership.java b/java/com/google/gerrit/server/account/GroupMembership.java
index b59b9897fc..e051794e1e 100644
--- a/java/com/google/gerrit/server/account/GroupMembership.java
+++ b/java/com/google/gerrit/server/account/GroupMembership.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import java.util.Collections;
import java.util.Set;
diff --git a/java/com/google/gerrit/server/account/GroupUUID.java b/java/com/google/gerrit/server/account/GroupUUID.java
index a7b32a1156..ac834821b7 100644
--- a/java/com/google/gerrit/server/account/GroupUUID.java
+++ b/java/com/google/gerrit/server/account/GroupUUID.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import java.security.MessageDigest;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -26,7 +26,7 @@ public class GroupUUID {
md.update(Constants.encode("group " + groupName + "\n"));
md.update(Constants.encode("creator " + creator.toExternalString() + "\n"));
md.update(Constants.encode(String.valueOf(Math.random())));
- return new AccountGroup.UUID(ObjectId.fromRaw(md.digest()).name());
+ return AccountGroup.uuid(ObjectId.fromRaw(md.digest()).name());
}
private GroupUUID() {}
diff --git a/java/com/google/gerrit/server/account/HashedPassword.java b/java/com/google/gerrit/server/account/HashedPassword.java
index bffa3ced6d..64a44950c7 100644
--- a/java/com/google/gerrit/server/account/HashedPassword.java
+++ b/java/com/google/gerrit/server/account/HashedPassword.java
@@ -22,7 +22,6 @@ import com.google.common.primitives.Ints;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.List;
-import org.apache.commons.codec.DecoderException;
import org.bouncycastle.crypto.generators.BCrypt;
import org.bouncycastle.util.Arrays;
@@ -39,6 +38,14 @@ public class HashedPassword {
// for a high cost.
private static final int DEFAULT_COST = 4;
+ public static class DecoderException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DecoderException(String message) {
+ super(message);
+ }
+ }
+
/**
* decodes a hashed password encoded with {@link #encode}.
*
diff --git a/java/com/google/gerrit/server/account/IncludingGroupMembership.java b/java/com/google/gerrit/server/account/IncludingGroupMembership.java
index b6969ac9a8..6dc79761ad 100644
--- a/java/com/google/gerrit/server/account/IncludingGroupMembership.java
+++ b/java/com/google/gerrit/server/account/IncludingGroupMembership.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.account;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.group.InternalGroup;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/account/InternalAccountDirectory.java b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
index ce97ff9602..e27b77cb3a 100644
--- a/java/com/google/gerrit/server/account/InternalAccountDirectory.java
+++ b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
@@ -20,11 +20,11 @@ import static java.util.stream.Collectors.toSet;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.AvatarInfo;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.externalids.ExternalId;
@@ -98,15 +98,14 @@ public class InternalAccountDirectory extends AccountDirectory {
Set<FillOptions> fillOptionsWithoutSecondaryEmails =
Sets.difference(options, EnumSet.of(FillOptions.SECONDARY_EMAILS));
- Set<Account.Id> ids =
- Streams.stream(in).map(a -> new Account.Id(a._accountId)).collect(toSet());
+ Set<Account.Id> ids = Streams.stream(in).map(a -> Account.id(a._accountId)).collect(toSet());
Map<Account.Id, AccountState> accountStates = accountCache.get(ids);
for (AccountInfo info : in) {
- Account.Id id = new Account.Id(info._accountId);
+ Account.Id id = Account.id(info._accountId);
AccountState state = accountStates.get(id);
if (state != null) {
if (!options.contains(FillOptions.SECONDARY_EMAILS)
- || Objects.equals(currentUserId, state.getAccount().getId())
+ || Objects.equals(currentUserId, state.account().id())
|| canModifyAccount) {
fill(info, accountStates.get(id), options);
} else {
@@ -121,50 +120,53 @@ public class InternalAccountDirectory extends AccountDirectory {
}
private void fill(AccountInfo info, AccountState accountState, Set<FillOptions> options) {
- Account account = accountState.getAccount();
+ Account account = accountState.account();
if (options.contains(FillOptions.ID)) {
- info._accountId = account.getId().get();
+ info._accountId = account.id().get();
} else {
// Was previously set to look up account for filling.
info._accountId = null;
}
if (options.contains(FillOptions.NAME)) {
- info.name = Strings.emptyToNull(account.getFullName());
+ info.name = Strings.emptyToNull(account.fullName());
if (info.name == null) {
- info.name = accountState.getUserName().orElse(null);
+ info.name = accountState.userName().orElse(null);
}
}
if (options.contains(FillOptions.EMAIL)) {
- info.email = account.getPreferredEmail();
+ info.email = account.preferredEmail();
}
if (options.contains(FillOptions.SECONDARY_EMAILS)) {
- info.secondaryEmails = getSecondaryEmails(account, accountState.getExternalIds());
+ info.secondaryEmails = getSecondaryEmails(account, accountState.externalIds());
}
if (options.contains(FillOptions.USERNAME)) {
- info.username = accountState.getUserName().orElse(null);
+ info.username = accountState.userName().orElse(null);
}
if (options.contains(FillOptions.STATUS)) {
- info.status = account.getStatus();
+ info.status = account.status();
+ }
+
+ if (options.contains(FillOptions.STATE)) {
+ info.inactive = account.inactive() ? true : null;
}
if (options.contains(FillOptions.AVATARS)) {
AvatarProvider ap = avatar.get();
if (ap != null) {
- info.avatars = new ArrayList<>(3);
- IdentifiedUser user = userFactory.create(account.getId());
-
- // GWT UI uses DEFAULT_SIZE (26px).
+ info.avatars = new ArrayList<>();
+ IdentifiedUser user = userFactory.create(account.id());
+
+ // PolyGerrit UI uses the following sizes for avatars:
+ // - 32px for avatars next to names e.g. on the dashboard. This is also Gerrit's default.
+ // - 56px for the user's own avatar in the menu
+ // - 100ox for other user's avatars on dashboards
+ // - 120px for the user's own profile settings page
addAvatar(ap, info, user, AvatarInfo.DEFAULT_SIZE);
-
- // PolyGerrit UI prefers 32px and 100px.
if (!info.avatars.isEmpty()) {
- if (32 != AvatarInfo.DEFAULT_SIZE) {
- addAvatar(ap, info, user, 32);
- }
- if (100 != AvatarInfo.DEFAULT_SIZE) {
- addAvatar(ap, info, user, 100);
- }
+ addAvatar(ap, info, user, 56);
+ addAvatar(ap, info, user, 100);
+ addAvatar(ap, info, user, 120);
}
}
}
@@ -172,7 +174,7 @@ public class InternalAccountDirectory extends AccountDirectory {
public List<String> getSecondaryEmails(Account account, Collection<ExternalId> externalIds) {
return ExternalId.getEmails(externalIds)
- .filter(e -> !e.equals(account.getPreferredEmail()))
+ .filter(e -> !e.equals(account.preferredEmail()))
.sorted()
.collect(toList());
}
diff --git a/java/com/google/gerrit/server/account/InternalAccountUpdate.java b/java/com/google/gerrit/server/account/InternalAccountUpdate.java
index c778fca17c..cf77a75b26 100644
--- a/java/com/google/gerrit/server/account/InternalAccountUpdate.java
+++ b/java/com/google/gerrit/server/account/InternalAccountUpdate.java
@@ -18,10 +18,10 @@ import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
diff --git a/java/com/google/gerrit/server/account/InternalGroupBackend.java b/java/com/google/gerrit/server/account/InternalGroupBackend.java
index ea6eb87443..ddd3da20b4 100644
--- a/java/com/google/gerrit/server/account/InternalGroupBackend.java
+++ b/java/com/google/gerrit/server/account/InternalGroupBackend.java
@@ -19,7 +19,7 @@ import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.InternalGroupDescription;
diff --git a/java/com/google/gerrit/server/account/ListGroupMembership.java b/java/com/google/gerrit/server/account/ListGroupMembership.java
index 60e73454fc..0f4fb78c6f 100644
--- a/java/com/google/gerrit/server/account/ListGroupMembership.java
+++ b/java/com/google/gerrit/server/account/ListGroupMembership.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.account;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import java.util.Set;
/** GroupMembership over an explicit list. */
diff --git a/java/com/google/gerrit/server/account/Preferences.java b/java/com/google/gerrit/server/account/Preferences.java
index cd849b8273..ece610b687 100644
--- a/java/com/google/gerrit/server/account/Preferences.java
+++ b/java/com/google/gerrit/server/account/Preferences.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 The Android Open Source Project
+// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -11,566 +11,419 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-
package com.google.gerrit.server.account;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.config.ConfigUtil.loadSection;
-import static com.google.gerrit.server.config.ConfigUtil.skipField;
-import static com.google.gerrit.server.config.ConfigUtil.storeSection;
-import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE;
-import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
-import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
-import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
-import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.flogger.FluentLogger;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DateFormat;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DefaultBase;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo.TimeFormat;
import com.google.gerrit.extensions.client.MenuItem;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.UserConfigSections;
-import com.google.gerrit.server.git.ValidationError;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.git.meta.VersionedMetaData;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Optional;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Repository;
-
-/**
- * Parses/writes preferences from/to a {@link Config} file.
- *
- * <p>This is a low-level API. Read/write of preferences in a user branch should be done through
- * {@link AccountsUpdate} or {@link AccountConfig}.
- *
- * <p>The config file has separate sections for general, diff and edit preferences:
- *
- * <pre>
- * [general]
- * showSiteHeader = false
- * [diff]
- * hideTopMenu = true
- * [edit]
- * lineLength = 80
- * </pre>
- *
- * <p>The parameter names match the names that are used in the preferences REST API.
- *
- * <p>If the preference is omitted in the config file, then the default value for the preference is
- * used.
- *
- * <p>Defaults for preferences that apply for all accounts can be configured in the {@code
- * refs/users/default} branch in the {@code All-Users} repository. The config for the default
- * preferences must be provided to this class so that it can read default values from it.
- *
- * <p>The preferences are lazily parsed.
- */
-public class Preferences {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- public static final String PREFERENCES_CONFIG = "preferences.config";
-
- private final Account.Id accountId;
- private final Config cfg;
- private final Config defaultCfg;
- private final ValidationError.Sink validationErrorSink;
-
- private GeneralPreferencesInfo generalPreferences;
- private DiffPreferencesInfo diffPreferences;
- private EditPreferencesInfo editPreferences;
-
- Preferences(
- Account.Id accountId,
- Config cfg,
- Config defaultCfg,
- ValidationError.Sink validationErrorSink) {
- this.accountId = requireNonNull(accountId, "accountId");
- this.cfg = requireNonNull(cfg, "cfg");
- this.defaultCfg = requireNonNull(defaultCfg, "defaultCfg");
- this.validationErrorSink = requireNonNull(validationErrorSink, "validationErrorSink");
- }
- public GeneralPreferencesInfo getGeneralPreferences() {
- if (generalPreferences == null) {
- parse();
- }
- return generalPreferences;
- }
+@AutoValue
+public abstract class Preferences {
+ @AutoValue
+ public abstract static class General {
+ public abstract Optional<Integer> changesPerPage();
- public DiffPreferencesInfo getDiffPreferences() {
- if (diffPreferences == null) {
- parse();
- }
- return diffPreferences;
- }
+ public abstract Optional<String> downloadScheme();
- public EditPreferencesInfo getEditPreferences() {
- if (editPreferences == null) {
- parse();
- }
- return editPreferences;
- }
+ public abstract Optional<DateFormat> dateFormat();
- public void parse() {
- generalPreferences = parseGeneralPreferences(null);
- diffPreferences = parseDiffPreferences(null);
- editPreferences = parseEditPreferences(null);
- }
+ public abstract Optional<TimeFormat> timeFormat();
- public Config saveGeneralPreferences(
- Optional<GeneralPreferencesInfo> generalPreferencesInput,
- Optional<DiffPreferencesInfo> diffPreferencesInput,
- Optional<EditPreferencesInfo> editPreferencesInput)
- throws ConfigInvalidException {
- if (generalPreferencesInput.isPresent()) {
- GeneralPreferencesInfo mergedGeneralPreferencesInput =
- parseGeneralPreferences(generalPreferencesInput.get());
-
- storeSection(
- cfg,
- UserConfigSections.GENERAL,
- null,
- mergedGeneralPreferencesInput,
- parseDefaultGeneralPreferences(defaultCfg, null));
- setChangeTable(cfg, mergedGeneralPreferencesInput.changeTable);
- setMy(cfg, mergedGeneralPreferencesInput.my);
-
- // evict the cached general preferences
- this.generalPreferences = null;
- }
+ public abstract Optional<Boolean> expandInlineDiffs();
- if (diffPreferencesInput.isPresent()) {
- DiffPreferencesInfo mergedDiffPreferencesInput =
- parseDiffPreferences(diffPreferencesInput.get());
+ public abstract Optional<Boolean> highlightAssigneeInChangeTable();
- storeSection(
- cfg,
- UserConfigSections.DIFF,
- null,
- mergedDiffPreferencesInput,
- parseDefaultDiffPreferences(defaultCfg, null));
+ public abstract Optional<Boolean> relativeDateInChangeTable();
- // evict the cached diff preferences
- this.diffPreferences = null;
- }
+ public abstract Optional<DiffView> diffView();
- if (editPreferencesInput.isPresent()) {
- EditPreferencesInfo mergedEditPreferencesInput =
- parseEditPreferences(editPreferencesInput.get());
+ public abstract Optional<Boolean> sizeBarInChangeTable();
- storeSection(
- cfg,
- UserConfigSections.EDIT,
- null,
- mergedEditPreferencesInput,
- parseDefaultEditPreferences(defaultCfg, null));
+ public abstract Optional<Boolean> legacycidInChangeTable();
- // evict the cached edit preferences
- this.editPreferences = null;
- }
+ public abstract Optional<Boolean> muteCommonPathPrefixes();
- return cfg;
- }
+ public abstract Optional<Boolean> signedOffBy();
- private GeneralPreferencesInfo parseGeneralPreferences(@Nullable GeneralPreferencesInfo input) {
- try {
- return parseGeneralPreferences(cfg, defaultCfg, input);
- } catch (ConfigInvalidException e) {
- validationErrorSink.error(
- new ValidationError(
- PREFERENCES_CONFIG,
- String.format(
- "Invalid general preferences for account %d: %s",
- accountId.get(), e.getMessage())));
- return new GeneralPreferencesInfo();
- }
- }
+ public abstract Optional<EmailStrategy> emailStrategy();
- private DiffPreferencesInfo parseDiffPreferences(@Nullable DiffPreferencesInfo input) {
- try {
- return parseDiffPreferences(cfg, defaultCfg, input);
- } catch (ConfigInvalidException e) {
- validationErrorSink.error(
- new ValidationError(
- PREFERENCES_CONFIG,
- String.format(
- "Invalid diff preferences for account %d: %s", accountId.get(), e.getMessage())));
- return new DiffPreferencesInfo();
- }
- }
+ public abstract Optional<EmailFormat> emailFormat();
- private EditPreferencesInfo parseEditPreferences(@Nullable EditPreferencesInfo input) {
- try {
- return parseEditPreferences(cfg, defaultCfg, input);
- } catch (ConfigInvalidException e) {
- validationErrorSink.error(
- new ValidationError(
- PREFERENCES_CONFIG,
- String.format(
- "Invalid edit preferences for account %d: %s", accountId.get(), e.getMessage())));
- return new EditPreferencesInfo();
- }
- }
+ public abstract Optional<DefaultBase> defaultBaseForMerges();
- private static GeneralPreferencesInfo parseGeneralPreferences(
- Config cfg, @Nullable Config defaultCfg, @Nullable GeneralPreferencesInfo input)
- throws ConfigInvalidException {
- GeneralPreferencesInfo r =
- loadSection(
- cfg,
- UserConfigSections.GENERAL,
- null,
- new GeneralPreferencesInfo(),
- defaultCfg != null
- ? parseDefaultGeneralPreferences(defaultCfg, input)
- : GeneralPreferencesInfo.defaults(),
- input);
- if (input != null) {
- r.changeTable = input.changeTable;
- r.my = input.my;
- } else {
- r.changeTable = parseChangeTableColumns(cfg, defaultCfg);
- r.my = parseMyMenus(cfg, defaultCfg);
- }
- return r;
- }
+ public abstract Optional<Boolean> publishCommentsOnPush();
- private static DiffPreferencesInfo parseDiffPreferences(
- Config cfg, @Nullable Config defaultCfg, @Nullable DiffPreferencesInfo input)
- throws ConfigInvalidException {
- return loadSection(
- cfg,
- UserConfigSections.DIFF,
- null,
- new DiffPreferencesInfo(),
- defaultCfg != null
- ? parseDefaultDiffPreferences(defaultCfg, input)
- : DiffPreferencesInfo.defaults(),
- input);
- }
+ public abstract Optional<Boolean> workInProgressByDefault();
- private static EditPreferencesInfo parseEditPreferences(
- Config cfg, @Nullable Config defaultCfg, @Nullable EditPreferencesInfo input)
- throws ConfigInvalidException {
- return loadSection(
- cfg,
- UserConfigSections.EDIT,
- null,
- new EditPreferencesInfo(),
- defaultCfg != null
- ? parseDefaultEditPreferences(defaultCfg, input)
- : EditPreferencesInfo.defaults(),
- input);
- }
+ public abstract Optional<ImmutableList<MenuItem>> my();
- private static GeneralPreferencesInfo parseDefaultGeneralPreferences(
- Config defaultCfg, GeneralPreferencesInfo input) throws ConfigInvalidException {
- GeneralPreferencesInfo allUserPrefs = new GeneralPreferencesInfo();
- loadSection(
- defaultCfg,
- UserConfigSections.GENERAL,
- null,
- allUserPrefs,
- GeneralPreferencesInfo.defaults(),
- input);
- return updateGeneralPreferencesDefaults(allUserPrefs);
- }
+ public abstract Optional<ImmutableList<String>> changeTable();
- private static DiffPreferencesInfo parseDefaultDiffPreferences(
- Config defaultCfg, DiffPreferencesInfo input) throws ConfigInvalidException {
- DiffPreferencesInfo allUserPrefs = new DiffPreferencesInfo();
- loadSection(
- defaultCfg,
- UserConfigSections.DIFF,
- null,
- allUserPrefs,
- DiffPreferencesInfo.defaults(),
- input);
- return updateDiffPreferencesDefaults(allUserPrefs);
- }
+ @AutoValue.Builder
+ public abstract static class Builder {
+ abstract Builder changesPerPage(@Nullable Integer val);
- private static EditPreferencesInfo parseDefaultEditPreferences(
- Config defaultCfg, EditPreferencesInfo input) throws ConfigInvalidException {
- EditPreferencesInfo allUserPrefs = new EditPreferencesInfo();
- loadSection(
- defaultCfg,
- UserConfigSections.EDIT,
- null,
- allUserPrefs,
- EditPreferencesInfo.defaults(),
- input);
- return updateEditPreferencesDefaults(allUserPrefs);
- }
+ abstract Builder downloadScheme(@Nullable String val);
- private static GeneralPreferencesInfo updateGeneralPreferencesDefaults(
- GeneralPreferencesInfo input) {
- GeneralPreferencesInfo result = GeneralPreferencesInfo.defaults();
- try {
- for (Field field : input.getClass().getDeclaredFields()) {
- if (skipField(field)) {
- continue;
- }
- Object newVal = field.get(input);
- if (newVal != null) {
- field.set(result, newVal);
- }
- }
- } catch (IllegalAccessException e) {
- logger.atSevere().withCause(e).log("Failed to apply default general preferences");
- return GeneralPreferencesInfo.defaults();
- }
- return result;
- }
+ abstract Builder dateFormat(@Nullable DateFormat val);
- private static DiffPreferencesInfo updateDiffPreferencesDefaults(DiffPreferencesInfo input) {
- DiffPreferencesInfo result = DiffPreferencesInfo.defaults();
- try {
- for (Field field : input.getClass().getDeclaredFields()) {
- if (skipField(field)) {
- continue;
- }
- Object newVal = field.get(input);
- if (newVal != null) {
- field.set(result, newVal);
- }
- }
- } catch (IllegalAccessException e) {
- logger.atSevere().withCause(e).log("Failed to apply default diff preferences");
- return DiffPreferencesInfo.defaults();
- }
- return result;
- }
+ abstract Builder timeFormat(@Nullable TimeFormat val);
- private static EditPreferencesInfo updateEditPreferencesDefaults(EditPreferencesInfo input) {
- EditPreferencesInfo result = EditPreferencesInfo.defaults();
- try {
- for (Field field : input.getClass().getDeclaredFields()) {
- if (skipField(field)) {
- continue;
- }
- Object newVal = field.get(input);
- if (newVal != null) {
- field.set(result, newVal);
- }
- }
- } catch (IllegalAccessException e) {
- logger.atSevere().withCause(e).log("Failed to apply default edit preferences");
- return EditPreferencesInfo.defaults();
- }
- return result;
- }
+ abstract Builder expandInlineDiffs(@Nullable Boolean val);
+
+ abstract Builder highlightAssigneeInChangeTable(@Nullable Boolean val);
- private static List<String> parseChangeTableColumns(Config cfg, @Nullable Config defaultCfg) {
- List<String> changeTable = changeTable(cfg);
- if (changeTable == null && defaultCfg != null) {
- changeTable = changeTable(defaultCfg);
+ abstract Builder relativeDateInChangeTable(@Nullable Boolean val);
+
+ abstract Builder diffView(@Nullable DiffView val);
+
+ abstract Builder sizeBarInChangeTable(@Nullable Boolean val);
+
+ abstract Builder legacycidInChangeTable(@Nullable Boolean val);
+
+ abstract Builder muteCommonPathPrefixes(@Nullable Boolean val);
+
+ abstract Builder signedOffBy(@Nullable Boolean val);
+
+ abstract Builder emailStrategy(@Nullable EmailStrategy val);
+
+ abstract Builder emailFormat(@Nullable EmailFormat val);
+
+ abstract Builder defaultBaseForMerges(@Nullable DefaultBase val);
+
+ abstract Builder publishCommentsOnPush(@Nullable Boolean val);
+
+ abstract Builder workInProgressByDefault(@Nullable Boolean val);
+
+ abstract Builder my(@Nullable ImmutableList<MenuItem> val);
+
+ abstract Builder changeTable(@Nullable ImmutableList<String> val);
+
+ abstract General build();
}
- return changeTable;
- }
- private static List<MenuItem> parseMyMenus(Config cfg, @Nullable Config defaultCfg) {
- List<MenuItem> my = my(cfg);
- if (my.isEmpty() && defaultCfg != null) {
- my = my(defaultCfg);
+ public static General fromInfo(GeneralPreferencesInfo info) {
+ return (new AutoValue_Preferences_General.Builder())
+ .changesPerPage(info.changesPerPage)
+ .downloadScheme(info.downloadScheme)
+ .dateFormat(info.dateFormat)
+ .timeFormat(info.timeFormat)
+ .expandInlineDiffs(info.expandInlineDiffs)
+ .highlightAssigneeInChangeTable(info.highlightAssigneeInChangeTable)
+ .relativeDateInChangeTable(info.relativeDateInChangeTable)
+ .diffView(info.diffView)
+ .sizeBarInChangeTable(info.sizeBarInChangeTable)
+ .legacycidInChangeTable(info.legacycidInChangeTable)
+ .muteCommonPathPrefixes(info.muteCommonPathPrefixes)
+ .signedOffBy(info.signedOffBy)
+ .emailStrategy(info.emailStrategy)
+ .emailFormat(info.emailFormat)
+ .defaultBaseForMerges(info.defaultBaseForMerges)
+ .publishCommentsOnPush(info.publishCommentsOnPush)
+ .workInProgressByDefault(info.workInProgressByDefault)
+ .my(info.my == null ? null : ImmutableList.copyOf(info.my))
+ .changeTable(info.changeTable == null ? null : ImmutableList.copyOf(info.changeTable))
+ .build();
}
- if (my.isEmpty()) {
- my.add(new MenuItem("Changes", "#/dashboard/self", null));
- my.add(new MenuItem("Draft Comments", "#/q/has:draft", null));
- my.add(new MenuItem("Edits", "#/q/has:edit", null));
- my.add(new MenuItem("Watched Changes", "#/q/is:watched+is:open", null));
- my.add(new MenuItem("Starred Changes", "#/q/is:starred", null));
- my.add(new MenuItem("Groups", "#/groups/self", null));
+
+ public GeneralPreferencesInfo toInfo() {
+ GeneralPreferencesInfo info = new GeneralPreferencesInfo();
+ info.changesPerPage = changesPerPage().orElse(null);
+ info.downloadScheme = downloadScheme().orElse(null);
+ info.dateFormat = dateFormat().orElse(null);
+ info.timeFormat = timeFormat().orElse(null);
+ info.expandInlineDiffs = expandInlineDiffs().orElse(null);
+ info.highlightAssigneeInChangeTable = highlightAssigneeInChangeTable().orElse(null);
+ info.relativeDateInChangeTable = relativeDateInChangeTable().orElse(null);
+ info.diffView = diffView().orElse(null);
+ info.sizeBarInChangeTable = sizeBarInChangeTable().orElse(null);
+ info.legacycidInChangeTable = legacycidInChangeTable().orElse(null);
+ info.muteCommonPathPrefixes = muteCommonPathPrefixes().orElse(null);
+ info.signedOffBy = signedOffBy().orElse(null);
+ info.emailStrategy = emailStrategy().orElse(null);
+ info.emailFormat = emailFormat().orElse(null);
+ info.defaultBaseForMerges = defaultBaseForMerges().orElse(null);
+ info.publishCommentsOnPush = publishCommentsOnPush().orElse(null);
+ info.workInProgressByDefault = workInProgressByDefault().orElse(null);
+ info.my = my().orElse(null);
+ info.changeTable = changeTable().orElse(null);
+ return info;
}
- return my;
}
- public static GeneralPreferencesInfo readDefaultGeneralPreferences(
- AllUsersName allUsersName, Repository allUsersRepo)
- throws IOException, ConfigInvalidException {
- return parseGeneralPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
- }
+ @AutoValue
+ public abstract static class Edit {
+ public abstract Optional<Integer> tabSize();
- public static DiffPreferencesInfo readDefaultDiffPreferences(
- AllUsersName allUsersName, Repository allUsersRepo)
- throws IOException, ConfigInvalidException {
- return parseDiffPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
- }
+ public abstract Optional<Integer> lineLength();
- public static EditPreferencesInfo readDefaultEditPreferences(
- AllUsersName allUsersName, Repository allUsersRepo)
- throws IOException, ConfigInvalidException {
- return parseEditPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
- }
+ public abstract Optional<Integer> indentUnit();
- static Config readDefaultConfig(AllUsersName allUsersName, Repository allUsersRepo)
- throws IOException, ConfigInvalidException {
- VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
- defaultPrefs.load(allUsersName, allUsersRepo);
- return defaultPrefs.getConfig();
- }
+ public abstract Optional<Integer> cursorBlinkRate();
- public static GeneralPreferencesInfo updateDefaultGeneralPreferences(
- MetaDataUpdate md, GeneralPreferencesInfo input) throws IOException, ConfigInvalidException {
- VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
- defaultPrefs.load(md);
- storeSection(
- defaultPrefs.getConfig(),
- UserConfigSections.GENERAL,
- null,
- input,
- GeneralPreferencesInfo.defaults());
- setMy(defaultPrefs.getConfig(), input.my);
- setChangeTable(defaultPrefs.getConfig(), input.changeTable);
- defaultPrefs.commit(md);
-
- return parseGeneralPreferences(defaultPrefs.getConfig(), null, null);
- }
+ public abstract Optional<Boolean> hideTopMenu();
- public static DiffPreferencesInfo updateDefaultDiffPreferences(
- MetaDataUpdate md, DiffPreferencesInfo input) throws IOException, ConfigInvalidException {
- VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
- defaultPrefs.load(md);
- storeSection(
- defaultPrefs.getConfig(),
- UserConfigSections.DIFF,
- null,
- input,
- DiffPreferencesInfo.defaults());
- defaultPrefs.commit(md);
-
- return parseDiffPreferences(defaultPrefs.getConfig(), null, null);
- }
+ public abstract Optional<Boolean> showTabs();
- public static EditPreferencesInfo updateDefaultEditPreferences(
- MetaDataUpdate md, EditPreferencesInfo input) throws IOException, ConfigInvalidException {
- VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
- defaultPrefs.load(md);
- storeSection(
- defaultPrefs.getConfig(),
- UserConfigSections.EDIT,
- null,
- input,
- EditPreferencesInfo.defaults());
- defaultPrefs.commit(md);
-
- return parseEditPreferences(defaultPrefs.getConfig(), null, null);
- }
+ public abstract Optional<Boolean> showWhitespaceErrors();
- private static List<String> changeTable(Config cfg) {
- return Lists.newArrayList(cfg.getStringList(CHANGE_TABLE, null, CHANGE_TABLE_COLUMN));
- }
+ public abstract Optional<Boolean> syntaxHighlighting();
- private static void setChangeTable(Config cfg, List<String> changeTable) {
- if (changeTable != null) {
- unsetSection(cfg, UserConfigSections.CHANGE_TABLE);
- cfg.setStringList(UserConfigSections.CHANGE_TABLE, null, CHANGE_TABLE_COLUMN, changeTable);
- }
- }
+ public abstract Optional<Boolean> hideLineNumbers();
- private static List<MenuItem> my(Config cfg) {
- List<MenuItem> my = new ArrayList<>();
- for (String subsection : cfg.getSubsections(UserConfigSections.MY)) {
- String url = my(cfg, subsection, KEY_URL, "#/");
- String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank");
- my.add(new MenuItem(subsection, url, target, my(cfg, subsection, KEY_ID, null)));
- }
- return my;
- }
+ public abstract Optional<Boolean> matchBrackets();
- private static String my(Config cfg, String subsection, String key, String defaultValue) {
- String val = cfg.getString(UserConfigSections.MY, subsection, key);
- return !Strings.isNullOrEmpty(val) ? val : defaultValue;
- }
+ public abstract Optional<Boolean> lineWrapping();
- private static void setMy(Config cfg, List<MenuItem> my) {
- if (my != null) {
- unsetSection(cfg, UserConfigSections.MY);
- for (MenuItem item : my) {
- checkState(!isNullOrEmpty(item.name), "MenuItem.name must not be null or empty");
- checkState(!isNullOrEmpty(item.url), "MenuItem.url must not be null or empty");
-
- setMy(cfg, item.name, KEY_URL, item.url);
- setMy(cfg, item.name, KEY_TARGET, item.target);
- setMy(cfg, item.name, KEY_ID, item.id);
- }
- }
- }
+ public abstract Optional<Boolean> indentWithTabs();
+
+ public abstract Optional<Boolean> autoCloseBrackets();
+
+ public abstract Optional<Boolean> showBase();
- public static void validateMy(List<MenuItem> my) throws BadRequestException {
- if (my == null) {
- return;
+ @AutoValue.Builder
+ public abstract static class Builder {
+ abstract Builder tabSize(@Nullable Integer val);
+
+ abstract Builder lineLength(@Nullable Integer val);
+
+ abstract Builder indentUnit(@Nullable Integer val);
+
+ abstract Builder cursorBlinkRate(@Nullable Integer val);
+
+ abstract Builder hideTopMenu(@Nullable Boolean val);
+
+ abstract Builder showTabs(@Nullable Boolean val);
+
+ abstract Builder showWhitespaceErrors(@Nullable Boolean val);
+
+ abstract Builder syntaxHighlighting(@Nullable Boolean val);
+
+ abstract Builder hideLineNumbers(@Nullable Boolean val);
+
+ abstract Builder matchBrackets(@Nullable Boolean val);
+
+ abstract Builder lineWrapping(@Nullable Boolean val);
+
+ abstract Builder indentWithTabs(@Nullable Boolean val);
+
+ abstract Builder autoCloseBrackets(@Nullable Boolean val);
+
+ abstract Builder showBase(@Nullable Boolean val);
+
+ abstract Edit build();
}
- for (MenuItem item : my) {
- checkRequiredMenuItemField(item.name, "name");
- checkRequiredMenuItemField(item.url, "URL");
+
+ public static Edit fromInfo(EditPreferencesInfo info) {
+ return (new AutoValue_Preferences_Edit.Builder())
+ .tabSize(info.tabSize)
+ .lineLength(info.lineLength)
+ .indentUnit(info.indentUnit)
+ .cursorBlinkRate(info.cursorBlinkRate)
+ .hideTopMenu(info.hideTopMenu)
+ .showTabs(info.showTabs)
+ .showWhitespaceErrors(info.showWhitespaceErrors)
+ .syntaxHighlighting(info.syntaxHighlighting)
+ .hideLineNumbers(info.hideLineNumbers)
+ .matchBrackets(info.matchBrackets)
+ .lineWrapping(info.lineWrapping)
+ .indentWithTabs(info.indentWithTabs)
+ .autoCloseBrackets(info.autoCloseBrackets)
+ .showBase(info.showBase)
+ .build();
}
- }
- private static void checkRequiredMenuItemField(String value, String name)
- throws BadRequestException {
- if (isNullOrEmpty(value)) {
- throw new BadRequestException(name + " for menu item is required");
+ public EditPreferencesInfo toInfo() {
+ EditPreferencesInfo info = new EditPreferencesInfo();
+ info.tabSize = tabSize().orElse(null);
+ info.lineLength = lineLength().orElse(null);
+ info.indentUnit = indentUnit().orElse(null);
+ info.cursorBlinkRate = cursorBlinkRate().orElse(null);
+ info.hideTopMenu = hideTopMenu().orElse(null);
+ info.showTabs = showTabs().orElse(null);
+ info.showWhitespaceErrors = showWhitespaceErrors().orElse(null);
+ info.syntaxHighlighting = syntaxHighlighting().orElse(null);
+ info.hideLineNumbers = hideLineNumbers().orElse(null);
+ info.matchBrackets = matchBrackets().orElse(null);
+ info.lineWrapping = lineWrapping().orElse(null);
+ info.indentWithTabs = indentWithTabs().orElse(null);
+ info.autoCloseBrackets = autoCloseBrackets().orElse(null);
+ info.showBase = showBase().orElse(null);
+ return info;
}
}
- private static boolean isNullOrEmpty(String value) {
- return value == null || value.trim().isEmpty();
- }
+ @AutoValue
+ public abstract static class Diff {
+ public abstract Optional<Integer> context();
- private static void setMy(Config cfg, String section, String key, @Nullable String val) {
- if (val == null || val.trim().isEmpty()) {
- cfg.unset(UserConfigSections.MY, section.trim(), key);
- } else {
- cfg.setString(UserConfigSections.MY, section.trim(), key, val.trim());
- }
- }
+ public abstract Optional<Integer> tabSize();
- private static void unsetSection(Config cfg, String section) {
- cfg.unsetSection(section, null);
- for (String subsection : cfg.getSubsections(section)) {
- cfg.unsetSection(section, subsection);
- }
- }
+ public abstract Optional<Integer> fontSize();
- private static class VersionedDefaultPreferences extends VersionedMetaData {
- private Config cfg;
+ public abstract Optional<Integer> lineLength();
- @Override
- protected String getRefName() {
- return RefNames.REFS_USERS_DEFAULT;
- }
+ public abstract Optional<Integer> cursorBlinkRate();
+
+ public abstract Optional<Boolean> expandAllComments();
+
+ public abstract Optional<Boolean> intralineDifference();
+
+ public abstract Optional<Boolean> manualReview();
+
+ public abstract Optional<Boolean> showLineEndings();
+
+ public abstract Optional<Boolean> showTabs();
+
+ public abstract Optional<Boolean> showWhitespaceErrors();
+
+ public abstract Optional<Boolean> syntaxHighlighting();
+
+ public abstract Optional<Boolean> hideTopMenu();
+
+ public abstract Optional<Boolean> autoHideDiffTableHeader();
+
+ public abstract Optional<Boolean> hideLineNumbers();
+
+ public abstract Optional<Boolean> renderEntireFile();
+
+ public abstract Optional<Boolean> hideEmptyPane();
+
+ public abstract Optional<Boolean> matchBrackets();
+
+ public abstract Optional<Boolean> lineWrapping();
+
+ public abstract Optional<Whitespace> ignoreWhitespace();
+
+ public abstract Optional<Boolean> retainHeader();
+
+ public abstract Optional<Boolean> skipDeleted();
+
+ public abstract Optional<Boolean> skipUnchanged();
+
+ public abstract Optional<Boolean> skipUncommented();
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ abstract Builder context(@Nullable Integer val);
+
+ abstract Builder tabSize(@Nullable Integer val);
+
+ abstract Builder fontSize(@Nullable Integer val);
+
+ abstract Builder lineLength(@Nullable Integer val);
+
+ abstract Builder cursorBlinkRate(@Nullable Integer val);
+
+ abstract Builder expandAllComments(@Nullable Boolean val);
+
+ abstract Builder intralineDifference(@Nullable Boolean val);
+
+ abstract Builder manualReview(@Nullable Boolean val);
+
+ abstract Builder showLineEndings(@Nullable Boolean val);
+
+ abstract Builder showTabs(@Nullable Boolean val);
+
+ abstract Builder showWhitespaceErrors(@Nullable Boolean val);
+
+ abstract Builder syntaxHighlighting(@Nullable Boolean val);
+
+ abstract Builder hideTopMenu(@Nullable Boolean val);
+
+ abstract Builder autoHideDiffTableHeader(@Nullable Boolean val);
+
+ abstract Builder hideLineNumbers(@Nullable Boolean val);
+
+ abstract Builder renderEntireFile(@Nullable Boolean val);
+
+ abstract Builder hideEmptyPane(@Nullable Boolean val);
+
+ abstract Builder matchBrackets(@Nullable Boolean val);
+
+ abstract Builder lineWrapping(@Nullable Boolean val);
+
+ abstract Builder ignoreWhitespace(@Nullable Whitespace val);
+
+ abstract Builder retainHeader(@Nullable Boolean val);
+
+ abstract Builder skipDeleted(@Nullable Boolean val);
+
+ abstract Builder skipUnchanged(@Nullable Boolean val);
+
+ abstract Builder skipUncommented(@Nullable Boolean val);
- private Config getConfig() {
- checkState(cfg != null, "Default preferences not loaded yet.");
- return cfg;
+ abstract Diff build();
}
- @Override
- protected void onLoad() throws IOException, ConfigInvalidException {
- cfg = readConfig(PREFERENCES_CONFIG);
+ public static Diff fromInfo(DiffPreferencesInfo info) {
+ return (new AutoValue_Preferences_Diff.Builder())
+ .context(info.context)
+ .tabSize(info.tabSize)
+ .fontSize(info.fontSize)
+ .lineLength(info.lineLength)
+ .cursorBlinkRate(info.cursorBlinkRate)
+ .expandAllComments(info.expandAllComments)
+ .intralineDifference(info.intralineDifference)
+ .manualReview(info.manualReview)
+ .showLineEndings(info.showLineEndings)
+ .showTabs(info.showTabs)
+ .showWhitespaceErrors(info.showWhitespaceErrors)
+ .syntaxHighlighting(info.syntaxHighlighting)
+ .hideTopMenu(info.hideTopMenu)
+ .autoHideDiffTableHeader(info.autoHideDiffTableHeader)
+ .hideLineNumbers(info.hideLineNumbers)
+ .renderEntireFile(info.renderEntireFile)
+ .hideEmptyPane(info.hideEmptyPane)
+ .matchBrackets(info.matchBrackets)
+ .lineWrapping(info.lineWrapping)
+ .ignoreWhitespace(info.ignoreWhitespace)
+ .retainHeader(info.retainHeader)
+ .skipDeleted(info.skipDeleted)
+ .skipUnchanged(info.skipUnchanged)
+ .skipUncommented(info.skipUncommented)
+ .build();
}
- @Override
- protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
- if (Strings.isNullOrEmpty(commit.getMessage())) {
- commit.setMessage("Update default preferences\n");
- }
- saveConfig(PREFERENCES_CONFIG, cfg);
- return true;
+ public DiffPreferencesInfo toInfo() {
+ DiffPreferencesInfo info = new DiffPreferencesInfo();
+ info.context = context().orElse(null);
+ info.tabSize = tabSize().orElse(null);
+ info.fontSize = fontSize().orElse(null);
+ info.lineLength = lineLength().orElse(null);
+ info.cursorBlinkRate = cursorBlinkRate().orElse(null);
+ info.expandAllComments = expandAllComments().orElse(null);
+ info.intralineDifference = intralineDifference().orElse(null);
+ info.manualReview = manualReview().orElse(null);
+ info.showLineEndings = showLineEndings().orElse(null);
+ info.showTabs = showTabs().orElse(null);
+ info.showWhitespaceErrors = showWhitespaceErrors().orElse(null);
+ info.syntaxHighlighting = syntaxHighlighting().orElse(null);
+ info.hideTopMenu = hideTopMenu().orElse(null);
+ info.autoHideDiffTableHeader = autoHideDiffTableHeader().orElse(null);
+ info.hideLineNumbers = hideLineNumbers().orElse(null);
+ info.renderEntireFile = renderEntireFile().orElse(null);
+ info.hideEmptyPane = hideEmptyPane().orElse(null);
+ info.matchBrackets = matchBrackets().orElse(null);
+ info.lineWrapping = lineWrapping().orElse(null);
+ info.ignoreWhitespace = ignoreWhitespace().orElse(null);
+ info.retainHeader = retainHeader().orElse(null);
+ info.skipDeleted = skipDeleted().orElse(null);
+ info.skipUnchanged = skipUnchanged().orElse(null);
+ info.skipUncommented = skipUncommented().orElse(null);
+ return info;
}
}
}
diff --git a/java/com/google/gerrit/server/account/ProjectWatches.java b/java/com/google/gerrit/server/account/ProjectWatches.java
index a750ba5056..b153b781ad 100644
--- a/java/com/google/gerrit/server/account/ProjectWatches.java
+++ b/java/com/google/gerrit/server/account/ProjectWatches.java
@@ -30,8 +30,8 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.ValidationError;
import java.util.ArrayList;
import java.util.Collection;
@@ -168,7 +168,7 @@ public class ProjectWatches {
}
ProjectWatchKey key =
- ProjectWatchKey.create(new Project.NameKey(projectName), notifyValue.filter());
+ ProjectWatchKey.create(Project.nameKey(projectName), notifyValue.filter());
if (!projectWatches.containsKey(key)) {
projectWatches.put(key, EnumSet.noneOf(NotifyType.class));
}
diff --git a/java/com/google/gerrit/server/account/Realm.java b/java/com/google/gerrit/server/account/Realm.java
index 4e8cf09713..798b4e89d6 100644
--- a/java/com/google/gerrit/server/account/Realm.java
+++ b/java/com/google/gerrit/server/account/Realm.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.externalids.ExternalId;
import java.io.IOException;
diff --git a/java/com/google/gerrit/server/account/SetInactiveFlag.java b/java/com/google/gerrit/server/account/SetInactiveFlag.java
index da2d640b01..40cc185ff4 100644
--- a/java/com/google/gerrit/server/account/SetInactiveFlag.java
+++ b/java/com/google/gerrit/server/account/SetInactiveFlag.java
@@ -14,11 +14,12 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.extensions.events.AccountActivationListener;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.validators.AccountActivationValidationListener;
@@ -37,13 +38,16 @@ public class SetInactiveFlag {
private final PluginSetContext<AccountActivationValidationListener>
accountActivationValidationListeners;
private final Provider<AccountsUpdate> accountsUpdateProvider;
+ private final PluginSetContext<AccountActivationListener> accountActivationListeners;
@Inject
SetInactiveFlag(
PluginSetContext<AccountActivationValidationListener> accountActivationValidationListeners,
- @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider) {
+ @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider,
+ PluginSetContext<AccountActivationListener> accountActivationListeners) {
this.accountActivationValidationListeners = accountActivationValidationListeners;
this.accountsUpdateProvider = accountsUpdateProvider;
+ this.accountActivationListeners = accountActivationListeners;
}
public Response<?> deactivate(Account.Id accountId)
@@ -56,7 +60,7 @@ public class SetInactiveFlag {
"Deactivate Account via API",
accountId,
(a, u) -> {
- if (!a.getAccount().isActive()) {
+ if (!a.account().isActive()) {
alreadyInactive.set(true);
} else {
try {
@@ -76,6 +80,12 @@ public class SetInactiveFlag {
if (alreadyInactive.get()) {
throw new ResourceConflictException("account not active");
}
+
+ // At this point the account got set inactive and no errors occurred
+
+ int id = accountId.get();
+ accountActivationListeners.runEach(l -> l.onAccountDeactivated(id));
+
return Response.none();
}
@@ -89,7 +99,7 @@ public class SetInactiveFlag {
"Activate Account via API",
accountId,
(a, u) -> {
- if (a.getAccount().isActive()) {
+ if (a.account().isActive()) {
alreadyActive.set(true);
} else {
try {
@@ -106,6 +116,16 @@ public class SetInactiveFlag {
if (exception.get().isPresent()) {
throw exception.get().get();
}
- return alreadyActive.get() ? Response.ok("") : Response.created("");
+
+ Response<String> res;
+ if (alreadyActive.get()) {
+ res = Response.ok("");
+ } else {
+ res = Response.created("");
+
+ int id = accountId.get();
+ accountActivationListeners.runEach(l -> l.onAccountActivated(id));
+ }
+ return res;
}
}
diff --git a/java/com/google/gerrit/server/account/StoredPreferences.java b/java/com/google/gerrit/server/account/StoredPreferences.java
new file mode 100644
index 0000000000..0e8eb04af1
--- /dev/null
+++ b/java/com/google/gerrit/server/account/StoredPreferences.java
@@ -0,0 +1,574 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.config.ConfigUtil.loadSection;
+import static com.google.gerrit.server.config.ConfigUtil.skipField;
+import static com.google.gerrit.server.config.ConfigUtil.storeSection;
+import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE;
+import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
+import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
+import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
+import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.EditPreferencesInfo;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.extensions.client.MenuItem;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.UserConfigSections;
+import com.google.gerrit.server.git.ValidationError;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.git.meta.VersionedMetaData;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Parses/writes preferences from/to a {@link Config} file.
+ *
+ * <p>This is a low-level API. Read/write of preferences in a user branch should be done through
+ * {@link AccountsUpdate} or {@link AccountConfig}.
+ *
+ * <p>The config file has separate sections for general, diff and edit preferences:
+ *
+ * <pre>
+ * [diff]
+ * hideTopMenu = true
+ * [edit]
+ * lineLength = 80
+ * </pre>
+ *
+ * <p>The parameter names match the names that are used in the preferences REST API.
+ *
+ * <p>If the preference is omitted in the config file, then the default value for the preference is
+ * used.
+ *
+ * <p>Defaults for preferences that apply for all accounts can be configured in the {@code
+ * refs/users/default} branch in the {@code All-Users} repository. The config for the default
+ * preferences must be provided to this class so that it can read default values from it.
+ *
+ * <p>The preferences are lazily parsed.
+ */
+public class StoredPreferences {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ public static final String PREFERENCES_CONFIG = "preferences.config";
+
+ private final Account.Id accountId;
+ private final Config cfg;
+ private final Config defaultCfg;
+ private final ValidationError.Sink validationErrorSink;
+
+ private GeneralPreferencesInfo generalPreferences;
+ private DiffPreferencesInfo diffPreferences;
+ private EditPreferencesInfo editPreferences;
+
+ StoredPreferences(
+ Account.Id accountId,
+ Config cfg,
+ Config defaultCfg,
+ ValidationError.Sink validationErrorSink) {
+ this.accountId = requireNonNull(accountId, "accountId");
+ this.cfg = requireNonNull(cfg, "cfg");
+ this.defaultCfg = requireNonNull(defaultCfg, "defaultCfg");
+ this.validationErrorSink = requireNonNull(validationErrorSink, "validationErrorSink");
+ }
+
+ public GeneralPreferencesInfo getGeneralPreferences() {
+ if (generalPreferences == null) {
+ parse();
+ }
+ return generalPreferences;
+ }
+
+ public DiffPreferencesInfo getDiffPreferences() {
+ if (diffPreferences == null) {
+ parse();
+ }
+ return diffPreferences;
+ }
+
+ public EditPreferencesInfo getEditPreferences() {
+ if (editPreferences == null) {
+ parse();
+ }
+ return editPreferences;
+ }
+
+ public void parse() {
+ generalPreferences = parseGeneralPreferences(null);
+ diffPreferences = parseDiffPreferences(null);
+ editPreferences = parseEditPreferences(null);
+ }
+
+ public Config saveGeneralPreferences(
+ Optional<GeneralPreferencesInfo> generalPreferencesInput,
+ Optional<DiffPreferencesInfo> diffPreferencesInput,
+ Optional<EditPreferencesInfo> editPreferencesInput)
+ throws ConfigInvalidException {
+ if (generalPreferencesInput.isPresent()) {
+ GeneralPreferencesInfo mergedGeneralPreferencesInput =
+ parseGeneralPreferences(generalPreferencesInput.get());
+
+ storeSection(
+ cfg,
+ UserConfigSections.GENERAL,
+ null,
+ mergedGeneralPreferencesInput,
+ parseDefaultGeneralPreferences(defaultCfg, null));
+ setChangeTable(cfg, mergedGeneralPreferencesInput.changeTable);
+ setMy(cfg, mergedGeneralPreferencesInput.my);
+
+ // evict the cached general preferences
+ this.generalPreferences = null;
+ }
+
+ if (diffPreferencesInput.isPresent()) {
+ DiffPreferencesInfo mergedDiffPreferencesInput =
+ parseDiffPreferences(diffPreferencesInput.get());
+
+ storeSection(
+ cfg,
+ UserConfigSections.DIFF,
+ null,
+ mergedDiffPreferencesInput,
+ parseDefaultDiffPreferences(defaultCfg, null));
+
+ // evict the cached diff preferences
+ this.diffPreferences = null;
+ }
+
+ if (editPreferencesInput.isPresent()) {
+ EditPreferencesInfo mergedEditPreferencesInput =
+ parseEditPreferences(editPreferencesInput.get());
+
+ storeSection(
+ cfg,
+ UserConfigSections.EDIT,
+ null,
+ mergedEditPreferencesInput,
+ parseDefaultEditPreferences(defaultCfg, null));
+
+ // evict the cached edit preferences
+ this.editPreferences = null;
+ }
+
+ return cfg;
+ }
+
+ private GeneralPreferencesInfo parseGeneralPreferences(@Nullable GeneralPreferencesInfo input) {
+ try {
+ return parseGeneralPreferences(cfg, defaultCfg, input);
+ } catch (ConfigInvalidException e) {
+ validationErrorSink.error(
+ new ValidationError(
+ PREFERENCES_CONFIG,
+ String.format(
+ "Invalid general preferences for account %d: %s",
+ accountId.get(), e.getMessage())));
+ return new GeneralPreferencesInfo();
+ }
+ }
+
+ private DiffPreferencesInfo parseDiffPreferences(@Nullable DiffPreferencesInfo input) {
+ try {
+ return parseDiffPreferences(cfg, defaultCfg, input);
+ } catch (ConfigInvalidException e) {
+ validationErrorSink.error(
+ new ValidationError(
+ PREFERENCES_CONFIG,
+ String.format(
+ "Invalid diff preferences for account %d: %s", accountId.get(), e.getMessage())));
+ return new DiffPreferencesInfo();
+ }
+ }
+
+ private EditPreferencesInfo parseEditPreferences(@Nullable EditPreferencesInfo input) {
+ try {
+ return parseEditPreferences(cfg, defaultCfg, input);
+ } catch (ConfigInvalidException e) {
+ validationErrorSink.error(
+ new ValidationError(
+ PREFERENCES_CONFIG,
+ String.format(
+ "Invalid edit preferences for account %d: %s", accountId.get(), e.getMessage())));
+ return new EditPreferencesInfo();
+ }
+ }
+
+ private static GeneralPreferencesInfo parseGeneralPreferences(
+ Config cfg, @Nullable Config defaultCfg, @Nullable GeneralPreferencesInfo input)
+ throws ConfigInvalidException {
+ GeneralPreferencesInfo r =
+ loadSection(
+ cfg,
+ UserConfigSections.GENERAL,
+ null,
+ new GeneralPreferencesInfo(),
+ defaultCfg != null
+ ? parseDefaultGeneralPreferences(defaultCfg, input)
+ : GeneralPreferencesInfo.defaults(),
+ input);
+ if (input != null) {
+ r.changeTable = input.changeTable;
+ r.my = input.my;
+ } else {
+ r.changeTable = parseChangeTableColumns(cfg, defaultCfg);
+ r.my = parseMyMenus(cfg, defaultCfg);
+ }
+ return r;
+ }
+
+ private static DiffPreferencesInfo parseDiffPreferences(
+ Config cfg, @Nullable Config defaultCfg, @Nullable DiffPreferencesInfo input)
+ throws ConfigInvalidException {
+ return loadSection(
+ cfg,
+ UserConfigSections.DIFF,
+ null,
+ new DiffPreferencesInfo(),
+ defaultCfg != null
+ ? parseDefaultDiffPreferences(defaultCfg, input)
+ : DiffPreferencesInfo.defaults(),
+ input);
+ }
+
+ private static EditPreferencesInfo parseEditPreferences(
+ Config cfg, @Nullable Config defaultCfg, @Nullable EditPreferencesInfo input)
+ throws ConfigInvalidException {
+ return loadSection(
+ cfg,
+ UserConfigSections.EDIT,
+ null,
+ new EditPreferencesInfo(),
+ defaultCfg != null
+ ? parseDefaultEditPreferences(defaultCfg, input)
+ : EditPreferencesInfo.defaults(),
+ input);
+ }
+
+ private static GeneralPreferencesInfo parseDefaultGeneralPreferences(
+ Config defaultCfg, GeneralPreferencesInfo input) throws ConfigInvalidException {
+ GeneralPreferencesInfo allUserPrefs = new GeneralPreferencesInfo();
+ loadSection(
+ defaultCfg,
+ UserConfigSections.GENERAL,
+ null,
+ allUserPrefs,
+ GeneralPreferencesInfo.defaults(),
+ input);
+ return updateGeneralPreferencesDefaults(allUserPrefs);
+ }
+
+ private static DiffPreferencesInfo parseDefaultDiffPreferences(
+ Config defaultCfg, DiffPreferencesInfo input) throws ConfigInvalidException {
+ DiffPreferencesInfo allUserPrefs = new DiffPreferencesInfo();
+ loadSection(
+ defaultCfg,
+ UserConfigSections.DIFF,
+ null,
+ allUserPrefs,
+ DiffPreferencesInfo.defaults(),
+ input);
+ return updateDiffPreferencesDefaults(allUserPrefs);
+ }
+
+ private static EditPreferencesInfo parseDefaultEditPreferences(
+ Config defaultCfg, EditPreferencesInfo input) throws ConfigInvalidException {
+ EditPreferencesInfo allUserPrefs = new EditPreferencesInfo();
+ loadSection(
+ defaultCfg,
+ UserConfigSections.EDIT,
+ null,
+ allUserPrefs,
+ EditPreferencesInfo.defaults(),
+ input);
+ return updateEditPreferencesDefaults(allUserPrefs);
+ }
+
+ private static GeneralPreferencesInfo updateGeneralPreferencesDefaults(
+ GeneralPreferencesInfo input) {
+ GeneralPreferencesInfo result = GeneralPreferencesInfo.defaults();
+ try {
+ for (Field field : input.getClass().getDeclaredFields()) {
+ if (skipField(field)) {
+ continue;
+ }
+ Object newVal = field.get(input);
+ if (newVal != null) {
+ field.set(result, newVal);
+ }
+ }
+ } catch (IllegalAccessException e) {
+ logger.atSevere().withCause(e).log("Failed to apply default general preferences");
+ return GeneralPreferencesInfo.defaults();
+ }
+ return result;
+ }
+
+ private static DiffPreferencesInfo updateDiffPreferencesDefaults(DiffPreferencesInfo input) {
+ DiffPreferencesInfo result = DiffPreferencesInfo.defaults();
+ try {
+ for (Field field : input.getClass().getDeclaredFields()) {
+ if (skipField(field)) {
+ continue;
+ }
+ Object newVal = field.get(input);
+ if (newVal != null) {
+ field.set(result, newVal);
+ }
+ }
+ } catch (IllegalAccessException e) {
+ logger.atSevere().withCause(e).log("Failed to apply default diff preferences");
+ return DiffPreferencesInfo.defaults();
+ }
+ return result;
+ }
+
+ private static EditPreferencesInfo updateEditPreferencesDefaults(EditPreferencesInfo input) {
+ EditPreferencesInfo result = EditPreferencesInfo.defaults();
+ try {
+ for (Field field : input.getClass().getDeclaredFields()) {
+ if (skipField(field)) {
+ continue;
+ }
+ Object newVal = field.get(input);
+ if (newVal != null) {
+ field.set(result, newVal);
+ }
+ }
+ } catch (IllegalAccessException e) {
+ logger.atSevere().withCause(e).log("Failed to apply default edit preferences");
+ return EditPreferencesInfo.defaults();
+ }
+ return result;
+ }
+
+ private static List<String> parseChangeTableColumns(Config cfg, @Nullable Config defaultCfg) {
+ List<String> changeTable = changeTable(cfg);
+ if (changeTable == null && defaultCfg != null) {
+ changeTable = changeTable(defaultCfg);
+ }
+ return changeTable;
+ }
+
+ private static List<MenuItem> parseMyMenus(Config cfg, @Nullable Config defaultCfg) {
+ List<MenuItem> my = my(cfg);
+ if (my.isEmpty() && defaultCfg != null) {
+ my = my(defaultCfg);
+ }
+ if (my.isEmpty()) {
+ my.add(new MenuItem("Changes", "#/dashboard/self", null));
+ my.add(new MenuItem("Draft Comments", "#/q/has:draft", null));
+ my.add(new MenuItem("Edits", "#/q/has:edit", null));
+ my.add(new MenuItem("Watched Changes", "#/q/is:watched+is:open", null));
+ my.add(new MenuItem("Starred Changes", "#/q/is:starred", null));
+ my.add(new MenuItem("Groups", "#/settings/#Groups", null));
+ }
+ return my;
+ }
+
+ public static GeneralPreferencesInfo readDefaultGeneralPreferences(
+ AllUsersName allUsersName, Repository allUsersRepo)
+ throws IOException, ConfigInvalidException {
+ return parseGeneralPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
+ }
+
+ public static DiffPreferencesInfo readDefaultDiffPreferences(
+ AllUsersName allUsersName, Repository allUsersRepo)
+ throws IOException, ConfigInvalidException {
+ return parseDiffPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
+ }
+
+ public static EditPreferencesInfo readDefaultEditPreferences(
+ AllUsersName allUsersName, Repository allUsersRepo)
+ throws IOException, ConfigInvalidException {
+ return parseEditPreferences(readDefaultConfig(allUsersName, allUsersRepo), null, null);
+ }
+
+ static Config readDefaultConfig(AllUsersName allUsersName, Repository allUsersRepo)
+ throws IOException, ConfigInvalidException {
+ VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
+ defaultPrefs.load(allUsersName, allUsersRepo);
+ return defaultPrefs.getConfig();
+ }
+
+ public static GeneralPreferencesInfo updateDefaultGeneralPreferences(
+ MetaDataUpdate md, GeneralPreferencesInfo input) throws IOException, ConfigInvalidException {
+ VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
+ defaultPrefs.load(md);
+ storeSection(
+ defaultPrefs.getConfig(),
+ UserConfigSections.GENERAL,
+ null,
+ input,
+ GeneralPreferencesInfo.defaults());
+ setMy(defaultPrefs.getConfig(), input.my);
+ setChangeTable(defaultPrefs.getConfig(), input.changeTable);
+ defaultPrefs.commit(md);
+
+ return parseGeneralPreferences(defaultPrefs.getConfig(), null, null);
+ }
+
+ public static DiffPreferencesInfo updateDefaultDiffPreferences(
+ MetaDataUpdate md, DiffPreferencesInfo input) throws IOException, ConfigInvalidException {
+ VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
+ defaultPrefs.load(md);
+ storeSection(
+ defaultPrefs.getConfig(),
+ UserConfigSections.DIFF,
+ null,
+ input,
+ DiffPreferencesInfo.defaults());
+ defaultPrefs.commit(md);
+
+ return parseDiffPreferences(defaultPrefs.getConfig(), null, null);
+ }
+
+ public static EditPreferencesInfo updateDefaultEditPreferences(
+ MetaDataUpdate md, EditPreferencesInfo input) throws IOException, ConfigInvalidException {
+ VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
+ defaultPrefs.load(md);
+ storeSection(
+ defaultPrefs.getConfig(),
+ UserConfigSections.EDIT,
+ null,
+ input,
+ EditPreferencesInfo.defaults());
+ defaultPrefs.commit(md);
+
+ return parseEditPreferences(defaultPrefs.getConfig(), null, null);
+ }
+
+ private static List<String> changeTable(Config cfg) {
+ return Lists.newArrayList(cfg.getStringList(CHANGE_TABLE, null, CHANGE_TABLE_COLUMN));
+ }
+
+ private static void setChangeTable(Config cfg, List<String> changeTable) {
+ if (changeTable != null) {
+ unsetSection(cfg, UserConfigSections.CHANGE_TABLE);
+ cfg.setStringList(UserConfigSections.CHANGE_TABLE, null, CHANGE_TABLE_COLUMN, changeTable);
+ }
+ }
+
+ private static List<MenuItem> my(Config cfg) {
+ List<MenuItem> my = new ArrayList<>();
+ for (String subsection : cfg.getSubsections(UserConfigSections.MY)) {
+ String url = my(cfg, subsection, KEY_URL, "#/");
+ String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank");
+ my.add(new MenuItem(subsection, url, target, my(cfg, subsection, KEY_ID, null)));
+ }
+ return my;
+ }
+
+ private static String my(Config cfg, String subsection, String key, String defaultValue) {
+ String val = cfg.getString(UserConfigSections.MY, subsection, key);
+ return !Strings.isNullOrEmpty(val) ? val : defaultValue;
+ }
+
+ private static void setMy(Config cfg, List<MenuItem> my) {
+ if (my != null) {
+ unsetSection(cfg, UserConfigSections.MY);
+ for (MenuItem item : my) {
+ checkState(!isNullOrEmpty(item.name), "MenuItem.name must not be null or empty");
+ checkState(!isNullOrEmpty(item.url), "MenuItem.url must not be null or empty");
+
+ setMy(cfg, item.name, KEY_URL, item.url);
+ setMy(cfg, item.name, KEY_TARGET, item.target);
+ setMy(cfg, item.name, KEY_ID, item.id);
+ }
+ }
+ }
+
+ public static void validateMy(List<MenuItem> my) throws BadRequestException {
+ if (my == null) {
+ return;
+ }
+ for (MenuItem item : my) {
+ checkRequiredMenuItemField(item.name, "name");
+ checkRequiredMenuItemField(item.url, "URL");
+ }
+ }
+
+ private static void checkRequiredMenuItemField(String value, String name)
+ throws BadRequestException {
+ if (isNullOrEmpty(value)) {
+ throw new BadRequestException(name + " for menu item is required");
+ }
+ }
+
+ private static boolean isNullOrEmpty(String value) {
+ return value == null || value.trim().isEmpty();
+ }
+
+ private static void setMy(Config cfg, String section, String key, @Nullable String val) {
+ if (val == null || val.trim().isEmpty()) {
+ cfg.unset(UserConfigSections.MY, section.trim(), key);
+ } else {
+ cfg.setString(UserConfigSections.MY, section.trim(), key, val.trim());
+ }
+ }
+
+ private static void unsetSection(Config cfg, String section) {
+ cfg.unsetSection(section, null);
+ for (String subsection : cfg.getSubsections(section)) {
+ cfg.unsetSection(section, subsection);
+ }
+ }
+
+ private static class VersionedDefaultPreferences extends VersionedMetaData {
+ private Config cfg;
+
+ @Override
+ protected String getRefName() {
+ return RefNames.REFS_USERS_DEFAULT;
+ }
+
+ private Config getConfig() {
+ checkState(cfg != null, "Default preferences not loaded yet.");
+ return cfg;
+ }
+
+ @Override
+ protected void onLoad() throws IOException, ConfigInvalidException {
+ cfg = readConfig(PREFERENCES_CONFIG);
+ }
+
+ @Override
+ protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
+ if (Strings.isNullOrEmpty(commit.getMessage())) {
+ commit.setMessage("Update default preferences\n");
+ }
+ saveConfig(PREFERENCES_CONFIG, cfg);
+ return true;
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/account/UniversalGroupBackend.java b/java/com/google/gerrit/server/account/UniversalGroupBackend.java
index ecd7468632..fddbd2ba01 100644
--- a/java/com/google/gerrit/server/account/UniversalGroupBackend.java
+++ b/java/com/google/gerrit/server/account/UniversalGroupBackend.java
@@ -26,7 +26,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.StartupCheck;
import com.google.gerrit.server.StartupException;
@@ -220,7 +220,7 @@ public class UniversalGroupBackend implements GroupBackend {
cfg.getSubsections("groups").stream()
.filter(
sub -> {
- AccountGroup.UUID uuid = new AccountGroup.UUID(sub);
+ AccountGroup.UUID uuid = AccountGroup.uuid(sub);
GroupBackend groupBackend = universalGroupBackend.backend(uuid);
return groupBackend == null || groupBackend.get(uuid) == null;
})
diff --git a/java/com/google/gerrit/server/account/VersionedAccountDestinations.java b/java/com/google/gerrit/server/account/VersionedAccountDestinations.java
index e2f1bc222c..f1cf9fe6db 100644
--- a/java/com/google/gerrit/server/account/VersionedAccountDestinations.java
+++ b/java/com/google/gerrit/server/account/VersionedAccountDestinations.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.account;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
diff --git a/java/com/google/gerrit/server/account/VersionedAccountQueries.java b/java/com/google/gerrit/server/account/VersionedAccountQueries.java
index daf7100568..e2ffe9bde7 100644
--- a/java/com/google/gerrit/server/account/VersionedAccountQueries.java
+++ b/java/com/google/gerrit/server/account/VersionedAccountQueries.java
@@ -14,11 +14,17 @@
package com.google.gerrit.server.account;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -46,6 +52,16 @@ public class VersionedAccountQueries extends VersionedMetaData {
return queryList;
}
+ public void setQueryList(String text) throws IOException, ConfigInvalidException {
+ List<ValidationError> errors = new ArrayList<>();
+ QueryList newQueryList = QueryList.parse(text, error -> errors.add(error));
+ if (!errors.isEmpty()) {
+ String messages = errors.stream().map(ValidationError::getMessage).collect(joining(", "));
+ throw new ConfigInvalidException("Invalid named queries: " + messages);
+ }
+ queryList = newQueryList;
+ }
+
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
queryList =
@@ -58,6 +74,10 @@ public class VersionedAccountQueries extends VersionedMetaData {
@Override
protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
- throw new UnsupportedOperationException("Cannot yet save named queries");
+ if (Strings.isNullOrEmpty(commit.getMessage())) {
+ commit.setMessage("Updated named queries\n");
+ }
+ saveUTF8(QueryList.FILE_NAME, queryList.asText());
+ return true;
}
}
diff --git a/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java b/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
index c7808de9b5..235537c716 100644
--- a/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
+++ b/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
@@ -20,9 +20,9 @@ import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
import com.google.common.collect.Ordering;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.InvalidSshKeyException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
diff --git a/java/com/google/gerrit/server/account/externalids/AllExternalIds.java b/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
index 5d12ae101b..4da2a9eff3 100644
--- a/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
+++ b/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
@@ -21,8 +21,8 @@ import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.cache.proto.Cache.AllExternalIdsProto;
import com.google.gerrit.server.cache.proto.Cache.AllExternalIdsProto.ExternalIdProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
@@ -96,7 +96,7 @@ public abstract class AllExternalIds {
private static ExternalId toExternalId(ObjectIdConverter idConverter, ExternalIdProto proto) {
return ExternalId.create(
ExternalId.Key.parse(proto.getKey()),
- new Account.Id(proto.getAccountId()),
+ Account.id(proto.getAccountId()),
// ExternalId treats null and empty strings the same, so no need to distinguish here.
proto.getEmail(),
proto.getPassword(),
diff --git a/java/com/google/gerrit/server/account/externalids/DisabledExternalIdCache.java b/java/com/google/gerrit/server/account/externalids/DisabledExternalIdCache.java
index 589405129b..e1e9c70842 100644
--- a/java/com/google/gerrit/server/account/externalids/DisabledExternalIdCache.java
+++ b/java/com/google/gerrit/server/account/externalids/DisabledExternalIdCache.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.account.externalids;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import java.io.IOException;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalId.java b/java/com/google/gerrit/server/account/externalids/ExternalId.java
index ab8271493e..2d501ad37f 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -27,8 +27,9 @@ import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AuthType;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.server.account.HashedPassword;
import java.io.Serializable;
import java.util.Collection;
@@ -39,7 +40,6 @@ import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@AutoValue
@@ -364,7 +364,7 @@ public abstract class ExternalId implements Serializable {
return create(
externalIdKey,
- new Account.Id(accountId),
+ Account.id(accountId),
Strings.emptyToNull(email),
Strings.emptyToNull(password),
blobId);
@@ -432,10 +432,10 @@ public abstract class ExternalId implements Serializable {
public byte[] toByteArray() {
checkState(blobId() != null, "Missing blobId in external ID %s", key().get());
- byte[] b = new byte[2 * Constants.OBJECT_ID_STRING_LENGTH + 1];
+ byte[] b = new byte[2 * ObjectIds.STR_LEN + 1];
key().sha1().copyTo(b, 0);
- b[Constants.OBJECT_ID_STRING_LENGTH] = ':';
- blobId().copyTo(b, Constants.OBJECT_ID_STRING_LENGTH + 1);
+ b[ObjectIds.STR_LEN] = ':';
+ blobId().copyTo(b, ObjectIds.STR_LEN + 1);
return b;
}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdCache.java b/java/com/google/gerrit/server/account/externalids/ExternalIdCache.java
index 1ac737e2a9..0edf15481d 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdCache.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdCache.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.account.externalids;
import com.google.common.collect.SetMultimap;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java
index 5aa19d88f5..84b25c0167 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java
@@ -14,16 +14,12 @@
package com.google.gerrit.server.account.externalids;
-import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.logging.TraceContext;
-import com.google.gerrit.server.logging.TraceContext.TraceTimer;
+import com.google.gerrit.entities.Account;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
@@ -146,23 +142,4 @@ class ExternalIdCacheImpl implements ExternalIdCache {
lock.unlock();
}
}
-
- static class Loader extends CacheLoader<ObjectId, AllExternalIds> {
- private final ExternalIdReader externalIdReader;
-
- @Inject
- Loader(ExternalIdReader externalIdReader) {
- this.externalIdReader = externalIdReader;
- }
-
- @Override
- public AllExternalIds load(ObjectId notesRev) throws Exception {
- try (TraceTimer timer =
- TraceContext.newTimer("Loading external IDs (revision=%s)", notesRev)) {
- ImmutableSet<ExternalId> externalIds = externalIdReader.all(notesRev);
- externalIds.forEach(ExternalId::checkThatBlobIdIsSet);
- return AllExternalIds.create(externalIds);
- }
- }
- }
}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java
new file mode 100644
index 0000000000..8887e064cd
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java
@@ -0,0 +1,277 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.metrics.Counter1;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Description.Units;
+import com.google.gerrit.metrics.Field;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer0;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+
+/** Loads cache values for the external ID cache using either a full or a partial reload. */
+@Singleton
+public class ExternalIdCacheLoader extends CacheLoader<ObjectId, AllExternalIds> {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ // Maximum number of prior states we inspect to find a base for differential. If no cached state
+ // is found within this number of parents, we fall back to reading everything from scratch.
+ private static final int MAX_HISTORY_LOOKBACK = 10;
+
+ private final ExternalIdReader externalIdReader;
+ private final Provider<Cache<ObjectId, AllExternalIds>> externalIdCache;
+ private final GitRepositoryManager gitRepositoryManager;
+ private final AllUsersName allUsersName;
+ private final Counter1<Boolean> reloadCounter;
+ private final Timer0 reloadDifferential;
+ private final boolean enablePartialReloads;
+ private final boolean isPersistentCache;
+
+ @Inject
+ ExternalIdCacheLoader(
+ GitRepositoryManager gitRepositoryManager,
+ AllUsersName allUsersName,
+ ExternalIdReader externalIdReader,
+ @Named(ExternalIdCacheImpl.CACHE_NAME)
+ Provider<Cache<ObjectId, AllExternalIds>> externalIdCache,
+ MetricMaker metricMaker,
+ @GerritServerConfig Config config) {
+ this.externalIdReader = externalIdReader;
+ this.externalIdCache = externalIdCache;
+ this.gitRepositoryManager = gitRepositoryManager;
+ this.allUsersName = allUsersName;
+ this.reloadCounter =
+ metricMaker.newCounter(
+ "notedb/external_id_cache_load_count",
+ new Description("Total number of external ID cache reloads from Git.")
+ .setRate()
+ .setUnit("updates"),
+ Field.ofBoolean("partial", Metadata.Builder::partial).build());
+ this.reloadDifferential =
+ metricMaker.newTimer(
+ "notedb/external_id_partial_read_latency",
+ new Description(
+ "Latency for generating a new external ID cache state from a prior state.")
+ .setCumulative()
+ .setUnit(Units.MILLISECONDS));
+ this.enablePartialReloads =
+ config.getBoolean("cache", ExternalIdCacheImpl.CACHE_NAME, "enablePartialReloads", true);
+ this.isPersistentCache =
+ config.getInt("cache", ExternalIdCacheImpl.CACHE_NAME, "diskLimit", 0) > 0;
+ }
+
+ @Override
+ public AllExternalIds load(ObjectId notesRev) throws IOException, ConfigInvalidException {
+ if (!enablePartialReloads) {
+ logger.atInfo().log(
+ "Partial reloads of "
+ + ExternalIdCacheImpl.CACHE_NAME
+ + " disabled. Falling back to full reload.");
+ return reloadAllExternalIds(notesRev);
+ }
+
+ // The requested value was not in the cache (hence, this loader was invoked). Therefore, try to
+ // create this entry from a past value using the minimal amount of Git operations possible to
+ // reduce latency.
+ //
+ // First, try to find the most recent state we have in the cache. Most of the time, this will be
+ // the state before the last update happened, but it can also date further back. We try a best
+ // effort approach and check the last 10 states. If nothing is found, we default to loading the
+ // value from scratch.
+ //
+ // If a prior state was found, we use Git to diff the trees and find modifications. This is
+ // faster than just loading the complete current tree and working off of that because of how the
+ // data is structured: NotesMaps use nested trees, so, for example, a NotesMap with 200k entries
+ // has two layers of nesting: 12/34/1234..99. TreeWalk is smart in skipping the traversal of
+ // identical subtrees.
+ //
+ // Once we know what files changed, we apply additions and removals to the previously cached
+ // state.
+
+ try (Repository repo = gitRepositoryManager.openRepository(allUsersName);
+ RevWalk rw = new RevWalk(repo)) {
+ long start = System.nanoTime();
+ Ref extIdRef = repo.exactRef(RefNames.REFS_EXTERNAL_IDS);
+ if (extIdRef == null) {
+ logger.atInfo().log(
+ RefNames.REFS_EXTERNAL_IDS + " not initialized, falling back to full reload.");
+ return reloadAllExternalIds(notesRev);
+ }
+
+ RevCommit currentCommit = rw.parseCommit(extIdRef.getObjectId());
+ rw.markStart(currentCommit);
+ RevCommit parentWithCacheValue;
+ AllExternalIds oldExternalIds = null;
+ int i = 0;
+ while ((parentWithCacheValue = rw.next()) != null
+ && i++ < MAX_HISTORY_LOOKBACK
+ && parentWithCacheValue.getParentCount() < 2) {
+ oldExternalIds = externalIdCache.get().getIfPresent(parentWithCacheValue.getId());
+ if (oldExternalIds != null) {
+ // We found a previously cached state.
+ break;
+ }
+ }
+ if (oldExternalIds == null) {
+ if (isPersistentCache) {
+ // If there is no persistence, this is normal. Don't upset admins reading the logs.
+ logger.atWarning().log(
+ "Unable to find an old ExternalId cache state, falling back to full reload");
+ }
+ return reloadAllExternalIds(notesRev);
+ }
+
+ // Diff trees to recognize modifications
+ Set<ObjectId> removals = new HashSet<>(); // Set<Blob-Object-Id>
+ Map<ObjectId, ObjectId> additions = new HashMap<>(); // Map<Name-ObjectId, Blob-Object-Id>
+ try (TreeWalk treeWalk = new TreeWalk(repo)) {
+ treeWalk.setFilter(TreeFilter.ANY_DIFF);
+ treeWalk.setRecursive(true);
+ treeWalk.reset(parentWithCacheValue.getTree(), currentCommit.getTree());
+ while (treeWalk.next()) {
+ String path = treeWalk.getPathString();
+ ObjectId oldBlob = treeWalk.getObjectId(0);
+ ObjectId newBlob = treeWalk.getObjectId(1);
+ if (ObjectId.zeroId().equals(newBlob)) {
+ // Deletion
+ removals.add(oldBlob);
+ } else if (ObjectId.zeroId().equals(oldBlob)) {
+ // Addition
+ additions.put(fileNameToObjectId(path), newBlob);
+ } else {
+ // Modification
+ removals.add(oldBlob);
+ additions.put(fileNameToObjectId(path), newBlob);
+ }
+ }
+ }
+
+ AllExternalIds allExternalIds =
+ buildAllExternalIds(repo, oldExternalIds, additions, removals);
+ reloadCounter.increment(true);
+ reloadDifferential.record(System.nanoTime() - start, TimeUnit.NANOSECONDS);
+ return allExternalIds;
+ }
+ }
+
+ private static ObjectId fileNameToObjectId(String path) {
+ return ObjectId.fromString(CharMatcher.is('/').removeFrom(path));
+ }
+
+ /**
+ * Build a new {@link AllExternalIds} from an old state by applying additions and removals that
+ * were performed since then.
+ *
+ * <p>Removals are applied before additions.
+ *
+ * @param repo open repository
+ * @param oldExternalIds prior state that is used as base
+ * @param additions map of name to blob ID for each external ID that should be added
+ * @param removals set of name {@link ObjectId}s that should be removed
+ */
+ private static AllExternalIds buildAllExternalIds(
+ Repository repo,
+ AllExternalIds oldExternalIds,
+ Map<ObjectId, ObjectId> additions,
+ Set<ObjectId> removals)
+ throws IOException {
+ ImmutableSetMultimap.Builder<Account.Id, ExternalId> byAccount = ImmutableSetMultimap.builder();
+ ImmutableSetMultimap.Builder<String, ExternalId> byEmail = ImmutableSetMultimap.builder();
+
+ // Copy over old ExternalIds but exclude deleted ones
+ for (ExternalId externalId : oldExternalIds.byAccount().values()) {
+ if (removals.contains(externalId.blobId())) {
+ continue;
+ }
+
+ byAccount.put(externalId.accountId(), externalId);
+ if (externalId.email() != null) {
+ byEmail.put(externalId.email(), externalId);
+ }
+ }
+
+ // Add newly discovered ExternalIds
+ try (ObjectReader reader = repo.newObjectReader()) {
+ for (Map.Entry<ObjectId, ObjectId> nameToBlob : additions.entrySet()) {
+ ExternalId parsedExternalId;
+ try {
+ parsedExternalId =
+ ExternalId.parse(
+ nameToBlob.getKey().name(),
+ reader.open(nameToBlob.getValue()).getCachedBytes(),
+ nameToBlob.getValue());
+ } catch (ConfigInvalidException | RuntimeException e) {
+ logger.atSevere().withCause(e).log(
+ "Ignoring invalid external ID note %s", nameToBlob.getKey().name());
+ continue;
+ }
+
+ byAccount.put(parsedExternalId.accountId(), parsedExternalId);
+ if (parsedExternalId.email() != null) {
+ byEmail.put(parsedExternalId.email(), parsedExternalId);
+ }
+ }
+ }
+ return new AutoValue_AllExternalIds(byAccount.build(), byEmail.build());
+ }
+
+ private AllExternalIds reloadAllExternalIds(ObjectId notesRev)
+ throws IOException, ConfigInvalidException {
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Loading external IDs from scratch",
+ Metadata.builder().revision(notesRev.name()).build())) {
+ ImmutableSet<ExternalId> externalIds = externalIdReader.all(notesRev);
+ externalIds.forEach(ExternalId::checkThatBlobIdIsSet);
+ AllExternalIds allExternalIds = AllExternalIds.create(externalIds);
+ reloadCounter.increment(false);
+ return allExternalIds;
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdModule.java b/java/com/google/gerrit/server/account/externalids/ExternalIdModule.java
index fc311e7aab..3e5d7b8340 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdModule.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdModule.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.account.externalids;
-import com.google.gerrit.server.account.externalids.ExternalIdCacheImpl.Loader;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.serialize.ObjectIdCacheSerializer;
import com.google.inject.TypeLiteral;
@@ -31,9 +30,12 @@ public class ExternalIdModule extends CacheModule {
// from the cache. Extend the cache size by 1 to cover this case, but expire the extra
// object after a short period of time, since it may be a potentially large amount of
// memory.
+ // When loading a new value because the primary data advanced, we want to leverage the old
+ // cache state to recompute only what changed. This doesn't affect cache size though as
+ // Guava calls the loader first and evicts later on.
.maximumWeight(2)
.expireFromMemoryAfterAccess(Duration.ofMinutes(1))
- .loader(Loader.class)
+ .loader(ExternalIdCacheLoader.class)
.diskLimit(-1)
.version(1)
.keySerializer(ObjectIdCacheSerializer.INSTANCE)
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
index 9c35232acc..ec4f55344d 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
@@ -28,17 +28,21 @@ import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.metrics.Counter0;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import com.google.gerrit.server.index.account.AccountIndexer;
+import com.google.gerrit.server.logging.CallerFinder;
+import com.google.gerrit.server.update.RetryHelper;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -264,6 +268,7 @@ public class ExternalIdNotes extends VersionedMetaData {
private final AllUsersName allUsersName;
private final Counter0 updateCount;
private final Repository repo;
+ private final CallerFinder callerFinder;
private NoteMap noteMap;
private ObjectId oldRev;
@@ -293,6 +298,19 @@ public class ExternalIdNotes extends VersionedMetaData {
new Description("Total number of external ID updates.").setRate().setUnit("updates"));
this.allUsersName = requireNonNull(allUsersName, "allUsersRepo");
this.repo = requireNonNull(allUsersRepo, "allUsersRepo");
+ this.callerFinder =
+ CallerFinder.builder()
+ // 1. callers that come through ExternalIds
+ .addTarget(ExternalIds.class)
+
+ // 2. callers that come through AccountsUpdate
+ .addTarget(AccountsUpdate.class)
+ .addIgnoredPackage("com.github.rholder.retry")
+ .addIgnoredClass(RetryHelper.class)
+
+ // 3. direct callers
+ .addTarget(ExternalIdNotes.class)
+ .build();
}
public ExternalIdNotes setAfterReadRevision(Runnable afterReadRevision) {
@@ -657,7 +675,8 @@ public class ExternalIdNotes extends VersionedMetaData {
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
if (revision != null) {
- logger.atFine().log("Reading external ID note map");
+ logger.atFine().log(
+ "Reading external ID note map (caller: %s)", callerFinder.findCallerLazy());
noteMap = NoteMap.read(reader, revision);
} else {
noteMap = NoteMap.newEmptyMap();
@@ -670,7 +689,7 @@ public class ExternalIdNotes extends VersionedMetaData {
@Override
public RevCommit commit(MetaDataUpdate update) throws IOException {
- oldRev = revision != null ? revision.copy() : ObjectId.zeroId();
+ oldRev = ObjectIds.copyOrZero(revision);
RevCommit commit = super.commit(update);
updateCount.increment();
return commit;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java b/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
index cf5500ee81..6334265ca7 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdReader.java
@@ -17,11 +17,11 @@ package com.google.gerrit.server.account.externalids;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer0;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIds.java b/java/com/google/gerrit/server/account/externalids/ExternalIds.java
index 9098630f02..28d3af2d20 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIds.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIds.java
@@ -18,7 +18,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java b/java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java
index 489748771f..815f7d0dae 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdsConsistencyChecker.java
@@ -30,7 +30,6 @@ import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.apache.commons.codec.DecoderException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@@ -134,7 +133,7 @@ public class ExternalIdsConsistencyChecker {
if (extId.password() != null && extId.isScheme(SCHEME_USERNAME)) {
try {
HashedPassword.decode(extId.password());
- } catch (DecoderException e) {
+ } catch (HashedPassword.DecoderException e) {
addError(
String.format(
"External ID '%s' has an invalid password: %s", extId.key().get(), e.getMessage()),
diff --git a/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java b/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
new file mode 100644
index 0000000000..3f2f7749df
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids;
+
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+
+import com.google.common.base.Strings;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.server.account.HashedPassword;
+import java.util.Collection;
+
+/** Checks if a given username and password match a user's external IDs. */
+public class PasswordVerifier {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ /** Returns {@code true} if there is an external ID matching both the username and password. */
+ public static boolean checkPassword(
+ Collection<ExternalId> externalIds, String username, @Nullable String password) {
+ if (password == null) {
+ return false;
+ }
+ for (ExternalId id : externalIds) {
+ // Only process the "username:$USER" entry, which is unique.
+ if (!id.isScheme(SCHEME_USERNAME) || !username.equals(id.key().id())) {
+ continue;
+ }
+
+ String hashedStr = id.password();
+ if (!Strings.isNullOrEmpty(hashedStr)) {
+ try {
+ return HashedPassword.decode(hashedStr).checkPassword(password);
+ } catch (HashedPassword.DecoderException e) {
+ logger.atSevere().log("DecoderException for user %s: %s ", username, e.getMessage());
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/google/gerrit/server/account/externalids/testing/BUILD b/java/com/google/gerrit/server/account/externalids/testing/BUILD
new file mode 100644
index 0000000000..0e469e3206
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/testing/BUILD
@@ -0,0 +1,13 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "testing",
+ testonly = 1,
+ srcs = glob(["**/*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//java/com/google/gerrit/entities",
+ "//java/com/google/gerrit/server",
+ "//lib:jgit",
+ ],
+)
diff --git a/java/com/google/gerrit/server/account/externalids/testing/ExternalIdInserter.java b/java/com/google/gerrit/server/account/externalids/testing/ExternalIdInserter.java
new file mode 100644
index 0000000000..a93192a559
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/testing/ExternalIdInserter.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids.testing;
+
+import java.io.IOException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.notes.NoteMap;
+
+@FunctionalInterface
+public interface ExternalIdInserter {
+ public ObjectId addNote(ObjectInserter ins, NoteMap noteMap) throws IOException;
+}
diff --git a/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java b/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java
new file mode 100644
index 0000000000..b8040f7308
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java
@@ -0,0 +1,163 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids.testing;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdReader;
+import java.io.IOException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.notes.NoteMap;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/** Common methods for dealing with external IDs in tests. */
+public class ExternalIdTestUtil {
+
+ public static String insertExternalIdWithoutAccountId(
+ Repository repo, RevWalk rw, PersonIdent ident, Account.Id accountId, String externalId)
+ throws IOException {
+ return insertExternalId(
+ repo,
+ rw,
+ ident,
+ (ins, noteMap) -> {
+ ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), accountId);
+ ObjectId noteId = extId.key().sha1();
+ Config c = new Config();
+ extId.writeToConfig(c);
+ c.unset("externalId", extId.key().get(), "accountId");
+ byte[] raw = c.toText().getBytes(UTF_8);
+ ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
+ noteMap.set(noteId, dataBlob);
+ return noteId;
+ });
+ }
+
+ public static String insertExternalIdWithKeyThatDoesntMatchNoteId(
+ Repository repo, RevWalk rw, PersonIdent ident, Account.Id accountId, String externalId)
+ throws IOException {
+ return insertExternalId(
+ repo,
+ rw,
+ ident,
+ (ins, noteMap) -> {
+ ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), accountId);
+ ObjectId noteId = ExternalId.Key.parse(externalId + "x").sha1();
+ Config c = new Config();
+ extId.writeToConfig(c);
+ byte[] raw = c.toText().getBytes(UTF_8);
+ ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
+ noteMap.set(noteId, dataBlob);
+ return noteId;
+ });
+ }
+
+ public static String insertExternalIdWithInvalidConfig(
+ Repository repo, RevWalk rw, PersonIdent ident, String externalId) throws IOException {
+ return insertExternalId(
+ repo,
+ rw,
+ ident,
+ (ins, noteMap) -> {
+ ObjectId noteId = ExternalId.Key.parse(externalId).sha1();
+ byte[] raw = "bad-config".getBytes(UTF_8);
+ ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
+ noteMap.set(noteId, dataBlob);
+ return noteId;
+ });
+ }
+
+ public static String insertExternalIdWithEmptyNote(
+ Repository repo, RevWalk rw, PersonIdent ident, String externalId) throws IOException {
+ return insertExternalId(
+ repo,
+ rw,
+ ident,
+ (ins, noteMap) -> {
+ ObjectId noteId = ExternalId.Key.parse(externalId).sha1();
+ byte[] raw = "".getBytes(UTF_8);
+ ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
+ noteMap.set(noteId, dataBlob);
+ return noteId;
+ });
+ }
+
+ private static String insertExternalId(
+ Repository repo, RevWalk rw, PersonIdent ident, ExternalIdInserter extIdInserter)
+ throws IOException {
+ ObjectId rev = ExternalIdReader.readRevision(repo);
+ NoteMap noteMap = ExternalIdReader.readNoteMap(rw, rev);
+
+ try (ObjectInserter ins = repo.newObjectInserter()) {
+ ObjectId noteId = extIdInserter.addNote(ins, noteMap);
+
+ CommitBuilder cb = new CommitBuilder();
+ cb.setMessage("Update external IDs");
+ cb.setTreeId(noteMap.writeTree(ins));
+ cb.setAuthor(ident);
+ cb.setCommitter(ident);
+ if (!rev.equals(ObjectId.zeroId())) {
+ cb.setParentId(rev);
+ } else {
+ cb.setParentIds(); // Ref is currently nonexistent, commit has no parents.
+ }
+ if (cb.getTreeId() == null) {
+ if (rev.equals(ObjectId.zeroId())) {
+ cb.setTreeId(ins.insert(OBJ_TREE, new byte[] {})); // No parent, assume empty tree.
+ } else {
+ RevCommit p = rw.parseCommit(rev);
+ cb.setTreeId(p.getTree()); // Copy tree from parent.
+ }
+ }
+ ObjectId commitId = ins.insert(cb);
+ ins.flush();
+
+ RefUpdate u = repo.updateRef(RefNames.REFS_EXTERNAL_IDS);
+ u.setExpectedOldObjectId(rev);
+ u.setNewObjectId(commitId);
+ RefUpdate.Result res = u.update();
+ switch (res) {
+ case NEW:
+ case FAST_FORWARD:
+ case NO_CHANGE:
+ case RENAMED:
+ case FORCED:
+ break;
+ case LOCK_FAILURE:
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ throw new IOException("Updating external IDs failed with " + res);
+ }
+ return noteId.getName();
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/api/BUILD b/java/com/google/gerrit/server/api/BUILD
index 37e04bb588..0275c797ba 100644
--- a/java/com/google/gerrit/server/api/BUILD
+++ b/java/com/google/gerrit/server/api/BUILD
@@ -9,17 +9,17 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/util/cli",
"//lib:args4j",
"//lib:guava",
+ "//lib:jgit",
"//lib:servlet-api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index 673f5aede1..7fa976716b 100644
--- a/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -240,7 +240,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public AccountDetailInfo detail() throws RestApiException {
try {
- return getDetail.apply(account);
+ return getDetail.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot get detail", e);
}
@@ -248,8 +248,12 @@ public class AccountApiImpl implements AccountApi {
@Override
public boolean getActive() throws RestApiException {
- Response<String> result = getActive.apply(account);
- return result.statusCode() == SC_OK && result.value().equals("ok");
+ try {
+ Response<String> result = getActive.apply(account);
+ return result.statusCode() == SC_OK && result.value().equals("ok");
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get active", e);
+ }
}
@Override
@@ -274,7 +278,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public GeneralPreferencesInfo getPreferences() throws RestApiException {
try {
- return getPreferences.apply(account);
+ return getPreferences.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot get preferences", e);
}
@@ -283,7 +287,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public GeneralPreferencesInfo setPreferences(GeneralPreferencesInfo in) throws RestApiException {
try {
- return setPreferences.apply(account, in);
+ return setPreferences.apply(account, in).value();
} catch (Exception e) {
throw asRestApiException("Cannot set preferences", e);
}
@@ -292,7 +296,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public DiffPreferencesInfo getDiffPreferences() throws RestApiException {
try {
- return getDiffPreferences.apply(account);
+ return getDiffPreferences.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot query diff preferences", e);
}
@@ -301,7 +305,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public DiffPreferencesInfo setDiffPreferences(DiffPreferencesInfo in) throws RestApiException {
try {
- return setDiffPreferences.apply(account, in);
+ return setDiffPreferences.apply(account, in).value();
} catch (Exception e) {
throw asRestApiException("Cannot set diff preferences", e);
}
@@ -310,7 +314,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public EditPreferencesInfo getEditPreferences() throws RestApiException {
try {
- return getEditPreferences.apply(account);
+ return getEditPreferences.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot query edit preferences", e);
}
@@ -319,7 +323,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public EditPreferencesInfo setEditPreferences(EditPreferencesInfo in) throws RestApiException {
try {
- return setEditPreferences.apply(account, in);
+ return setEditPreferences.apply(account, in).value();
} catch (Exception e) {
throw asRestApiException("Cannot set edit preferences", e);
}
@@ -328,7 +332,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public List<ProjectWatchInfo> getWatchedProjects() throws RestApiException {
try {
- return getWatchedProjects.apply(account);
+ return getWatchedProjects.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot get watched projects", e);
}
@@ -338,7 +342,7 @@ public class AccountApiImpl implements AccountApi {
public List<ProjectWatchInfo> setWatchedProjects(List<ProjectWatchInfo> in)
throws RestApiException {
try {
- return postWatchedProjects.apply(account, in);
+ return postWatchedProjects.apply(account, in).value();
} catch (Exception e) {
throw asRestApiException("Cannot update watched projects", e);
}
@@ -389,7 +393,7 @@ public class AccountApiImpl implements AccountApi {
public SortedSet<String> getStars(String changeId) throws RestApiException {
try {
AccountResource.Star rsrc = stars.parse(account, IdString.fromUrl(changeId));
- return starsGet.apply(rsrc);
+ return starsGet.apply(rsrc).value();
} catch (Exception e) {
throw asRestApiException("Cannot get stars", e);
}
@@ -398,7 +402,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public List<ChangeInfo> getStarredChanges() throws RestApiException {
try {
- return stars.list().apply(account);
+ return stars.list().apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot get starred changes", e);
}
@@ -407,7 +411,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public List<GroupInfo> getGroups() throws RestApiException {
try {
- return getGroups.apply(account);
+ return getGroups.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot get groups", e);
}
@@ -416,7 +420,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public List<EmailInfo> getEmails() throws RestApiException {
try {
- return getEmails.apply(account);
+ return getEmails.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot get emails", e);
}
@@ -475,7 +479,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public List<SshKeyInfo> listSshKeys() throws RestApiException {
try {
- return getSshKeys.apply(account);
+ return getSshKeys.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot list SSH keys", e);
}
@@ -534,7 +538,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public List<AgreementInfo> listAgreements() throws RestApiException {
try {
- return getAgreements.apply(account);
+ return getAgreements.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot get agreements", e);
}
@@ -563,7 +567,7 @@ public class AccountApiImpl implements AccountApi {
@Override
public List<AccountExternalIdInfo> getExternalIds() throws RestApiException {
try {
- return getExternalIds.apply(account);
+ return getExternalIds.apply(account).value();
} catch (Exception e) {
throw asRestApiException("Cannot get external IDs", e);
}
@@ -582,7 +586,7 @@ public class AccountApiImpl implements AccountApi {
public List<DeletedDraftCommentInfo> deleteDraftComments(DeleteDraftCommentsInput input)
throws RestApiException {
try {
- return deleteDraftComments.apply(account, input);
+ return deleteDraftComments.apply(account, input).value();
} catch (Exception e) {
throw asRestApiException("Cannot delete draft comments", e);
}
diff --git a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
index 9d298884b8..012e6ce229 100644
--- a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
@@ -133,7 +133,7 @@ public class AccountsImpl implements Accounts {
myQueryAccounts.setSuggest(true);
myQueryAccounts.setQuery(r.getQuery());
myQueryAccounts.setLimit(r.getLimit());
- return myQueryAccounts.apply(TopLevelResource.INSTANCE);
+ return myQueryAccounts.apply(TopLevelResource.INSTANCE).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve suggested accounts", e);
}
@@ -164,7 +164,7 @@ public class AccountsImpl implements Accounts {
for (ListAccountsOption option : r.getOptions()) {
myQueryAccounts.addOption(option);
}
- return myQueryAccounts.apply(TopLevelResource.INSTANCE);
+ return myQueryAccounts.apply(TopLevelResource.INSTANCE).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve suggested accounts", e);
}
diff --git a/java/com/google/gerrit/server/api/accounts/EmailApiImpl.java b/java/com/google/gerrit/server/api/accounts/EmailApiImpl.java
index 759f60ca1d..f68142f982 100644
--- a/java/com/google/gerrit/server/api/accounts/EmailApiImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/EmailApiImpl.java
@@ -61,7 +61,7 @@ public class EmailApiImpl implements EmailApi {
@Override
public EmailInfo get() throws RestApiException {
try {
- return get.apply(resource());
+ return get.apply(resource()).value();
} catch (Exception e) {
throw asRestApiException("Cannot read email", e);
}
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index f027c92b3a..a04be30908 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -351,7 +351,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public ChangeApi revert(RevertInput in) throws RestApiException {
try {
- return changeApi.id(revert.apply(change, in)._number);
+ return changeApi.id(revert.apply(change, in).value()._number);
} catch (Exception e) {
throw asRestApiException("Cannot revert change", e);
}
@@ -401,7 +401,11 @@ class ChangeApiImpl implements ChangeApi {
@Override
public String topic() throws RestApiException {
- return getTopic.apply(change);
+ try {
+ return getTopic.apply(change).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get topic", e);
+ }
}
@Override
@@ -418,7 +422,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public IncludedInInfo includedIn() throws RestApiException {
try {
- return includedIn.apply(change);
+ return includedIn.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Could not extract IncludedIn data", e);
}
@@ -427,7 +431,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public AddReviewerResult addReviewer(AddReviewerInput in) throws RestApiException {
try {
- return postReviewers.apply(change, in);
+ return postReviewers.apply(change, in).value();
} catch (Exception e) {
throw asRestApiException("Cannot add change reviewer", e);
}
@@ -448,7 +452,9 @@ class ChangeApiImpl implements ChangeApi {
try {
suggestReviewers.setQuery(r.getQuery());
suggestReviewers.setLimit(r.getLimit());
- return suggestReviewers.apply(change);
+ suggestReviewers.setExcludeGroups(r.getExcludeGroups());
+ suggestReviewers.setReviewerState(r.getReviewerState());
+ return suggestReviewers.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve suggested reviewers", e);
}
@@ -457,7 +463,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public List<ReviewerInfo> reviewers() throws RestApiException {
try {
- return listReviewers.apply(change);
+ return listReviewers.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve reviewers", e);
}
@@ -512,7 +518,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public AccountInfo setAssignee(AssigneeInput input) throws RestApiException {
try {
- return putAssignee.apply(change, input);
+ return putAssignee.apply(change, input).value();
} catch (Exception e) {
throw asRestApiException("Cannot set assignee", e);
}
@@ -550,7 +556,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public Map<String, List<CommentInfo>> comments() throws RestApiException {
try {
- return listComments.apply(change);
+ return listComments.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot get comments", e);
}
@@ -568,7 +574,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
try {
- return listChangeRobotComments.apply(change);
+ return listChangeRobotComments.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot get robot comments", e);
}
@@ -577,7 +583,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public Map<String, List<CommentInfo>> drafts() throws RestApiException {
try {
- return listDrafts.apply(change);
+ return listDrafts.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot get drafts", e);
}
@@ -671,7 +677,7 @@ class ChangeApiImpl implements ChangeApi {
try {
GetPureRevert getPureRevert = getPureRevertProvider.get();
getPureRevert.setClaimedOriginal(claimedOriginal);
- return getPureRevert.apply(change);
+ return getPureRevert.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot compute pure revert", e);
}
@@ -680,7 +686,7 @@ class ChangeApiImpl implements ChangeApi {
@Override
public List<ChangeMessageInfo> messages() throws RestApiException {
try {
- return changeMessages.list().apply(change);
+ return changeMessages.list().apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot list change messages", e);
}
diff --git a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
index ffc65245d6..7f0feba9b4 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
@@ -221,7 +221,7 @@ public class ChangeEditApiImpl implements ChangeEditApi {
public String getCommitMessage() throws RestApiException {
try {
try (BinaryResult binaryResult =
- getChangeEditCommitMessageProvider.get().apply(changeResource)) {
+ getChangeEditCommitMessageProvider.get().apply(changeResource).value()) {
return binaryResult.asString();
}
} catch (Exception e) {
diff --git a/java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java
index 14310e85f3..490ec5b0d7 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeMessageApiImpl.java
@@ -48,7 +48,7 @@ class ChangeMessageApiImpl implements ChangeMessageApi {
@Override
public ChangeMessageInfo get() throws RestApiException {
try {
- return getChangeMessage.apply(changeMessageResource);
+ return getChangeMessage.apply(changeMessageResource).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve change message", e);
}
diff --git a/java/com/google/gerrit/server/api/changes/ChangesImpl.java b/java/com/google/gerrit/server/api/changes/ChangesImpl.java
index acff137628..b9635fbeb8 100644
--- a/java/com/google/gerrit/server/api/changes/ChangesImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangesImpl.java
@@ -20,6 +20,7 @@ import static java.util.Objects.requireNonNull;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.Changes;
import com.google.gerrit.extensions.client.ListChangesOption;
@@ -29,7 +30,6 @@ import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.api.changes.ChangeApiImpl.DynamicOptionParser;
import com.google.gerrit.server.restapi.change.ChangesCollection;
import com.google.gerrit.server.restapi.change.CreateChange;
@@ -92,7 +92,7 @@ class ChangesImpl implements Changes {
public ChangeApi create(ChangeInput in) throws RestApiException {
try {
ChangeInfo out = createChange.apply(TopLevelResource.INSTANCE, in).value();
- return api.create(changes.parse(new Change.Id(out._number)));
+ return api.create(changes.parse(Change.id(out._number)));
} catch (Exception e) {
throw asRestApiException("Cannot create change", e);
}
@@ -127,7 +127,7 @@ class ChangesImpl implements Changes {
dynamicOptionParser.parseDynamicOptions(qc, q.getPluginOptions());
try {
- List<?> result = qc.apply(TopLevelResource.INSTANCE);
+ List<?> result = qc.apply(TopLevelResource.INSTANCE).value();
if (result.isEmpty()) {
return ImmutableList.of();
}
diff --git a/java/com/google/gerrit/server/api/changes/CommentApiImpl.java b/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
index 418187db32..c5fcab104b 100644
--- a/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/CommentApiImpl.java
@@ -46,7 +46,7 @@ class CommentApiImpl implements CommentApi {
@Override
public CommentInfo get() throws RestApiException {
try {
- return getComment.apply(comment);
+ return getComment.apply(comment).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve comment", e);
}
@@ -55,7 +55,7 @@ class CommentApiImpl implements CommentApi {
@Override
public CommentInfo delete(DeleteCommentInput input) throws RestApiException {
try {
- return deleteComment.apply(comment, input);
+ return deleteComment.apply(comment, input).value();
} catch (Exception e) {
throw asRestApiException("Cannot delete comment", e);
}
diff --git a/java/com/google/gerrit/server/api/changes/DraftApiImpl.java b/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
index 4d26b11336..f6eb3c53e0 100644
--- a/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/DraftApiImpl.java
@@ -54,7 +54,7 @@ class DraftApiImpl implements DraftApi {
@Override
public CommentInfo get() throws RestApiException {
try {
- return getDraft.apply(draft);
+ return getDraft.apply(draft).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve draft", e);
}
diff --git a/java/com/google/gerrit/server/api/changes/FileApiImpl.java b/java/com/google/gerrit/server/api/changes/FileApiImpl.java
index 883ab99184..c506b2ec96 100644
--- a/java/com/google/gerrit/server/api/changes/FileApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/FileApiImpl.java
@@ -63,7 +63,7 @@ class FileApiImpl implements FileApi {
@Override
public BinaryResult content() throws RestApiException {
try {
- return getContent.apply(file);
+ return getContent.apply(file).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve file content", e);
}
diff --git a/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java b/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
index 11536cbfe9..2174ef0d43 100644
--- a/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ReviewerApiImpl.java
@@ -54,7 +54,7 @@ public class ReviewerApiImpl implements ReviewerApi {
@Override
public Map<String, Short> votes() throws RestApiException {
try {
- return listVotes.apply(reviewer);
+ return listVotes.apply(reviewer).value();
} catch (Exception e) {
throw asRestApiException("Cannot list votes", e);
}
diff --git a/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 2df7ae6c5f..01dfe36454 100644
--- a/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder.ListMultimapBuilder;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.Changes;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
@@ -54,7 +55,6 @@ import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.account.AccountDirectory.FillOptions;
import com.google.gerrit.server.account.AccountLoader;
@@ -254,7 +254,7 @@ class RevisionApiImpl implements RevisionApi {
public BinaryResult submitPreview(String format) throws RestApiException {
try {
submitPreview.setFormat(format);
- return submitPreview.apply(revision);
+ return submitPreview.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot get submit preview", e);
}
@@ -263,7 +263,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public ChangeApi rebase(RebaseInput in) throws RestApiException {
try {
- return changes.id(rebase.apply(revision, in)._number);
+ return changes.id(rebase.apply(revision, in).value()._number);
} catch (Exception e) {
throw asRestApiException("Cannot rebase ps", e);
}
@@ -282,7 +282,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public ChangeApi cherryPick(CherryPickInput in) throws RestApiException {
try {
- return changes.id(cherryPick.apply(revision, in)._number);
+ return changes.id(cherryPick.apply(revision, in).value()._number);
} catch (Exception e) {
throw asRestApiException("Cannot cherry pick", e);
}
@@ -291,7 +291,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public CherryPickChangeInfo cherryPickAsInfo(CherryPickInput in) throws RestApiException {
try {
- return cherryPick.apply(revision, in);
+ return cherryPick.apply(revision, in).value();
} catch (Exception e) {
throw asRestApiException("Cannot cherry pick", e);
}
@@ -336,7 +336,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public MergeableInfo mergeable() throws RestApiException {
try {
- return mergeable.apply(revision);
+ return mergeable.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot check mergeability", e);
}
@@ -346,7 +346,7 @@ class RevisionApiImpl implements RevisionApi {
public MergeableInfo mergeableOtherBranches() throws RestApiException {
try {
mergeable.setOtherBranches(true);
- return mergeable.apply(revision);
+ return mergeable.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot check mergeability", e);
}
@@ -400,7 +400,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public Map<String, List<CommentInfo>> comments() throws RestApiException {
try {
- return listComments.apply(revision);
+ return listComments.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve comments", e);
}
@@ -409,7 +409,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
try {
- return listRobotComments.apply(revision);
+ return listRobotComments.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve robot comments", e);
}
@@ -427,7 +427,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public Map<String, List<CommentInfo>> drafts() throws RestApiException {
try {
- return listDrafts.apply(revision);
+ return listDrafts.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve drafts", e);
}
@@ -476,7 +476,7 @@ class RevisionApiImpl implements RevisionApi {
// Reread change to pick up new notes refs.
return changes
.id(revision.getChange().getId().get())
- .revision(revision.getPatchSet().getId().get())
+ .revision(revision.getPatchSet().id().get())
.draft(id);
} catch (Exception e) {
throw asRestApiException("Cannot create draft", e);
@@ -504,7 +504,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public BinaryResult patch() throws RestApiException {
try {
- return getPatch.apply(revision);
+ return getPatch.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot get patch", e);
}
@@ -513,7 +513,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public BinaryResult patch(String path) throws RestApiException {
try {
- return getPatch.setPath(path).apply(revision);
+ return getPatch.setPath(path).apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot get patch", e);
}
@@ -531,7 +531,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public SubmitType submitType() throws RestApiException {
try {
- return getSubmitType.apply(revision);
+ return getSubmitType.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot get submit type", e);
}
@@ -540,16 +540,16 @@ class RevisionApiImpl implements RevisionApi {
@Override
public SubmitType testSubmitType(TestSubmitRuleInput in) throws RestApiException {
try {
- return testSubmitType.apply(revision, in);
+ return testSubmitType.apply(revision, in).value();
} catch (Exception e) {
throw asRestApiException("Cannot test submit type", e);
}
}
@Override
- public List<TestSubmitRuleInfo> testSubmitRule(TestSubmitRuleInput in) throws RestApiException {
+ public TestSubmitRuleInfo testSubmitRule(TestSubmitRuleInput in) throws RestApiException {
try {
- return testSubmitRule.get().apply(revision, in);
+ return testSubmitRule.get().apply(revision, in).value();
} catch (Exception e) {
throw asRestApiException("Cannot test submit rule", e);
}
@@ -575,7 +575,7 @@ class RevisionApiImpl implements RevisionApi {
@Override
public RelatedChangesInfo related() throws RestApiException {
try {
- return getRelated.apply(revision);
+ return getRelated.apply(revision).value();
} catch (Exception e) {
throw asRestApiException("Cannot get related changes", e);
}
@@ -587,20 +587,20 @@ class RevisionApiImpl implements RevisionApi {
ListMultimapBuilder.treeKeys().arrayListValues().build();
try {
Iterable<PatchSetApproval> approvals =
- approvalsUtil.byPatchSet(revision.getNotes(), revision.getPatchSet().getId(), null, null);
+ approvalsUtil.byPatchSet(revision.getNotes(), revision.getPatchSet().id(), null, null);
AccountLoader accountLoader =
accountLoaderFactory.create(
EnumSet.of(
FillOptions.ID, FillOptions.NAME, FillOptions.EMAIL, FillOptions.USERNAME));
for (PatchSetApproval approval : approvals) {
- String label = approval.getLabel();
+ String label = approval.label();
ApprovalInfo info =
new ApprovalInfo(
- approval.getAccountId().get(),
- Integer.valueOf(approval.getValue()),
+ approval.accountId().get(),
+ Integer.valueOf(approval.value()),
null,
- approval.getTag(),
- approval.getGranted());
+ approval.tag().orElse(null),
+ approval.granted());
accountLoader.put(info);
result.get(label).add(info);
}
@@ -624,7 +624,11 @@ class RevisionApiImpl implements RevisionApi {
@Override
public String description() throws RestApiException {
- return getDescription.apply(revision);
+ try {
+ return getDescription.apply(revision).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get description", e);
+ }
}
@Override
diff --git a/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java b/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
index 8cad507aad..49c2d4931c 100644
--- a/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RevisionReviewerApiImpl.java
@@ -47,7 +47,7 @@ public class RevisionReviewerApiImpl implements RevisionReviewerApi {
@Override
public Map<String, Short> votes() throws RestApiException {
try {
- return listVotes.apply(reviewer);
+ return listVotes.apply(reviewer).value();
} catch (Exception e) {
throw asRestApiException("Cannot list votes", e);
}
diff --git a/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java b/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
index 37a56fe0fb..ec13061493 100644
--- a/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RobotCommentApiImpl.java
@@ -41,7 +41,7 @@ public class RobotCommentApiImpl implements RobotCommentApi {
@Override
public RobotCommentInfo get() throws RestApiException {
try {
- return getComment.apply(comment);
+ return getComment.apply(comment).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve robot comment", e);
}
diff --git a/java/com/google/gerrit/server/api/config/ServerImpl.java b/java/com/google/gerrit/server/api/config/ServerImpl.java
index 6e78be241c..ab40ec8743 100644
--- a/java/com/google/gerrit/server/api/config/ServerImpl.java
+++ b/java/com/google/gerrit/server/api/config/ServerImpl.java
@@ -83,7 +83,7 @@ public class ServerImpl implements Server {
@Override
public ServerInfo getInfo() throws RestApiException {
try {
- return getServerInfo.apply(new ConfigResource());
+ return getServerInfo.apply(new ConfigResource()).value();
} catch (Exception e) {
throw asRestApiException("Cannot get server info", e);
}
@@ -92,7 +92,7 @@ public class ServerImpl implements Server {
@Override
public GeneralPreferencesInfo getDefaultPreferences() throws RestApiException {
try {
- return getPreferences.apply(new ConfigResource());
+ return getPreferences.apply(new ConfigResource()).value();
} catch (Exception e) {
throw asRestApiException("Cannot get default general preferences", e);
}
@@ -102,7 +102,7 @@ public class ServerImpl implements Server {
public GeneralPreferencesInfo setDefaultPreferences(GeneralPreferencesInfo in)
throws RestApiException {
try {
- return setPreferences.apply(new ConfigResource(), in);
+ return setPreferences.apply(new ConfigResource(), in).value();
} catch (Exception e) {
throw asRestApiException("Cannot set default general preferences", e);
}
@@ -111,7 +111,7 @@ public class ServerImpl implements Server {
@Override
public DiffPreferencesInfo getDefaultDiffPreferences() throws RestApiException {
try {
- return getDiffPreferences.apply(new ConfigResource());
+ return getDiffPreferences.apply(new ConfigResource()).value();
} catch (Exception e) {
throw asRestApiException("Cannot get default diff preferences", e);
}
@@ -121,7 +121,7 @@ public class ServerImpl implements Server {
public DiffPreferencesInfo setDefaultDiffPreferences(DiffPreferencesInfo in)
throws RestApiException {
try {
- return setDiffPreferences.apply(new ConfigResource(), in);
+ return setDiffPreferences.apply(new ConfigResource(), in).value();
} catch (Exception e) {
throw asRestApiException("Cannot set default diff preferences", e);
}
@@ -130,7 +130,7 @@ public class ServerImpl implements Server {
@Override
public EditPreferencesInfo getDefaultEditPreferences() throws RestApiException {
try {
- return getEditPreferences.apply(new ConfigResource());
+ return getEditPreferences.apply(new ConfigResource()).value();
} catch (Exception e) {
throw asRestApiException("Cannot get default edit preferences", e);
}
@@ -140,7 +140,7 @@ public class ServerImpl implements Server {
public EditPreferencesInfo setDefaultEditPreferences(EditPreferencesInfo in)
throws RestApiException {
try {
- return setEditPreferences.apply(new ConfigResource(), in);
+ return setEditPreferences.apply(new ConfigResource(), in).value();
} catch (Exception e) {
throw asRestApiException("Cannot set default edit preferences", e);
}
@@ -149,14 +149,18 @@ public class ServerImpl implements Server {
@Override
public ConsistencyCheckInfo checkConsistency(ConsistencyCheckInput in) throws RestApiException {
try {
- return checkConsistency.get().apply(new ConfigResource(), in);
+ return checkConsistency.get().apply(new ConfigResource(), in).value();
} catch (Exception e) {
throw asRestApiException("Cannot check consistency", e);
}
}
@Override
- public List<TopMenu.MenuEntry> topMenus() {
- return listTopMenus.apply(new ConfigResource()).value();
+ public List<TopMenu.MenuEntry> topMenus() throws RestApiException {
+ try {
+ return listTopMenus.apply(new ConfigResource()).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get top menus", e);
+ }
}
}
diff --git a/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
index b70a0294a3..bb04ab4da2 100644
--- a/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
@@ -119,7 +119,7 @@ class GroupApiImpl implements GroupApi {
@Override
public GroupInfo get() throws RestApiException {
try {
- return getGroup.apply(rsrc);
+ return getGroup.apply(rsrc).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve group", e);
}
@@ -128,7 +128,7 @@ class GroupApiImpl implements GroupApi {
@Override
public GroupInfo detail() throws RestApiException {
try {
- return getDetail.apply(rsrc);
+ return getDetail.apply(rsrc).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve group", e);
}
@@ -136,7 +136,11 @@ class GroupApiImpl implements GroupApi {
@Override
public String name() throws RestApiException {
- return getName.apply(rsrc);
+ try {
+ return getName.apply(rsrc).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get group name", e);
+ }
}
@Override
@@ -153,7 +157,7 @@ class GroupApiImpl implements GroupApi {
@Override
public GroupInfo owner() throws RestApiException {
try {
- return getOwner.apply(rsrc);
+ return getOwner.apply(rsrc).value();
} catch (Exception e) {
throw asRestApiException("Cannot get group owner", e);
}
@@ -172,7 +176,11 @@ class GroupApiImpl implements GroupApi {
@Override
public String description() throws RestApiException {
- return getDescription.apply(rsrc);
+ try {
+ return getDescription.apply(rsrc).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get group description", e);
+ }
}
@Override
@@ -188,7 +196,11 @@ class GroupApiImpl implements GroupApi {
@Override
public GroupOptionsInfo options() throws RestApiException {
- return getOptions.apply(rsrc);
+ try {
+ return getOptions.apply(rsrc).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get group options", e);
+ }
}
@Override
@@ -209,7 +221,7 @@ class GroupApiImpl implements GroupApi {
public List<AccountInfo> members(boolean recursive) throws RestApiException {
listMembers.setRecursive(recursive);
try {
- return listMembers.apply(rsrc);
+ return listMembers.apply(rsrc).value();
} catch (Exception e) {
throw asRestApiException("Cannot list group members", e);
}
@@ -236,7 +248,7 @@ class GroupApiImpl implements GroupApi {
@Override
public List<GroupInfo> includedGroups() throws RestApiException {
try {
- return listSubgroups.apply(rsrc);
+ return listSubgroups.apply(rsrc).value();
} catch (Exception e) {
throw asRestApiException("Cannot list subgroups", e);
}
@@ -263,7 +275,7 @@ class GroupApiImpl implements GroupApi {
@Override
public List<? extends GroupAuditEventInfo> auditLog() throws RestApiException {
try {
- return getAuditLog.apply(rsrc);
+ return getAuditLog.apply(rsrc).value();
} catch (Exception e) {
throw asRestApiException("Cannot get audit log", e);
}
diff --git a/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
index bae75dbea3..95dcad436b 100644
--- a/java/com/google/gerrit/server/api/groups/GroupsImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -98,7 +98,7 @@ class GroupsImpl implements Groups {
.currentUser()
.checkAny(GlobalPermission.fromAnnotation(createGroup.getClass()));
GroupInfo info =
- createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(in.name), in);
+ createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(in.name), in).value();
return id(info.id);
} catch (Exception e) {
throw asRestApiException("Cannot create group " + in.name, e);
@@ -141,7 +141,7 @@ class GroupsImpl implements Groups {
if (req.getUser() != null) {
try {
- list.setUser(accountResolver.resolve(req.getUser()).asUnique().getAccount().getId());
+ list.setUser(accountResolver.resolve(req.getUser()).asUnique().account().id());
} catch (Exception e) {
throw asRestApiException("Error looking up user " + req.getUser(), e);
}
@@ -154,7 +154,7 @@ class GroupsImpl implements Groups {
list.setMatchRegex(req.getRegex());
list.setSuggest(req.getSuggest());
try {
- return list.apply(tlr);
+ return list.apply(tlr).value();
} catch (Exception e) {
throw asRestApiException("Cannot list groups", e);
}
@@ -184,7 +184,7 @@ class GroupsImpl implements Groups {
for (ListGroupsOption option : r.getOptions()) {
myQueryGroups.addOption(option);
}
- return myQueryGroups.apply(TopLevelResource.INSTANCE);
+ return myQueryGroups.apply(TopLevelResource.INSTANCE).value();
} catch (Exception e) {
throw asRestApiException("Cannot query groups", e);
}
diff --git a/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java b/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
index 71f78320c0..39321779e4 100644
--- a/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
+++ b/java/com/google/gerrit/server/api/plugins/PluginApiImpl.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.api.plugins;
+import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
+
import com.google.gerrit.extensions.api.plugins.PluginApi;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.PluginInfo;
@@ -53,7 +55,11 @@ public class PluginApiImpl implements PluginApi {
@Override
public PluginInfo get() throws RestApiException {
- return getStatus.apply(resource);
+ try {
+ return getStatus.apply(resource).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get status", e);
+ }
}
@Override
diff --git a/java/com/google/gerrit/server/api/plugins/PluginsImpl.java b/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
index e5706559a0..c2750937ab 100644
--- a/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
+++ b/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.api.plugins;
+import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
+
import com.google.gerrit.extensions.api.plugins.InstallPluginInput;
import com.google.gerrit.extensions.api.plugins.PluginApi;
import com.google.gerrit.extensions.api.plugins.Plugins;
@@ -27,7 +29,6 @@ import com.google.gerrit.server.plugins.PluginsCollection;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.IOException;
import java.util.SortedMap;
@Singleton
@@ -59,7 +60,11 @@ public class PluginsImpl implements Plugins {
return new ListRequest() {
@Override
public SortedMap<String, PluginInfo> getAsMap() throws RestApiException {
- return listProvider.get().request(this).apply(TopLevelResource.INSTANCE);
+ try {
+ return listProvider.get().request(this).apply(TopLevelResource.INSTANCE).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot list plugins", e);
+ }
}
};
}
@@ -87,8 +92,8 @@ public class PluginsImpl implements Plugins {
Response<PluginInfo> created =
installProvider.get().setName(name).apply(TopLevelResource.INSTANCE, input);
return pluginApi.create(plugins.parse(created.value().id));
- } catch (IOException e) {
- throw new RestApiException("could not install plugin", e);
+ } catch (Exception e) {
+ throw asRestApiException("Cannot install plugin", e);
}
}
}
diff --git a/java/com/google/gerrit/server/api/projects/BranchApiImpl.java b/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
index b3506fc5d7..c7cca6f247 100644
--- a/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/BranchApiImpl.java
@@ -90,7 +90,7 @@ public class BranchApiImpl implements BranchApi {
@Override
public BranchInfo get() throws RestApiException {
try {
- return getBranch.apply(resource());
+ return getBranch.apply(resource()).value();
} catch (Exception e) {
throw asRestApiException("Cannot read branch", e);
}
@@ -109,7 +109,7 @@ public class BranchApiImpl implements BranchApi {
public BinaryResult file(String path) throws RestApiException {
try {
FileResource resource = filesCollection.parse(resource(), IdString.fromDecoded(path));
- return getContent.apply(resource);
+ return getContent.apply(resource).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve file", e);
}
@@ -118,9 +118,9 @@ public class BranchApiImpl implements BranchApi {
@Override
public List<ReflogEntryInfo> reflog() throws RestApiException {
try {
- return getReflog.apply(resource());
- } catch (IOException | PermissionBackendException e) {
- throw new RestApiException("Cannot retrieve reflog", e);
+ return getReflog.apply(resource()).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot retrieve reflog", e);
}
}
diff --git a/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
index d7c9bc7add..25e56febd7 100644
--- a/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ChildProjectApiImpl.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.api.projects;
+import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
+
import com.google.gerrit.extensions.api.projects.ChildProjectApi;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -43,7 +45,11 @@ public class ChildProjectApiImpl implements ChildProjectApi {
@Override
public ProjectInfo get(boolean recursive) throws RestApiException {
- getChildProject.setRecursive(recursive);
- return getChildProject.apply(rsrc);
+ try {
+ getChildProject.setRecursive(recursive);
+ return getChildProject.apply(rsrc).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get child project", e);
+ }
}
}
diff --git a/java/com/google/gerrit/server/api/projects/CommitApiImpl.java b/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
index 0a3b2369a7..5c7921ac88 100644
--- a/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/CommitApiImpl.java
@@ -58,7 +58,7 @@ public class CommitApiImpl implements CommitApi {
@Override
public CommitInfo get() throws RestApiException {
try {
- return getCommit.apply(commitResource);
+ return getCommit.apply(commitResource).value();
} catch (Exception e) {
throw asRestApiException("Cannot get commit info", e);
}
@@ -67,7 +67,7 @@ public class CommitApiImpl implements CommitApi {
@Override
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
try {
- return changes.id(cherryPickCommit.apply(commitResource, input)._number);
+ return changes.id(cherryPickCommit.apply(commitResource, input).value()._number);
} catch (Exception e) {
throw asRestApiException("Cannot cherry pick", e);
}
@@ -76,7 +76,7 @@ public class CommitApiImpl implements CommitApi {
@Override
public IncludedInInfo includedIn() throws RestApiException {
try {
- return includedIn.apply(commitResource);
+ return includedIn.apply(commitResource).value();
} catch (Exception e) {
throw asRestApiException("Could not extract IncludedIn data", e);
}
diff --git a/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java b/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
index c44f5bb502..61736f6d5c 100644
--- a/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
@@ -67,8 +67,8 @@ public class DashboardApiImpl implements DashboardApi {
@Override
public DashboardInfo get(boolean inherited) throws RestApiException {
try {
- return get.get().setInherited(inherited).apply(resource());
- } catch (IOException | PermissionBackendException | ConfigInvalidException e) {
+ return get.get().setInherited(inherited).apply(resource()).value();
+ } catch (Exception e) {
throw asRestApiException("Cannot read dashboard", e);
}
}
diff --git a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index 354331efcf..1ac905d7a0 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -43,6 +43,7 @@ import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.api.projects.TagApi;
import com.google.gerrit.extensions.api.projects.TagInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
@@ -69,6 +70,7 @@ import com.google.gerrit.server.restapi.project.GetDescription;
import com.google.gerrit.server.restapi.project.GetHead;
import com.google.gerrit.server.restapi.project.GetParent;
import com.google.gerrit.server.restapi.project.Index;
+import com.google.gerrit.server.restapi.project.IndexChanges;
import com.google.gerrit.server.restapi.project.ListBranches;
import com.google.gerrit.server.restapi.project.ListDashboards;
import com.google.gerrit.server.restapi.project.ListTags;
@@ -124,6 +126,7 @@ public class ProjectApiImpl implements ProjectApi {
private final GetParent getParent;
private final SetParent setParent;
private final Index index;
+ private final IndexChanges indexChanges;
@AssistedInject
ProjectApiImpl(
@@ -158,6 +161,7 @@ public class ProjectApiImpl implements ProjectApi {
GetParent getParent,
SetParent setParent,
Index index,
+ IndexChanges indexChanges,
@Assisted ProjectResource project) {
this(
permissionBackend,
@@ -192,6 +196,7 @@ public class ProjectApiImpl implements ProjectApi {
getParent,
setParent,
index,
+ indexChanges,
null);
}
@@ -228,6 +233,7 @@ public class ProjectApiImpl implements ProjectApi {
GetParent getParent,
SetParent setParent,
Index index,
+ IndexChanges indexChanges,
@Assisted String name) {
this(
permissionBackend,
@@ -262,6 +268,7 @@ public class ProjectApiImpl implements ProjectApi {
getParent,
setParent,
index,
+ indexChanges,
name);
}
@@ -298,6 +305,7 @@ public class ProjectApiImpl implements ProjectApi {
GetParent getParent,
SetParent setParent,
Index index,
+ IndexChanges indexChanges,
String name) {
this.permissionBackend = permissionBackend;
this.createProject = createProject;
@@ -332,6 +340,7 @@ public class ProjectApiImpl implements ProjectApi {
this.setParent = setParent;
this.name = name;
this.index = index;
+ this.indexChanges = indexChanges;
}
@Override
@@ -368,13 +377,17 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public String description() throws RestApiException {
- return getDescription.apply(checkExists());
+ try {
+ return getDescription.apply(checkExists()).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get description", e);
+ }
}
@Override
public ProjectAccessInfo access() throws RestApiException {
try {
- return getAccess.apply(checkExists());
+ return getAccess.apply(checkExists()).value();
} catch (Exception e) {
throw asRestApiException("Cannot get access rights", e);
}
@@ -383,7 +396,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public ProjectAccessInfo access(ProjectAccessInput p) throws RestApiException {
try {
- return setAccess.apply(checkExists(), p);
+ return setAccess.apply(checkExists(), p).value();
} catch (Exception e) {
throw asRestApiException("Cannot put access rights", e);
}
@@ -401,7 +414,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public AccessCheckInfo checkAccess(AccessCheckInput in) throws RestApiException {
try {
- return checkAccess.apply(checkExists(), in);
+ return checkAccess.apply(checkExists(), in).value();
} catch (Exception e) {
throw asRestApiException("Cannot check access rights", e);
}
@@ -410,7 +423,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public CheckProjectResultInfo check(CheckProjectInput in) throws RestApiException {
try {
- return check.apply(checkExists(), in);
+ return check.apply(checkExists(), in).value();
} catch (Exception e) {
throw asRestApiException("Cannot check project", e);
}
@@ -427,13 +440,17 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public ConfigInfo config() throws RestApiException {
- return getConfig.apply(checkExists());
+ try {
+ return getConfig.apply(checkExists()).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot get config", e);
+ }
}
@Override
public ConfigInfo config(ConfigInput in) throws RestApiException {
try {
- return putConfig.apply(checkExists(), in);
+ return putConfig.apply(checkExists(), in).value();
} catch (Exception e) {
throw asRestApiException("Cannot list tags", e);
}
@@ -445,7 +462,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public List<BranchInfo> get() throws RestApiException {
try {
- return listBranches.get().request(this).apply(checkExists());
+ return listBranches.get().request(this).apply(checkExists()).value();
} catch (Exception e) {
throw asRestApiException("Cannot list branches", e);
}
@@ -459,7 +476,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public List<TagInfo> get() throws RestApiException {
try {
- return listTags.get().request(this).apply(checkExists());
+ return listTags.get().request(this).apply(checkExists()).value();
} catch (Exception e) {
throw asRestApiException("Cannot list tags", e);
}
@@ -475,7 +492,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public List<ProjectInfo> children(boolean recursive) throws RestApiException {
try {
- return children.list().withRecursive(recursive).apply(checkExists());
+ return children.list().withRecursive(recursive).apply(checkExists()).value();
} catch (Exception e) {
throw asRestApiException("Cannot list children", e);
}
@@ -484,7 +501,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public List<ProjectInfo> children(int limit) throws RestApiException {
try {
- return children.list().withLimit(limit).apply(checkExists());
+ return children.list().withLimit(limit).apply(checkExists()).value();
} catch (Exception e) {
throw asRestApiException("Cannot list children", e);
}
@@ -574,7 +591,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public List<DashboardInfo> get() throws RestApiException {
try {
- List<?> r = listDashboards.get().apply(checkExists());
+ List<?> r = listDashboards.get().apply(checkExists()).value();
if (r.isEmpty()) {
return Collections.emptyList();
}
@@ -592,7 +609,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public String head() throws RestApiException {
try {
- return getHead.apply(checkExists());
+ return getHead.apply(checkExists()).value();
} catch (Exception e) {
throw asRestApiException("Cannot get HEAD", e);
}
@@ -612,7 +629,7 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public String parent() throws RestApiException {
try {
- return getParent.apply(checkExists());
+ return getParent.apply(checkExists()).value();
} catch (Exception e) {
throw asRestApiException("Cannot get parent", e);
}
@@ -640,6 +657,15 @@ public class ProjectApiImpl implements ProjectApi {
}
}
+ @Override
+ public void indexChanges() throws RestApiException {
+ try {
+ indexChanges.apply(checkExists(), new Input());
+ } catch (Exception e) {
+ throw asRestApiException("Cannot index changes", e);
+ }
+ }
+
private ProjectResource checkExists() throws ResourceNotFoundException {
if (project == null) {
throw new ResourceNotFoundException(name);
diff --git a/java/com/google/gerrit/server/args4j/AccountGroupIdHandler.java b/java/com/google/gerrit/server/args4j/AccountGroupIdHandler.java
index 46a22c06ed..66d0224899 100644
--- a/java/com/google/gerrit/server/args4j/AccountGroupIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountGroupIdHandler.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.args4j;
import static com.google.gerrit.util.cli.Localizable.localizable;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
import com.google.inject.Inject;
@@ -45,7 +45,7 @@ public class AccountGroupIdHandler extends OptionHandler<AccountGroup.Id> {
@Override
public final int parseArguments(Parameters params) throws CmdLineException {
final String n = params.getParameter(0);
- Optional<InternalGroup> group = groupCache.get(new AccountGroup.NameKey(n));
+ Optional<InternalGroup> group = groupCache.get(AccountGroup.nameKey(n));
if (!group.isPresent()) {
throw new CmdLineException(owner, localizable("Group \"%s\" does not exist"), n);
}
diff --git a/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java b/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
index 2dd0c7ab5b..20e8441774 100644
--- a/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
@@ -18,7 +18,7 @@ import static com.google.gerrit.util.cli.Localizable.localizable;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.account.GroupCache;
@@ -53,7 +53,7 @@ public class AccountGroupUUIDHandler extends OptionHandler<AccountGroup.UUID> {
@Override
public final int parseArguments(Parameters params) throws CmdLineException {
final String n = params.getParameter(0);
- AccountGroup.UUID uuid = new AccountGroup.UUID(n);
+ AccountGroup.UUID uuid = AccountGroup.uuid(n);
if (groupBackend.handles(uuid)) {
GroupDescription.Basic d = groupBackend.get(uuid);
if (d != null) {
diff --git a/java/com/google/gerrit/server/args4j/AccountIdHandler.java b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
index 37e52f4c82..73a970ba86 100644
--- a/java/com/google/gerrit/server/args4j/AccountIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
@@ -17,10 +17,10 @@ package com.google.gerrit.server.args4j;
import static com.google.gerrit.util.cli.Localizable.localizable;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AccountResolver;
@@ -65,7 +65,7 @@ public class AccountIdHandler extends OptionHandler<Account.Id> {
Account.Id accountId;
try {
try {
- accountId = accountResolver.resolve(token).asUnique().getAccount().getId();
+ accountId = accountResolver.resolve(token).asUnique().account().id();
} catch (UnprocessableEntityException e) {
switch (authType) {
case HTTP_LDAP:
diff --git a/java/com/google/gerrit/server/args4j/ChangeIdHandler.java b/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
index ceba3e620d..448c654105 100644
--- a/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
@@ -18,10 +18,10 @@ import static com.google.gerrit.util.cli.Localizable.localizable;
import com.google.common.base.Splitter;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.inject.Inject;
@@ -62,8 +62,8 @@ public class ChangeIdHandler extends OptionHandler<Change.Id> {
try {
Change.Key key = Change.Key.parse(tokens.get(2));
- Project.NameKey project = new Project.NameKey(tokens.get(0));
- Branch.NameKey branch = new Branch.NameKey(project, tokens.get(1));
+ Project.NameKey project = Project.nameKey(tokens.get(0));
+ BranchNameKey branch = BranchNameKey.create(project, tokens.get(1));
List<ChangeData> changes = queryProvider.get().byBranchKey(branch, key);
if (!changes.isEmpty()) {
if (changes.size() > 1) {
diff --git a/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java b/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
index 4581fe02dc..8b7cbd6fd6 100644
--- a/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.args4j;
import static com.google.gerrit.util.cli.Localizable.localizable;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.PatchSet;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.kohsuke.args4j.CmdLineException;
diff --git a/java/com/google/gerrit/server/args4j/ProjectHandler.java b/java/com/google/gerrit/server/args4j/ProjectHandler.java
index e3af82b9ce..61dbd2c456 100644
--- a/java/com/google/gerrit/server/args4j/ProjectHandler.java
+++ b/java/com/google/gerrit/server/args4j/ProjectHandler.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.args4j;
import static com.google.gerrit.util.cli.Localizable.localizable;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ProjectUtil;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -72,7 +72,7 @@ public class ProjectHandler extends OptionHandler<ProjectState> {
}
String nameWithoutSuffix = ProjectUtil.stripGitSuffix(projectName);
- Project.NameKey nameKey = new Project.NameKey(nameWithoutSuffix);
+ Project.NameKey nameKey = Project.nameKey(nameWithoutSuffix);
ProjectState state;
try {
diff --git a/java/com/google/gerrit/server/audit/AuditService.java b/java/com/google/gerrit/server/audit/AuditService.java
index 425e22a7ec..2a5d8689f8 100644
--- a/java/com/google/gerrit/server/audit/AuditService.java
+++ b/java/com/google/gerrit/server/audit/AuditService.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.audit;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.AuditEvent;
import com.google.gerrit.server.audit.group.GroupAuditListener;
import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
diff --git a/java/com/google/gerrit/server/audit/BUILD b/java/com/google/gerrit/server/audit/BUILD
index c8b71a9be2..95929d365d 100644
--- a/java/com/google/gerrit/server/audit/BUILD
+++ b/java/com/google/gerrit/server/audit/BUILD
@@ -9,15 +9,9 @@ java_library(
resources = ["//resources/com/google/gerrit/server"],
visibility = ["//visibility:public"],
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
- "//java/com/google/gerrit/server/ioutil",
- "//java/com/google/gerrit/server/logging",
- "//java/com/google/gerrit/server/util/time",
- "//java/com/google/gerrit/util/cli",
- "//java/com/google/gerrit/util/ssl",
- "//java/org/apache/commons/net",
"//lib:args4j",
"//lib:autolink",
"//lib:automaton",
@@ -50,6 +44,8 @@ java_library(
"//lib:gson",
"//lib:guava",
"//lib:guava-retrying",
+ "//lib:jgit",
+ "//lib:jgit-archive",
"//lib:jsch",
"//lib:juniversalchardet",
"//lib:mime-util",
@@ -71,8 +67,6 @@ java_library(
"//lib/guice",
"//lib/guice:guice-assistedinject",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit.archive:jgit-archive",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/jsoup",
"//lib/log:jsonevent-layout",
"//lib/log:log4j",
diff --git a/java/com/google/gerrit/server/audit/group/GroupAuditEvent.java b/java/com/google/gerrit/server/audit/group/GroupAuditEvent.java
index ae26e12907..252a1e262b 100644
--- a/java/com/google/gerrit/server/audit/group/GroupAuditEvent.java
+++ b/java/com/google/gerrit/server/audit/group/GroupAuditEvent.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.audit.group;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.sql.Timestamp;
/** An audit event for groups. */
@@ -23,14 +23,14 @@ public interface GroupAuditEvent {
/**
* Gets the acting user who is updating the group.
*
- * @return the {@link com.google.gerrit.reviewdb.client.Account.Id} of the acting user.
+ * @return the {@link com.google.gerrit.entities.Account.Id} of the acting user.
*/
Account.Id getActor();
/**
- * Gets the {@link com.google.gerrit.reviewdb.client.AccountGroup.UUID} of the updated group.
+ * Gets the {@link com.google.gerrit.entities.AccountGroup.UUID} of the updated group.
*
- * @return the {@link com.google.gerrit.reviewdb.client.AccountGroup.UUID} of the updated group.
+ * @return the {@link com.google.gerrit.entities.AccountGroup.UUID} of the updated group.
*/
AccountGroup.UUID getUpdatedGroup();
diff --git a/java/com/google/gerrit/server/audit/group/GroupMemberAuditEvent.java b/java/com/google/gerrit/server/audit/group/GroupMemberAuditEvent.java
index a5c11bc1f3..eccfbf40ec 100644
--- a/java/com/google/gerrit/server/audit/group/GroupMemberAuditEvent.java
+++ b/java/com/google/gerrit/server/audit/group/GroupMemberAuditEvent.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.audit.group;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.sql.Timestamp;
@AutoValue
diff --git a/java/com/google/gerrit/server/audit/group/GroupSubgroupAuditEvent.java b/java/com/google/gerrit/server/audit/group/GroupSubgroupAuditEvent.java
index 0d5b26f9e6..0fe396280c 100644
--- a/java/com/google/gerrit/server/audit/group/GroupSubgroupAuditEvent.java
+++ b/java/com/google/gerrit/server/audit/group/GroupSubgroupAuditEvent.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.audit.group;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.sql.Timestamp;
@AutoValue
diff --git a/java/com/google/gerrit/server/auth/InternalAuthBackend.java b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
index c06c66bd8e..2f8886b88b 100644
--- a/java/com/google/gerrit/server/auth/InternalAuthBackend.java
+++ b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.auth;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.externalids.PasswordVerifier;
import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -55,14 +56,14 @@ public class InternalAuthBackend implements AuthBackend {
AccountState who = accountCache.getByUsername(username).orElseThrow(UnknownUserException::new);
- if (!who.getAccount().isActive()) {
+ if (!who.account().isActive()) {
throw new UserNotAllowedException(
"Authentication failed for "
+ username
+ ": account inactive or not provisioned in Gerrit");
}
- if (!who.checkPassword(req.getPassword().get(), username)) {
+ if (!PasswordVerifier.checkPassword(who.externalIds(), username, req.getPassword().get())) {
throw new InvalidCredentialsException();
}
return new AuthUser(AuthUser.UUID.create(username), username);
diff --git a/java/com/google/gerrit/server/auth/ldap/Helper.java b/java/com/google/gerrit/server/auth/ldap/Helper.java
index 4b14c385dc..5c6b391369 100644
--- a/java/com/google/gerrit/server/auth/ldap/Helper.java
+++ b/java/com/google/gerrit/server/auth/ldap/Helper.java
@@ -19,7 +19,7 @@ import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.ParameterizedString;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AuthenticationFailedException;
import com.google.gerrit.server.auth.NoSuchUserException;
@@ -321,7 +321,7 @@ class Helper {
final Set<AccountGroup.UUID> actual = new HashSet<>();
for (String dn : groupDNs) {
- actual.add(new AccountGroup.UUID(LDAP_UUID + dn));
+ actual.add(AccountGroup.uuid(LDAP_UUID + dn));
}
if (actual.isEmpty()) {
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java b/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
index 87a4abfacb..1d85a5eb7a 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
@@ -27,7 +27,7 @@ import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.ParameterizedString;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupBackend;
@@ -92,7 +92,7 @@ public class LdapGroupBackend implements GroupBackend {
private static GroupReference groupReference(ParameterizedString p, LdapQuery.Result res)
throws NamingException {
return new GroupReference(
- new AccountGroup.UUID(LDAP_UUID + res.getDN()), LDAP_NAME + LdapRealm.apply(p, res));
+ AccountGroup.uuid(LDAP_UUID + res.getDN()), LDAP_NAME + LdapRealm.apply(p, res));
}
private static String cnFor(String dn) {
@@ -164,7 +164,7 @@ public class LdapGroupBackend implements GroupBackend {
@Override
public Collection<GroupReference> suggest(String name, ProjectState project) {
- AccountGroup.UUID uuid = new AccountGroup.UUID(name);
+ AccountGroup.UUID uuid = AccountGroup.uuid(name);
if (isLdapUUID(uuid)) {
GroupDescription.Basic g = get(uuid);
if (g == null) {
@@ -179,7 +179,7 @@ public class LdapGroupBackend implements GroupBackend {
@Override
public GroupMembership membershipsOf(IdentifiedUser user) {
- String id = findId(user.state().getExternalIds());
+ String id = findId(user.state().externalIds());
if (id == null) {
return GroupMembership.EMPTY;
}
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapGroupMembership.java b/java/com/google/gerrit/server/auth/ldap/LdapGroupMembership.java
index f5406c25d3..a6aa2f6162 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapGroupMembership.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapGroupMembership.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.auth.ldap;
import com.google.common.cache.LoadingCache;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.account.ListGroupMembership;
import com.google.gerrit.server.project.ProjectCache;
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapModule.java b/java/com/google/gerrit/server/auth/ldap/LdapModule.java
index 3fbf04996f..092b5ac4a2 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapModule.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapModule.java
@@ -15,9 +15,9 @@
package com.google.gerrit.server.auth.ldap;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.cache.CacheModule;
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
index be6303dfca..1421f1792e 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -22,10 +22,10 @@ import com.google.common.cache.LoadingCache;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.ParameterizedString;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.client.AuthType;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AbstractRealm;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AuthRequest;
@@ -37,6 +37,7 @@ import com.google.gerrit.server.auth.AuthenticationUnavailableException;
import com.google.gerrit.server.auth.NoSuchUserException;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.inject.Inject;
@@ -301,7 +302,7 @@ class LdapRealm extends AbstractRealm {
@Override
public void onCreateAccount(AuthRequest who, Account account) {
- usernameCache.put(who.getLocalUser(), Optional.of(account.getId()));
+ usernameCache.put(who.getLocalUser(), Optional.of(account.id()));
}
@Override
@@ -353,7 +354,9 @@ class LdapRealm extends AbstractRealm {
@Override
public Optional<Account.Id> load(String username) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading account for username %s", username)) {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading account for username", Metadata.builder().username(username).build())) {
return externalIds
.get(ExternalId.Key.create(SCHEME_GERRIT, username))
.map(ExternalId::accountId);
@@ -372,7 +375,9 @@ class LdapRealm extends AbstractRealm {
@Override
public Set<AccountGroup.UUID> load(String username) throws Exception {
try (TraceTimer timer =
- TraceContext.newTimer("Loading group for member with username %s", username)) {
+ TraceContext.newTimer(
+ "Loading group for member with username",
+ Metadata.builder().username(username).build())) {
final DirContext ctx = helper.open();
try {
return helper.queryForGroups(ctx, username, null);
@@ -393,7 +398,9 @@ class LdapRealm extends AbstractRealm {
@Override
public Boolean load(String groupDn) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading groupDn %s", groupDn)) {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading groupDn", Metadata.builder().authDomainName(groupDn).build())) {
final DirContext ctx = helper.open();
try {
Name compositeGroupName = new CompositeName().add(groupDn);
diff --git a/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java b/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java
index 2cef62d723..944bd44e05 100644
--- a/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java
+++ b/java/com/google/gerrit/server/auth/oauth/OAuthRealm.java
@@ -17,11 +17,11 @@ package com.google.gerrit.server.auth.oauth;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_EXTERNAL;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.auth.oauth.OAuthLoginProvider;
import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AbstractRealm;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
diff --git a/java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java b/java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java
index 0980116c7a..f58b0f7cca 100644
--- a/java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java
+++ b/java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java
@@ -17,17 +17,18 @@ package com.google.gerrit.server.auth.oauth;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Converter;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.auth.oauth.OAuthToken;
import com.google.gerrit.extensions.auth.oauth.OAuthTokenEncrypter;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.proto.Cache.OAuthTokenProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
-import com.google.gerrit.server.cache.serialize.IntKeyCacheSerializer;
+import com.google.gerrit.server.cache.serialize.IntegerCacheSerializer;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
@@ -45,7 +46,9 @@ public class OAuthTokenCache {
protected void configure() {
persist(OAUTH_TOKENS, Account.Id.class, OAuthToken.class)
.version(1)
- .keySerializer(new IntKeyCacheSerializer<>(Account.Id::new))
+ .keySerializer(
+ CacheSerializer.convert(
+ IntegerCacheSerializer.INSTANCE, Converter.from(Account.Id::get, Account::id)))
.valueSerializer(new Serializer());
}
};
diff --git a/java/com/google/gerrit/server/cache/CacheMetrics.java b/java/com/google/gerrit/server/cache/CacheMetrics.java
index 063ddbc660..12194e75cd 100644
--- a/java/com/google/gerrit/server/cache/CacheMetrics.java
+++ b/java/com/google/gerrit/server/cache/CacheMetrics.java
@@ -26,6 +26,7 @@ import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.logging.Metadata;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Set;
@@ -33,7 +34,8 @@ import org.eclipse.jgit.lib.Config;
@Singleton
public class CacheMetrics {
- private static final Field<String> F_NAME = Field.ofString("cache_name");
+ private static final Field<String> F_NAME =
+ Field.ofString("cache_name", Metadata.Builder::cacheName).build();
@Inject
public CacheMetrics(
diff --git a/java/com/google/gerrit/server/cache/h2/BUILD b/java/com/google/gerrit/server/cache/h2/BUILD
index a191f75fcb..5e64aa765d 100644
--- a/java/com/google/gerrit/server/cache/h2/BUILD
+++ b/java/com/google/gerrit/server/cache/h2/BUILD
@@ -14,8 +14,8 @@ java_library(
"//java/com/google/gerrit/server/util/time",
"//lib:guava",
"//lib:h2",
+ "//lib:jgit",
"//lib/flogger:api",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
index 3732e37e19..ef4e44cd4f 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
@@ -26,6 +26,7 @@ import com.google.common.hash.BloomFilter;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.server.cache.PersistentCache;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -237,7 +238,9 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements Per
@Override
public ValueHolder<V> load(K key) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading value for %s from cache", key)) {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading value from cache", Metadata.builder().cacheKey(key.toString()).build())) {
if (store.mightContain(key)) {
ValueHolder<V> h = store.getIfPresent(key);
if (h != null) {
diff --git a/java/com/google/gerrit/server/cache/mem/BUILD b/java/com/google/gerrit/server/cache/mem/BUILD
index bc5b66ac6b..a666df7bc8 100644
--- a/java/com/google/gerrit/server/cache/mem/BUILD
+++ b/java/com/google/gerrit/server/cache/mem/BUILD
@@ -11,7 +11,7 @@ java_library(
"//lib:caffeine",
"//lib:caffeine-guava",
"//lib:guava",
+ "//lib:jgit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/server/cache/serialize/BUILD b/java/com/google/gerrit/server/cache/serialize/BUILD
index 76dcbb1c28..aa9106bfb8 100644
--- a/java/com/google/gerrit/server/cache/serialize/BUILD
+++ b/java/com/google/gerrit/server/cache/serialize/BUILD
@@ -6,10 +6,10 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/git",
"//java/com/google/gerrit/proto",
- "//java/com/google/gwtorm",
"//lib:guava",
+ "//lib:jgit",
"//lib:protobuf",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/server/cache/serialize/CacheSerializer.java b/java/com/google/gerrit/server/cache/serialize/CacheSerializer.java
index 2d41f2c50b..5377fc1f5b 100644
--- a/java/com/google/gerrit/server/cache/serialize/CacheSerializer.java
+++ b/java/com/google/gerrit/server/cache/serialize/CacheSerializer.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.cache.serialize;
+import com.google.common.base.Converter;
+
/**
* Interface for serializing/deserializing a type to/from a persistent cache.
*
@@ -22,6 +24,27 @@ package com.google.gerrit.server.cache.serialize;
*/
public interface CacheSerializer<T> {
/**
+ * Convert a serializer of one type to another type using a {@link Converter}.
+ *
+ * @param delegate underlying serializer.
+ * @param converter converter between an arbitrary type {@code T} and {@code delegate}'s type.
+ * @return serializer of type {@code T}.
+ */
+ static <T, D> CacheSerializer<T> convert(CacheSerializer<D> delegate, Converter<T, D> converter) {
+ return new CacheSerializer<T>() {
+ @Override
+ public byte[] serialize(T object) {
+ return delegate.serialize(converter.convert(object));
+ }
+
+ @Override
+ public T deserialize(byte[] in) {
+ return converter.reverse().convert(delegate.deserialize(in));
+ }
+ };
+ }
+
+ /**
* Serializes the object to a new byte array.
*
* @param object object to serialize.
diff --git a/java/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializer.java b/java/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializer.java
deleted file mode 100644
index 85530f4a0e..0000000000
--- a/java/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.cache.serialize;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.gwtorm.client.IntKey;
-import java.util.function.Function;
-
-public class IntKeyCacheSerializer<K extends IntKey<?>> implements CacheSerializer<K> {
- private final Function<Integer, K> factory;
-
- public IntKeyCacheSerializer(Function<Integer, K> factory) {
- this.factory = requireNonNull(factory);
- }
-
- @Override
- public byte[] serialize(K object) {
- return IntegerCacheSerializer.INSTANCE.serialize(object.get());
- }
-
- @Override
- public K deserialize(byte[] in) {
- return factory.apply(IntegerCacheSerializer.INSTANCE.deserialize(in));
- }
-}
diff --git a/java/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializer.java b/java/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializer.java
index 500875dcb8..7c0f84f832 100644
--- a/java/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializer.java
+++ b/java/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializer.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.cache.serialize;
-import org.eclipse.jgit.lib.Constants;
+import com.google.gerrit.git.ObjectIds;
import org.eclipse.jgit.lib.ObjectId;
public enum ObjectIdCacheSerializer implements CacheSerializer<ObjectId> {
@@ -22,14 +22,14 @@ public enum ObjectIdCacheSerializer implements CacheSerializer<ObjectId> {
@Override
public byte[] serialize(ObjectId object) {
- byte[] buf = new byte[Constants.OBJECT_ID_LENGTH];
+ byte[] buf = new byte[ObjectIds.LEN];
object.copyRawTo(buf, 0);
return buf;
}
@Override
public ObjectId deserialize(byte[] in) {
- if (in == null || in.length != Constants.OBJECT_ID_LENGTH) {
+ if (in == null || in.length != ObjectIds.LEN) {
throw new IllegalArgumentException("Failed to deserialize ObjectId");
}
return ObjectId.fromRaw(in);
diff --git a/java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java b/java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java
index eb946a9a4a..22654e5bf2 100644
--- a/java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java
+++ b/java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.cache.serialize;
import static com.google.common.base.Preconditions.checkArgument;
-import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import com.google.gerrit.git.ObjectIds;
import com.google.protobuf.ByteString;
import org.eclipse.jgit.lib.ObjectId;
@@ -35,7 +35,7 @@ public class ObjectIdConverter {
return new ObjectIdConverter();
}
- private final byte[] buf = new byte[OBJECT_ID_LENGTH];
+ private final byte[] buf = new byte[ObjectIds.LEN];
private ObjectIdConverter() {}
@@ -46,10 +46,7 @@ public class ObjectIdConverter {
public ObjectId fromByteString(ByteString in) {
checkArgument(
- in.size() == OBJECT_ID_LENGTH,
- "expected ByteString of length %s: %s",
- OBJECT_ID_LENGTH,
- in);
+ in.size() == ObjectIds.LEN, "expected ByteString of length %s: %s", ObjectIds.LEN, in);
in.copyTo(buf, 0);
return ObjectId.fromRaw(buf);
}
diff --git a/java/com/google/gerrit/server/change/AbandonOp.java b/java/com/google/gerrit/server/change/AbandonOp.java
index 5ee5bc7fa1..eb6e8d7062 100644
--- a/java/com/google/gerrit/server/change/AbandonOp.java
+++ b/java/com/google/gerrit/server/change/AbandonOp.java
@@ -17,10 +17,10 @@ package com.google.gerrit.server.change;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PatchSetUtil;
@@ -112,7 +112,7 @@ public class AbandonOp implements BatchUpdateOp {
try {
ReplyToChangeSender cm = abandonedSenderFactory.create(ctx.getProject(), change.getId());
if (accountState != null) {
- cm.setFrom(accountState.getAccount().getId());
+ cm.setFrom(accountState.account().id());
}
cm.setChangeMessage(message.getMessage(), ctx.getWhen());
cm.setNotify(notify);
diff --git a/java/com/google/gerrit/server/change/AbandonUtil.java b/java/com/google/gerrit/server/change/AbandonUtil.java
index 6f464984a4..1bc1fad379 100644
--- a/java/com/google/gerrit/server/change/AbandonUtil.java
+++ b/java/com/google/gerrit/server/change/AbandonUtil.java
@@ -17,9 +17,9 @@ package com.google.gerrit.server.change;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.config.ChangeCleanupConfig;
import com.google.gerrit.server.query.change.ChangeData;
@@ -40,7 +40,10 @@ public class AbandonUtil {
private final ChangeCleanupConfig cfg;
private final Provider<ChangeQueryProcessor> queryProvider;
- private final ChangeQueryBuilder queryBuilder;
+ // Provider is needed, because AbandonUtil is singleton, but ChangeQueryBuilder accesses
+ // index collection, that is only provided when multiversion index module is started.
+ // TODO(davido); Remove provider again, when support for legacy numeric fields is removed.
+ private final Provider<ChangeQueryBuilder> queryBuilderProvider;
private final BatchAbandon batchAbandon;
private final InternalUser internalUser;
@@ -49,11 +52,11 @@ public class AbandonUtil {
ChangeCleanupConfig cfg,
InternalUser.Factory internalUserFactory,
Provider<ChangeQueryProcessor> queryProvider,
- ChangeQueryBuilder queryBuilder,
+ Provider<ChangeQueryBuilder> queryBuilderProvider,
BatchAbandon batchAbandon) {
this.cfg = cfg;
this.queryProvider = queryProvider;
- this.queryBuilder = queryBuilder;
+ this.queryBuilderProvider = queryBuilderProvider;
this.batchAbandon = batchAbandon;
internalUser = internalUserFactory.create();
}
@@ -71,7 +74,11 @@ public class AbandonUtil {
}
List<ChangeData> changesToAbandon =
- queryProvider.get().enforceVisibility(false).query(queryBuilder.parse(query)).entities();
+ queryProvider
+ .get()
+ .enforceVisibility(false)
+ .query(queryBuilderProvider.get().parse(query))
+ .entities();
ImmutableListMultimap.Builder<Project.NameKey, ChangeData> builder =
ImmutableListMultimap.builder();
for (ChangeData cd : changesToAbandon) {
@@ -111,7 +118,7 @@ public class AbandonUtil {
queryProvider
.get()
.enforceVisibility(false)
- .query(queryBuilder.parse(newQuery))
+ .query(queryBuilderProvider.get().parse(newQuery))
.entities();
if (!changesToAbandon.isEmpty()) {
validChanges.add(cd);
diff --git a/java/com/google/gerrit/server/change/AccountPatchReviewStore.java b/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
index fff3274447..8da2a90660 100644
--- a/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.change;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import java.util.Collection;
import java.util.Optional;
@@ -30,7 +30,8 @@ import java.util.Optional;
* number of reviewed flags is growing without bound. The store must be able handle this data volume
* efficiently.
*
- * <p>For a multi-master setup the store must replicate the data between the masters.
+ * <p>For a cluster setups with multiple primary nodes the store must replicate the data between the
+ * primary servers.
*/
public interface AccountPatchReviewStore {
diff --git a/java/com/google/gerrit/server/change/AddReviewersEmail.java b/java/com/google/gerrit/server/change/AddReviewersEmail.java
index d9c5dade4f..664b84ddc0 100644
--- a/java/com/google/gerrit/server/change/AddReviewersEmail.java
+++ b/java/com/google/gerrit/server/change/AddReviewersEmail.java
@@ -18,9 +18,9 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.mail.send.AddReviewerSender;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/AddReviewersOp.java b/java/com/google/gerrit/server/change/AddReviewersOp.java
index 610290dfeb..87d34a4712 100644
--- a/java/com/google/gerrit/server/change/AddReviewersOp.java
+++ b/java/com/google/gerrit/server/change/AddReviewersOp.java
@@ -27,13 +27,13 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountCache;
@@ -64,10 +64,14 @@ public class AddReviewersOp implements BatchUpdateOp {
* @param accountIds account IDs to add.
* @param addresses email addresses to add.
* @param state resulting reviewer state.
+ * @param forGroup whether this reviewer addition adds accounts for a group
* @return batch update operation.
*/
AddReviewersOp create(
- Set<Account.Id> accountIds, Collection<Address> addresses, ReviewerState state);
+ Set<Account.Id> accountIds,
+ Collection<Address> addresses,
+ ReviewerState state,
+ boolean forGroup);
}
@AutoValue
@@ -107,6 +111,7 @@ public class AddReviewersOp implements BatchUpdateOp {
private final Set<Account.Id> accountIds;
private final Collection<Address> addresses;
private final ReviewerState state;
+ private final boolean forGroup;
// Unlike addedCCs, addedReviewers is a PatchSetApproval because the AddReviewerResult returned
// via the REST API is supposed to include vote information.
@@ -130,7 +135,8 @@ public class AddReviewersOp implements BatchUpdateOp {
AddReviewersEmail addReviewersEmail,
@Assisted Set<Account.Id> accountIds,
@Assisted Collection<Address> addresses,
- @Assisted ReviewerState state) {
+ @Assisted ReviewerState state,
+ @Assisted boolean forGroup) {
checkArgument(state == REVIEWER || state == CC, "must be %s or %s: %s", REVIEWER, CC, state);
this.approvalsUtil = approvalsUtil;
this.psUtil = psUtil;
@@ -142,6 +148,7 @@ public class AddReviewersOp implements BatchUpdateOp {
this.accountIds = accountIds;
this.addresses = addresses;
this.state = state;
+ this.forGroup = forGroup;
}
// TODO(dborowitz): This mutable setter is ugly, but a) it's less ugly than adding boolean args
@@ -162,7 +169,7 @@ public class AddReviewersOp implements BatchUpdateOp {
if (state == CC) {
addedCCs =
approvalsUtil.addCcs(
- ctx.getNotes(), ctx.getUpdate(change.currentPatchSetId()), accountIds);
+ ctx.getNotes(), ctx.getUpdate(change.currentPatchSetId()), accountIds, forGroup);
} else {
addedReviewers =
approvalsUtil.addReviewers(
@@ -174,12 +181,11 @@ public class AddReviewersOp implements BatchUpdateOp {
}
}
- ImmutableList<Address> addressesToAdd = ImmutableList.of();
ReviewerStateInternal internalState = ReviewerStateInternal.fromReviewerState(state);
// TODO(dborowitz): This behavior should live in ApprovalsUtil or something, like addCcs does.
ImmutableSet<Address> existing = ctx.getNotes().getReviewersByEmail().byState(internalState);
- addressesToAdd =
+ ImmutableList<Address> addressesToAdd =
addresses.stream().filter(a -> !existing.contains(a)).collect(toImmutableList());
if (state == CC) {
@@ -240,7 +246,7 @@ public class AddReviewersOp implements BatchUpdateOp {
addReviewersEmail.emailReviewers(
ctx.getUser().asIdentifiedUser(),
change,
- Lists.transform(addedReviewers, PatchSetApproval::getAccountId),
+ Lists.transform(addedReviewers, PatchSetApproval::accountId),
addedCCs,
addedReviewersByEmail,
addedCCsByEmail,
@@ -249,7 +255,7 @@ public class AddReviewersOp implements BatchUpdateOp {
if (!addedReviewers.isEmpty()) {
List<AccountState> reviewers =
addedReviewers.stream()
- .map(r -> accountCache.get(r.getAccountId()))
+ .map(r -> accountCache.get(r.accountId()))
.flatMap(Streams::stream)
.collect(toList());
reviewerAdded.fire(change, patchSet, reviewers, ctx.getAccount(), ctx.getWhen());
diff --git a/java/com/google/gerrit/server/change/ArchiveFormat.java b/java/com/google/gerrit/server/change/ArchiveFormat.java
index 0316c5f36a..d895a66d48 100644
--- a/java/com/google/gerrit/server/change/ArchiveFormat.java
+++ b/java/com/google/gerrit/server/change/ArchiveFormat.java
@@ -35,7 +35,9 @@ public enum ArchiveFormat {
TXZ("application/x-xz", new TxzFormat()),
ZIP("application/x-zip", new ZipFormat());
+ @SuppressWarnings("ImmutableEnumChecker") // ArchiveCommand.Format is effectively immutable.
private final ArchiveCommand.Format<?> format;
+
private final String mimeType;
ArchiveFormat(String mimeType, ArchiveCommand.Format<?> format) {
diff --git a/java/com/google/gerrit/server/change/BatchAbandon.java b/java/com/google/gerrit/server/change/BatchAbandon.java
index bc29c5dc54..e0a72ac42d 100644
--- a/java/com/google/gerrit/server/change/BatchAbandon.java
+++ b/java/com/google/gerrit/server/change/BatchAbandon.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.config.ChangeCleanupConfig;
diff --git a/java/com/google/gerrit/server/change/ChangeETagComputation.java b/java/com/google/gerrit/server/change/ChangeETagComputation.java
new file mode 100644
index 0000000000..a5b7d49f80
--- /dev/null
+++ b/java/com/google/gerrit/server/change/ChangeETagComputation.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/**
+ * Allows plugins to contribute a value to the change ETag computation.
+ *
+ * <p>Plugins can affect the result of the get change / get change details REST endpoints by:
+ *
+ * <ul>
+ * <li>providing plugin defined attributes to {@link
+ * com.google.gerrit.extensions.common.ChangeInfo#plugins} (see {@link
+ * ChangeAttributeFactory})
+ * <li>implementing a {@link com.google.gerrit.server.rules.SubmitRule} which affects the
+ * computation of {@link com.google.gerrit.extensions.common.ChangeInfo#submittable}
+ * </ul>
+ *
+ * <p>If the plugin defined part of {@link com.google.gerrit.extensions.common.ChangeInfo} depends
+ * on plugin specific data, callers that use the change ETags to avoid unneeded recomputations of
+ * ChangeInfos may see outdated plugin attributes and/or outdated submittable information, because a
+ * ChangeInfo is only reloaded if the change ETag changes.
+ *
+ * <p>By implementating this interface plugins can contribute to the change ETag computation and
+ * thus ensure that the ETag changes when the plugin data was changed. This way it is ensured that
+ * callers do not see outdated ChangeInfos.
+ *
+ * @see ChangeResource#getETag()
+ */
+@ExtensionPoint
+public interface ChangeETagComputation {
+ /**
+ * Computes an ETag of plugin-specific data for the given change.
+ *
+ * <p><strong>Note:</strong> Change ETags are computed very frequently and the computation must be
+ * cheap. Take good care to not perform any expensive computations when implementing this.
+ *
+ * <p>If an error is encountered during the ETag computation the plugin can indicate this by
+ * throwing any RuntimeException. In this case no value will be included in the change ETag
+ * computation. This means if the error is transient, the ETag will differ when the computation
+ * succeeds on a follow-up run.
+ *
+ * @param projectName the name of the project that contains the change
+ * @param changeId ID of the change for which the ETag should be computed
+ * @return the ETag
+ */
+ String getETag(Project.NameKey projectName, Change.Id changeId);
+}
diff --git a/java/com/google/gerrit/server/change/ChangeFinder.java b/java/com/google/gerrit/server/change/ChangeFinder.java
index 751d4b89b8..8d2d83dc79 100644
--- a/java/com/google/gerrit/server/change/ChangeFinder.java
+++ b/java/com/google/gerrit/server/change/ChangeFinder.java
@@ -19,17 +19,18 @@ import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.metrics.Counter1;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData;
@@ -91,7 +92,8 @@ public class ChangeFinder {
new Description("Total number of API calls per identifier type.")
.setRate()
.setUnit("requests"),
- Field.ofEnum(ChangeIdType.class, "change_id_type"));
+ Field.ofEnum(ChangeIdType.class, "change_id_type", Metadata.Builder::changeIdType)
+ .build());
}
public ChangeNotes findOne(String id) {
@@ -129,7 +131,7 @@ public class ChangeFinder {
Integer n = Ints.tryParse(id);
if (n != null) {
changeIdCounter.increment(ChangeIdType.NUMERIC_ID);
- return find(new Change.Id(n));
+ return find(Change.id(n));
}
}
@@ -138,7 +140,7 @@ public class ChangeFinder {
InternalChangeQuery query = queryProvider.get().noFields();
// Try commit hash
- if (id.matches("^([0-9a-fA-F]{" + RevId.ABBREV_LEN + "," + RevId.LEN + "})$")) {
+ if (id.matches("^([0-9a-fA-F]{" + ObjectIds.ABBREV_STR_LEN + "," + ObjectIds.STR_LEN + "})$")) {
changeIdCounter.increment(ChangeIdType.COMMIT_HASH);
return asChangeNotes(query.byCommit(id));
}
@@ -162,7 +164,7 @@ public class ChangeFinder {
}
private List<ChangeNotes> fromProjectNumber(String project, int changeNumber) {
- Change.Id cId = new Change.Id(changeNumber);
+ Change.Id cId = Change.id(changeNumber);
try {
return ImmutableList.of(
changeNotesFactory.createChecked(Project.NameKey.parse(project), cId));
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index a3b5db0e4e..a00f1f8500 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.change;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.reviewdb.client.Change.INITIAL_PATCH_SET_ID;
+import static com.google.gerrit.entities.Change.INITIAL_PATCH_SET_ID;
import static com.google.gerrit.server.change.ReviewerAdder.newAddReviewerInputFromCommitIdentity;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import static java.util.Objects.requireNonNull;
@@ -30,18 +30,18 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.PatchSetInfo;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.PatchSetUtil;
@@ -168,7 +168,7 @@ public class ChangeInserter implements InsertChangeOp {
this.reviewerAdder = reviewerAdder;
this.changeId = changeId;
- this.psId = new PatchSet.Id(changeId, INITIAL_PATCH_SET_ID);
+ this.psId = PatchSet.id(changeId, INITIAL_PATCH_SET_ID);
this.commitId = commitId.copy();
this.refName = refName;
this.reviewerInputs = ImmutableList.of();
@@ -185,7 +185,7 @@ public class ChangeInserter implements InsertChangeOp {
getChangeKey(ctx.getRevWalk(), commitId),
changeId,
ctx.getAccountId(),
- new Branch.NameKey(ctx.getProject(), refName),
+ BranchNameKey.create(ctx.getProject(), refName),
ctx.getWhen());
change.setStatus(MoreObjects.firstNonNull(status, Change.Status.NEW));
change.setTopic(topic);
@@ -201,7 +201,7 @@ public class ChangeInserter implements InsertChangeOp {
rw.parseBody(commit);
List<String> idList = commit.getFooterLines(FooterConstants.CHANGE_ID);
if (!idList.isEmpty()) {
- return new Change.Key(idList.get(idList.size() - 1).trim());
+ return Change.key(idList.get(idList.size() - 1).trim());
}
// A Change-Id is generated for the review, but not appended to the commit message.
// This can happen if requireChangeId is false.
@@ -370,7 +370,7 @@ public class ChangeInserter implements InsertChangeOp {
ChangeUpdate update = ctx.getUpdate(psId);
update.setChangeId(change.getKey().get());
update.setSubjectForCommit("Create change");
- update.setBranch(change.getDest().get());
+ update.setBranch(change.getDest().branch());
update.setTopic(change.getTopic());
update.setPsDescription(patchSetDescription);
update.setPrivate(isPrivate);
@@ -419,9 +419,9 @@ public class ChangeInserter implements InsertChangeOp {
if (message != null) {
changeMessage =
ChangeMessagesUtil.newMessage(
- patchSet.getId(),
+ patchSet.id(),
ctx.getUser(),
- patchSet.getCreatedOn(),
+ patchSet.createdOn(),
message,
ChangeMessagesUtil.uploadedPatchSetTag(workInProgress));
cmUtil.addChangeMessage(update, changeMessage);
@@ -446,7 +446,7 @@ public class ChangeInserter implements InsertChangeOp {
cm.setNotify(notify);
cm.addReviewers(
reviewerAdditions.flattenResults(AddReviewersOp.Result::addedReviewers).stream()
- .map(PatchSetApproval::getAccountId)
+ .map(PatchSetApproval::accountId)
.collect(toImmutableSet()));
cm.addReviewersByEmail(
reviewerAdditions.flattenResults(AddReviewersOp.Result::addedReviewersByEmail));
@@ -511,14 +511,14 @@ public class ChangeInserter implements InsertChangeOp {
new CommitReceivedEvent(
cmd,
projectState.getProject(),
- change.getDest().get(),
+ change.getDest().branch(),
ctx.getRevWalk().getObjectReader(),
commitId,
ctx.getIdentifiedUser())) {
commitValidatorsFactory
.forGerritCommits(
permissionBackend.user(ctx.getUser()).project(ctx.getProject()),
- new Branch.NameKey(ctx.getProject(), refName),
+ BranchNameKey.create(ctx.getProject(), refName),
ctx.getIdentifiedUser(),
new NoSshInfo(),
ctx.getRevWalk(),
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 1008a307d3..3b7a2c480a 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -28,6 +28,7 @@ import static com.google.gerrit.extensions.client.ListChangesOption.LABELS;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import static com.google.gerrit.extensions.client.ListChangesOption.REVIEWED;
import static com.google.gerrit.extensions.client.ListChangesOption.REVIEWER_UPDATES;
+import static com.google.gerrit.extensions.client.ListChangesOption.SKIP_DIFFSTAT;
import static com.google.gerrit.extensions.client.ListChangesOption.SKIP_MERGEABLE;
import static com.google.gerrit.extensions.client.ListChangesOption.SUBMITTABLE;
import static com.google.gerrit.extensions.client.ListChangesOption.TRACKING_IDS;
@@ -48,6 +49,12 @@ import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRecord.Status;
import com.google.gerrit.common.data.SubmitRequirement;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.FixInput;
import com.google.gerrit.extensions.client.ListChangesOption;
@@ -69,12 +76,6 @@ import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer0;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GpgException;
@@ -286,7 +287,7 @@ public class ChangeJson {
public ChangeInfo format(RevisionResource rsrc) {
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
- return format(cd, Optional.of(rsrc.getPatchSet().getId()), true, ChangeInfo::new);
+ return format(cd, Optional.of(rsrc.getPatchSet().id()), true, ChangeInfo::new);
}
public List<List<ChangeInfo>> format(List<QueryResult<ChangeData>> in)
@@ -434,7 +435,7 @@ public class ChangeJson {
info = format(cd, Optional.empty(), false, ChangeInfo::new);
changeInfos.add(info);
if (isCacheable) {
- cache.put(new Change.Id(info._number), info);
+ cache.put(Change.id(info._number), info);
}
} catch (RuntimeException e) {
logger.atWarning().withCause(e).log(
@@ -465,7 +466,7 @@ public class ChangeJson {
Change c = result.change();
if (c != null) {
info.project = c.getProject().get();
- info.branch = c.getDest().getShortName();
+ info.branch = c.getDest().shortName();
info.topic = c.getTopic();
info.changeId = c.getKey().get();
info.subject = c.getSubject();
@@ -513,7 +514,7 @@ public class ChangeJson {
Change in = cd.change();
out.project = in.getProject().get();
- out.branch = in.getDest().getShortName();
+ out.branch = in.getDest().shortName();
out.topic = in.getTopic();
out.assignee = in.getAssignee() != null ? accountLoader.get(in.getAssignee()) : null;
out.hashtags = cd.hashtags();
@@ -530,10 +531,12 @@ public class ChangeJson {
out.submittable = submittable(cd);
}
}
- Optional<ChangedLines> changedLines = cd.changedLines();
- if (changedLines.isPresent()) {
- out.insertions = changedLines.get().insertions;
- out.deletions = changedLines.get().deletions;
+ if (!has(SKIP_DIFFSTAT)) {
+ Optional<ChangedLines> changedLines = cd.changedLines();
+ if (changedLines.isPresent()) {
+ out.insertions = changedLines.get().insertions;
+ out.deletions = changedLines.get().deletions;
+ }
}
out.isPrivate = in.isPrivate() ? true : null;
out.workInProgress = in.isWorkInProgress() ? true : null;
@@ -670,8 +673,8 @@ public class ChangeJson {
if (!s.isPresent()) {
return;
}
- out.submitted = s.get().getGranted();
- out.submitter = accountLoader.get(s.get().getAccountId());
+ out.submitted = s.get().granted();
+ out.submitter = accountLoader.get(s.get().accountId());
}
private Collection<ChangeMessageInfo> messages(ChangeData cd) {
@@ -713,7 +716,7 @@ public class ChangeJson {
continue;
}
for (ApprovalInfo ai : label.all) {
- Account.Id id = new Account.Id(ai._accountId);
+ Account.Id id = Account.id(ai._accountId);
if (canRemoveAnyReviewer
|| removeReviewerControl.testRemoveReviewer(
@@ -733,7 +736,7 @@ public class ChangeJson {
if (ccs != null) {
for (AccountInfo ai : ccs) {
if (ai._accountId != null) {
- Account.Id id = new Account.Id(ai._accountId);
+ Account.Id id = Account.id(ai._accountId);
if (canRemoveAnyReviewer
|| removeReviewerControl.testRemoveReviewer(cd, userProvider.get(), id, 0)) {
removable.add(id);
@@ -798,7 +801,7 @@ public class ChangeJson {
}
Map<PatchSet.Id, PatchSet> map = Maps.newHashMapWithExpectedSize(src.size());
for (PatchSet patchSet : src) {
- map.put(patchSet.getId(), patchSet);
+ map.put(patchSet.id(), patchSet);
}
return map;
}
diff --git a/java/com/google/gerrit/server/change/ChangeKeyAdapter.java b/java/com/google/gerrit/server/change/ChangeKeyAdapter.java
new file mode 100644
index 0000000000..0db4ceab52
--- /dev/null
+++ b/java/com/google/gerrit/server/change/ChangeKeyAdapter.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.entities.Change;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+
+/**
+ * Adapter that serializes {@link com.google.gerrit.entities.Change.Key}'s {@code key} field as
+ * {@code id}, for backwards compatibility in stream-events.
+ */
+// TODO(dborowitz): auto-value-gson should support this directly using @SerializedName on the
+// AutoValue method.
+public class ChangeKeyAdapter implements JsonSerializer<Change.Key>, JsonDeserializer<Change.Key> {
+ @Override
+ public JsonElement serialize(Change.Key src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject obj = new JsonObject();
+ obj.addProperty("id", src.get());
+ return obj;
+ }
+
+ @Override
+ public Change.Key deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ JsonElement keyJson = json.getAsJsonObject().get("id");
+ if (keyJson == null || !keyJson.isJsonPrimitive() || !keyJson.getAsJsonPrimitive().isString()) {
+ throw new JsonParseException("Key is not a string: " + keyJson);
+ }
+ String key = keyJson.getAsJsonPrimitive().getAsString();
+ return Change.key(key);
+ }
+}
diff --git a/java/com/google/gerrit/server/change/ChangeKindCache.java b/java/com/google/gerrit/server/change/ChangeKindCache.java
index 44da4d6094..9bd7ad74de 100644
--- a/java/com/google/gerrit/server/change/ChangeKindCache.java
+++ b/java/com/google/gerrit/server/change/ChangeKindCache.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.change;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ChangeKind;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.query.change.ChangeData;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
diff --git a/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
index c20c0a27d0..1e149548db 100644
--- a/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
+++ b/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -23,12 +23,12 @@ import com.google.common.cache.Weigher;
import com.google.common.collect.FluentIterable;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.proto.Cache.ChangeKindKeyProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
@@ -374,13 +374,13 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
ChangeKind kind = ChangeKind.REWORK;
// Trivial case: if we're on the first patch, we don't need to use
// the repository.
- if (patch.getId().get() > 1) {
+ if (patch.id().get() > 1) {
try {
Collection<PatchSet> patchSetCollection = change.patchSets();
PatchSet priorPs = patch;
for (PatchSet ps : patchSetCollection) {
- if (ps.getId().get() < patch.getId().get()
- && (ps.getId().get() > priorPs.getId().get() || priorPs == patch)) {
+ if (ps.id().get() < patch.id().get()
+ && (ps.id().get() > priorPs.id().get() || priorPs == patch)) {
// We only want the previous patch set, so walk until the last one
priorPs = ps;
}
@@ -393,22 +393,17 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
if (priorPs != patch) {
kind =
cache.getChangeKind(
- change.project(),
- rw,
- repoConfig,
- ObjectId.fromString(priorPs.getRevision().get()),
- ObjectId.fromString(patch.getRevision().get()));
+ change.project(), rw, repoConfig, priorPs.commitId(), patch.commitId());
}
} catch (StorageException e) {
// Do nothing; assume we have a complex change
logger.atWarning().withCause(e).log(
"Unable to get change kind for patchSet %s of change %s",
- patch.getPatchSetId(), change.getId());
+ patch.number(), change.getId());
}
}
logger.atFine().log(
- "Change kind for patchSet %s of change %s: %s",
- patch.getPatchSetId(), change.getId(), kind);
+ "Change kind for patchSet %s of change %s: %s", patch.number(), change.getId(), kind);
return kind;
}
@@ -422,7 +417,7 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
ChangeKind kind = ChangeKind.REWORK;
// Trivial case: if we're on the first patch, we don't need to open
// the repository.
- if (patch.getId().get() > 1) {
+ if (patch.id().get() > 1) {
try (Repository repo = repoManager.openRepository(change.getProject());
RevWalk rw = new RevWalk(repo)) {
kind =
@@ -432,12 +427,11 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
// Do nothing; assume we have a complex change
logger.atWarning().withCause(e).log(
"Unable to get change kind for patchSet %s of change %s",
- patch.getPatchSetId(), change.getChangeId());
+ patch.number(), change.getChangeId());
}
}
logger.atFine().log(
- "Change kind for patchSet %s of change %s: %s",
- patch.getPatchSetId(), change.getChangeId(), kind);
+ "Change kind for patchSet %s of change %s: %s", patch.number(), change.getChangeId(), kind);
return kind;
}
}
diff --git a/java/com/google/gerrit/server/change/ChangeMessageResource.java b/java/com/google/gerrit/server/change/ChangeMessageResource.java
index 3c9ef34a28..25f952d0b1 100644
--- a/java/com/google/gerrit/server/change/ChangeMessageResource.java
+++ b/java/com/google/gerrit/server/change/ChangeMessageResource.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.inject.TypeLiteral;
/** A change message resource. */
diff --git a/java/com/google/gerrit/server/change/ChangeResource.java b/java/com/google/gerrit/server/change/ChangeResource.java
index 98b728f167..8b8ce54745 100644
--- a/java/com/google/gerrit/server/change/ChangeResource.java
+++ b/java/com/google/gerrit/server/change/ChangeResource.java
@@ -21,23 +21,27 @@ import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestResource.HasETag;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
@@ -75,6 +79,7 @@ public class ChangeResource implements RestResource, HasETag {
private final PermissionBackend permissionBackend;
private final StarredChangesUtil starredChangesUtil;
private final ProjectCache projectCache;
+ private final PluginSetContext<ChangeETagComputation> changeETagComputation;
private final ChangeNotes notes;
private final CurrentUser user;
@@ -86,6 +91,7 @@ public class ChangeResource implements RestResource, HasETag {
PermissionBackend permissionBackend,
StarredChangesUtil starredChangesUtil,
ProjectCache projectCache,
+ PluginSetContext<ChangeETagComputation> changeETagComputation,
@Assisted ChangeNotes notes,
@Assisted CurrentUser user) {
this.accountCache = accountCache;
@@ -94,6 +100,7 @@ public class ChangeResource implements RestResource, HasETag {
this.permissionBackend = permissionBackend;
this.starredChangesUtil = starredChangesUtil;
this.projectCache = projectCache;
+ this.changeETagComputation = changeETagComputation;
this.notes = notes;
this.user = user;
}
@@ -149,7 +156,7 @@ public class ChangeResource implements RestResource, HasETag {
accounts.add(getChange().getAssignee());
}
try {
- patchSetUtil.byChange(notes).stream().map(PatchSet::getUploader).forEach(accounts::add);
+ patchSetUtil.byChange(notes).stream().map(PatchSet::uploader).forEach(accounts::add);
// It's intentional to include the states for *all* reviewers into the ETag computation.
// We need the states of all current reviewers and CCs because they are part of ChangeInfo.
@@ -193,16 +200,32 @@ public class ChangeResource implements RestResource, HasETag {
for (ProjectState p : projectStateTree) {
hashObjectId(h, p.getConfig().getRevision(), buf);
}
+
+ changeETagComputation.runEach(
+ c -> {
+ String pluginETag = c.getETag(notes.getProjectName(), notes.getChangeId());
+ if (pluginETag != null) {
+ h.putString(pluginETag, UTF_8);
+ }
+ });
}
@Override
public String getETag() {
- Hasher h = Hashing.murmur3_128().newHasher();
- if (user.isIdentifiedUser()) {
- h.putString(starredChangesUtil.getObjectId(user.getAccountId(), getId()).name(), UTF_8);
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Compute change ETag",
+ Metadata.builder()
+ .changeId(notes.getChangeId().get())
+ .projectName(notes.getProjectName().get())
+ .build())) {
+ Hasher h = Hashing.murmur3_128().newHasher();
+ if (user.isIdentifiedUser()) {
+ h.putString(starredChangesUtil.getObjectId(user.getAccountId(), getId()).name(), UTF_8);
+ }
+ prepareETag(h, user);
+ return h.hash().toString();
}
- prepareETag(h, user);
- return h.hash().toString();
}
private void hashObjectId(Hasher h, ObjectId id, byte[] buf) {
@@ -211,9 +234,8 @@ public class ChangeResource implements RestResource, HasETag {
}
private void hashAccount(Hasher h, AccountState accountState, byte[] buf) {
- h.putInt(accountState.getAccount().getId().get());
- h.putString(
- MoreObjects.firstNonNull(accountState.getAccount().getMetaId(), ZERO_ID_STRING), UTF_8);
- accountState.getExternalIds().stream().forEach(e -> hashObjectId(h, e.blobId(), buf));
+ h.putInt(accountState.account().id().get());
+ h.putString(MoreObjects.firstNonNull(accountState.account().metaId(), ZERO_ID_STRING), UTF_8);
+ accountState.externalIds().stream().forEach(e -> hashObjectId(h, e.blobId(), buf));
}
}
diff --git a/java/com/google/gerrit/server/change/ChangeTriplet.java b/java/com/google/gerrit/server/change/ChangeTriplet.java
index e4e6870250..10743022e5 100644
--- a/java/com/google/gerrit/server/change/ChangeTriplet.java
+++ b/java/com/google/gerrit/server/change/ChangeTriplet.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.change;
import com.google.auto.value.AutoValue;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import java.util.Optional;
@AutoValue
@@ -27,8 +27,8 @@ public abstract class ChangeTriplet {
return format(change.getDest(), change.getKey());
}
- private static String format(Branch.NameKey branch, Change.Key change) {
- return branch.getParentKey().get() + "~" + branch.getShortName() + "~" + change.get();
+ private static String format(BranchNameKey branch, Change.Key change) {
+ return branch.project().get() + "~" + branch.shortName() + "~" + change.get();
}
/**
@@ -53,14 +53,14 @@ public abstract class ChangeTriplet {
String changeId = Url.decode(triplet.substring(z + 1));
return Optional.of(
new AutoValue_ChangeTriplet(
- new Branch.NameKey(new Project.NameKey(project), branch), new Change.Key(changeId)));
+ BranchNameKey.create(Project.nameKey(project), branch), Change.key(changeId)));
}
public final Project.NameKey project() {
- return branch().getParentKey();
+ return branch().project();
}
- public abstract Branch.NameKey branch();
+ public abstract BranchNameKey branch();
public abstract Change.Key id();
diff --git a/java/com/google/gerrit/server/change/CommentResource.java b/java/com/google/gerrit/server/change/CommentResource.java
index 1b7cbf801a..dbe7a7642c 100644
--- a/java/com/google/gerrit/server/change/CommentResource.java
+++ b/java/com/google/gerrit/server/change/CommentResource.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.inject.TypeLiteral;
public class CommentResource implements RestResource {
diff --git a/java/com/google/gerrit/server/change/ConsistencyChecker.java b/java/com/google/gerrit/server/change/ConsistencyChecker.java
index 80b7190df7..0374a1c0db 100644
--- a/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.change;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
+import static com.google.gerrit.entities.RefNames.REFS_CHANGES;
import static com.google.gerrit.server.ChangeUtil.PS_ID_ORDER;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
@@ -30,14 +30,14 @@ import com.google.common.collect.SetMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.FixInput;
import com.google.gerrit.extensions.common.ProblemInfo;
import com.google.gerrit.extensions.common.ProblemInfo.Status;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -238,7 +238,7 @@ public class ConsistencyChecker {
}
private boolean openRepo() {
- Project.NameKey project = change().getDest().getParentKey();
+ Project.NameKey project = change().getDest().project();
try {
repo = repoManager.openRepository(project);
oi = repo.newObjectInserter();
@@ -265,7 +265,7 @@ public class ConsistencyChecker {
try {
refs =
repo.getRefDatabase()
- .exactRef(all.stream().map(ps -> ps.getId().toRefName()).toArray(String[]::new));
+ .exactRef(all.stream().map(ps -> ps.id().toRefName()).toArray(String[]::new));
} catch (IOException e) {
error("error reading refs", e);
refs = Collections.emptyMap();
@@ -274,12 +274,9 @@ public class ConsistencyChecker {
List<DeletePatchSetFromDbOp> deletePatchSetOps = new ArrayList<>();
for (PatchSet ps : all) {
// Check revision format.
- int psNum = ps.getId().get();
- String refName = ps.getId().toRefName();
- ObjectId objId = parseObjectId(ps.getRevision().get(), "patch set " + psNum);
- if (objId == null) {
- continue;
- }
+ int psNum = ps.id().get();
+ String refName = ps.id().toRefName();
+ ObjectId objId = ps.commitId();
patchSetsBySha.put(objId, ps);
// Check ref existence.
@@ -299,13 +296,13 @@ public class ConsistencyChecker {
RevCommit psCommit = parseCommit(objId, String.format("patch set %d", psNum));
if (psCommit == null) {
if (fix != null && fix.deletePatchSetIfCommitMissing) {
- deletePatchSetOps.add(new DeletePatchSetFromDbOp(lastProblem(), ps.getId()));
+ deletePatchSetOps.add(new DeletePatchSetFromDbOp(lastProblem(), ps.id()));
}
continue;
} else if (refProblem != null && fix != null) {
fixPatchSetRef(refProblem, ps);
}
- if (ps.getId().equals(change().currentPatchSetId())) {
+ if (ps.id().equals(change().currentPatchSetId())) {
currPsCommit = psCommit;
}
}
@@ -319,7 +316,7 @@ public class ConsistencyChecker {
problem(
String.format(
"Multiple patch sets pointing to %s: %s",
- e.getKey().name(), Collections2.transform(e.getValue(), PatchSet::getPatchSetId)));
+ e.getKey().name(), Collections2.transform(e.getValue(), PatchSet::number)));
}
}
@@ -327,7 +324,7 @@ public class ConsistencyChecker {
}
private void checkMerged() {
- String refName = change().getDest().get();
+ String refName = change().getDest().branch();
Ref dest;
try {
dest = repo.getRefDatabase().exactRef(refName);
@@ -351,15 +348,15 @@ public class ConsistencyChecker {
try {
merged = rw.isMergedInto(currPsCommit, tip);
} catch (IOException e) {
- problem("Error checking whether patch set " + currPs.getId().get() + " is merged");
+ problem("Error checking whether patch set " + currPs.id().get() + " is merged");
return;
}
- checkMergedBitMatchesStatus(currPs.getId(), currPsCommit, merged);
+ checkMergedBitMatchesStatus(currPs.id(), currPsCommit, merged);
}
}
private ProblemInfo wrongChangeStatus(PatchSet.Id psId, RevCommit commit) {
- String refName = change().getDest().get();
+ String refName = change().getDest().branch();
return problem(
formatProblemMessage(
"Patch set %d (%s) is merged into destination ref %s (%s), but change"
@@ -368,7 +365,7 @@ public class ConsistencyChecker {
}
private void checkMergedBitMatchesStatus(PatchSet.Id psId, RevCommit commit, boolean merged) {
- String refName = change().getDest().get();
+ String refName = change().getDest().branch();
if (merged && !change().isMerged()) {
ProblemInfo p = wrongChangeStatus(psId, commit);
if (fix != null) {
@@ -379,7 +376,7 @@ public class ConsistencyChecker {
formatProblemMessage(
"Patch set %d (%s) is not merged into"
+ " destination ref %s (%s), but change status is %s",
- currPs.getId().get(), commit.name(), refName, tip.name()));
+ currPs.id().get(), commit.name(), refName, tip.name()));
}
}
@@ -395,7 +392,11 @@ public class ConsistencyChecker {
}
private void checkExpectMergedAs() {
- ObjectId objId = parseObjectId(fix.expectMergedAs, "expected merged commit");
+ if (!ObjectId.isId(fix.expectMergedAs)) {
+ problem("Invalid revision on expected merged commit: " + fix.expectMergedAs);
+ return;
+ }
+ ObjectId objId = ObjectId.fromString(fix.expectMergedAs);
RevCommit commit = parseCommit(objId, "expected merged commit");
if (commit == null) {
return;
@@ -406,7 +407,7 @@ public class ConsistencyChecker {
problem(
String.format(
"Expected merged commit %s is not merged into destination ref %s (%s)",
- commit.name(), change().getDest().get(), tip.name()));
+ commit.name(), change().getDest().branch(), tip.name()));
return;
}
@@ -420,8 +421,7 @@ public class ConsistencyChecker {
continue;
}
try {
- Change c =
- notesFactory.createChecked(change().getProject(), psId.getParentKey()).getChange();
+ Change c = notesFactory.createChecked(change().getProject(), psId.changeId()).getChange();
if (!c.getDest().equals(change().getDest())) {
continue;
}
@@ -601,9 +601,9 @@ public class ConsistencyChecker {
private void fixPatchSetRef(ProblemInfo p, PatchSet ps) {
try {
- RefUpdate ru = repo.updateRef(ps.getId().toRefName());
+ RefUpdate ru = repo.updateRef(ps.id().toRefName());
ru.setForceUpdate(true);
- ru.setNewObjectId(ObjectId.fromString(ps.getRevision().get()));
+ ru.setNewObjectId(ps.commitId());
ru.setRefLogIdent(newRefLogIdent());
ru.setRefLogMessage("Repair patch set ref", true);
RefUpdate.Result result = ru.update();
@@ -630,7 +630,7 @@ public class ConsistencyChecker {
}
} catch (IOException e) {
String msg = "Error fixing patch set ref";
- logger.atWarning().withCause(e).log("%s %s", msg, ps.getId().toRefName());
+ logger.atWarning().withCause(e).log("%s %s", msg, ps.id().toRefName());
p.status = Status.FIX_FAILED;
p.outcome = msg;
}
@@ -640,7 +640,7 @@ public class ConsistencyChecker {
try (BatchUpdate bu = newBatchUpdate()) {
bu.setRepository(repo, rw, oi);
for (DeletePatchSetFromDbOp op : ops) {
- checkArgument(op.psId.getParentKey().equals(notes.getChangeId()));
+ checkArgument(op.psId.changeId().equals(notes.getChangeId()));
bu.addOp(notes.getChangeId(), op);
}
bu.addOp(notes.getChangeId(), new UpdateCurrentPatchSetOp(ops));
@@ -652,7 +652,7 @@ public class ConsistencyChecker {
}
} catch (UpdateException | RestApiException e) {
String msg = "Error deleting patch set";
- logger.atWarning().withCause(e).log("%s of change %s", msg, ops.get(0).psId.getParentKey());
+ logger.atWarning().withCause(e).log("%s of change %s", msg, ops.get(0).psId.changeId());
for (DeletePatchSetFromDbOp op : ops) {
// Overwrite existing statuses that were set before the transaction was
// rolled back.
@@ -714,8 +714,8 @@ public class ConsistencyChecker {
// and whether they are seen by this op; we are already given the full set
// of patch sets that will eventually be deleted in this update.
for (PatchSet ps : psUtil.byChange(ctx.getNotes())) {
- if (!toDelete.contains(ps.getId())) {
- all.add(ps.getId());
+ if (!toDelete.contains(ps.id())) {
+ all.add(ps.id());
}
}
if (all.isEmpty()) {
@@ -734,15 +734,6 @@ public class ConsistencyChecker {
return serverIdent.get();
}
- private ObjectId parseObjectId(String objIdStr, String desc) {
- try {
- return ObjectId.fromString(objIdStr);
- } catch (IllegalArgumentException e) {
- problem(String.format("Invalid revision on %s: %s", desc, objIdStr));
- return null;
- }
- }
-
private RevCommit parseCommit(ObjectId objId, String desc) {
try {
return rw.parseCommit(objId);
diff --git a/java/com/google/gerrit/server/change/DeleteChangeOp.java b/java/com/google/gerrit/server/change/DeleteChangeOp.java
index ec20e87093..14298d5149 100644
--- a/java/com/google/gerrit/server/change/DeleteChangeOp.java
+++ b/java/com/google/gerrit/server/change/DeleteChangeOp.java
@@ -14,16 +14,19 @@
package com.google.gerrit.server.change;
+import static com.google.common.flogger.LazyArgs.lazy;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.extensions.events.ChangeDeleted;
import com.google.gerrit.server.plugincontext.PluginItemContext;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.RepoContext;
@@ -37,6 +40,8 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
public class DeleteChangeOp implements BatchUpdateOp {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
public interface Factory {
DeleteChangeOp create(Change.Id id);
}
@@ -71,8 +76,19 @@ public class DeleteChangeOp implements BatchUpdateOp {
ensureDeletable(ctx, id, patchSets);
// Cleaning up is only possible as long as the change and its elements are
// still part of the database.
- cleanUpReferences(ctx, id);
+ cleanUpReferences(id);
+ logger.atFine().log(
+ "Deleting change %s, current patch set %d is commit %s",
+ id,
+ ctx.getChange().currentPatchSetId().get(),
+ lazy(
+ () ->
+ patchSets.stream()
+ .filter(p -> p.number() == ctx.getChange().currentPatchSetId().get())
+ .findAny()
+ .map(p -> p.commitId().name())
+ .orElse("n/a")));
ctx.deleteChange();
changeDeleted.fire(ctx.getChange(), ctx.getAccount(), ctx.getWhen());
return true;
@@ -87,37 +103,50 @@ public class DeleteChangeOp implements BatchUpdateOp {
if (isPatchSetMerged(ctx, patchSet)) {
throw new ResourceConflictException(
String.format(
- "Cannot delete change %s: patch set %s is already merged",
- id, patchSet.getPatchSetId()));
+ "Cannot delete change %s: patch set %s is already merged", id, patchSet.number()));
}
}
}
private boolean isPatchSetMerged(ChangeContext ctx, PatchSet patchSet) throws IOException {
- Optional<ObjectId> destId = ctx.getRepoView().getRef(ctx.getChange().getDest().get());
+ Optional<ObjectId> destId = ctx.getRepoView().getRef(ctx.getChange().getDest().branch());
if (!destId.isPresent()) {
return false;
}
RevWalk revWalk = ctx.getRevWalk();
- ObjectId objectId = ObjectId.fromString(patchSet.getRevision().get());
- return revWalk.isMergedInto(revWalk.parseCommit(objectId), revWalk.parseCommit(destId.get()));
+ return revWalk.isMergedInto(
+ revWalk.parseCommit(patchSet.commitId()), revWalk.parseCommit(destId.get()));
}
- private void cleanUpReferences(ChangeContext ctx, Change.Id id) throws NoSuchChangeException {
+ private void cleanUpReferences(Change.Id id) throws IOException {
accountPatchReviewStore.run(s -> s.clearReviewed(id));
- // Non-atomic operation on Accounts table; not much we can do to make it
- // atomic.
- starredChangesUtil.unstarAll(ctx.getChange().getProject(), id);
+ // Non-atomic operation on All-Users refs; not much we can do to make it atomic.
+ starredChangesUtil.unstarAllForChangeDeletion(id);
}
@Override
public void updateRepo(RepoContext ctx) throws IOException {
- String prefix = new PatchSet.Id(id, 1).toRefName();
- prefix = prefix.substring(0, prefix.length() - 1);
+ String changeRefPrefix = RefNames.changeRefPrefix(id);
+ for (Map.Entry<String, ObjectId> e : ctx.getRepoView().getRefs(changeRefPrefix).entrySet()) {
+ removeRef(ctx, e, changeRefPrefix);
+ }
+ removeUserEdits(ctx);
+ }
+
+ private void removeUserEdits(RepoContext ctx) throws IOException {
+ String prefix = RefNames.REFS_USERS;
+ String editRef = String.format("/edit-%s/", id);
for (Map.Entry<String, ObjectId> e : ctx.getRepoView().getRefs(prefix).entrySet()) {
- ctx.addRefUpdate(e.getValue(), ObjectId.zeroId(), prefix + e.getKey());
+ if (e.getKey().contains(editRef)) {
+ removeRef(ctx, e, prefix);
+ }
}
}
+
+ private void removeRef(RepoContext ctx, Map.Entry<String, ObjectId> entry, String prefix)
+ throws IOException {
+ ctx.addRefUpdate(entry.getValue(), ObjectId.zeroId(), prefix + entry.getKey());
+ }
}
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java b/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
index 4b5572b6bb..3bc9324c36 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
+++ b/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.change;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.mail.send.DeleteReviewerSender;
import com.google.gerrit.server.update.BatchUpdateOp;
@@ -55,7 +55,7 @@ public class DeleteReviewerByEmailOp implements BatchUpdateOp {
String msg = "Removed reviewer " + reviewer;
changeMessage =
new ChangeMessage(
- new ChangeMessage.Key(change.getId(), ChangeUtil.messageUuid()),
+ ChangeMessage.key(change.getId(), ChangeUtil.messageUuid()),
ctx.getAccountId(),
ctx.getWhen(),
psId);
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerOp.java b/java/com/google/gerrit/server/change/DeleteReviewerOp.java
index 29458a864b..c4de02c812 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerOp.java
+++ b/java/com/google/gerrit/server/change/DeleteReviewerOp.java
@@ -18,17 +18,17 @@ import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
@@ -108,7 +108,7 @@ public class DeleteReviewerOp implements BatchUpdateOp {
@Override
public boolean updateChange(ChangeContext ctx)
throws AuthException, ResourceNotFoundException, PermissionBackendException, IOException {
- Account.Id reviewerId = reviewer.getAccount().getId();
+ Account.Id reviewerId = reviewer.account().id();
// Check of removing this reviewer (even if there is no vote processed by the loop below) is OK
removeReviewerControl.checkRemoveReviewer(ctx.getNotes(), ctx.getUser(), reviewerId);
@@ -125,7 +125,7 @@ public class DeleteReviewerOp implements BatchUpdateOp {
}
StringBuilder msg = new StringBuilder();
- msg.append("Removed reviewer " + reviewer.getAccount().getFullName());
+ msg.append("Removed reviewer " + reviewer.account().fullName());
StringBuilder removedVotesMsg = new StringBuilder();
removedVotesMsg.append(" with the following votes:\n\n");
List<PatchSetApproval> del = new ArrayList<>();
@@ -134,14 +134,14 @@ public class DeleteReviewerOp implements BatchUpdateOp {
// Check if removing this vote is OK
removeReviewerControl.checkRemoveReviewer(ctx.getNotes(), ctx.getUser(), a);
del.add(a);
- if (a.getPatchSetId().equals(currPs.getId()) && a.getValue() != 0) {
- oldApprovals.put(a.getLabel(), a.getValue());
+ if (a.patchSetId().equals(currPs.id()) && a.value() != 0) {
+ oldApprovals.put(a.label(), a.value());
removedVotesMsg
.append("* ")
- .append(a.getLabel())
- .append(formatLabelValue(a.getValue()))
+ .append(a.label())
+ .append(formatLabelValue(a.value()))
.append(" by ")
- .append(userFactory.create(a.getAccountId()).getNameEmail())
+ .append(userFactory.create(a.accountId()).getNameEmail())
.append("\n");
votesRemoved = true;
}
@@ -152,7 +152,7 @@ public class DeleteReviewerOp implements BatchUpdateOp {
} else {
msg.append(".");
}
- ChangeUpdate update = ctx.getUpdate(currPs.getId());
+ ChangeUpdate update = ctx.getUpdate(currPs.id());
update.removeReviewer(reviewerId);
changeMessage =
@@ -195,7 +195,7 @@ public class DeleteReviewerOp implements BatchUpdateOp {
private Iterable<PatchSetApproval> approvals(ChangeContext ctx, Account.Id accountId) {
Iterable<PatchSetApproval> approvals;
approvals = approvalsUtil.byChange(ctx.getNotes()).values();
- return Iterables.filter(approvals, psa -> accountId.equals(psa.getAccountId()));
+ return Iterables.filter(approvals, psa -> accountId.equals(psa.accountId()));
}
private String formatLabelValue(short value) {
@@ -206,16 +206,19 @@ public class DeleteReviewerOp implements BatchUpdateOp {
}
private void emailReviewers(
- NameKey projectName, Change change, ChangeMessage changeMessage, NotifyResolver.Result notify)
+ Project.NameKey projectName,
+ Change change,
+ ChangeMessage changeMessage,
+ NotifyResolver.Result notify)
throws EmailException {
Account.Id userId = user.get().getAccountId();
- if (userId.equals(reviewer.getAccount().getId())) {
+ if (userId.equals(reviewer.account().id())) {
// The user knows they removed themselves, don't bother emailing them.
return;
}
DeleteReviewerSender cm = deleteReviewerSenderFactory.create(projectName, change.getId());
cm.setFrom(userId);
- cm.addReviewers(Collections.singleton(reviewer.getAccount().getId()));
+ cm.addReviewers(Collections.singleton(reviewer.account().id()));
cm.setChangeMessage(changeMessage.getMessage(), changeMessage.getWrittenOn());
cm.setNotify(notify);
cm.send();
diff --git a/java/com/google/gerrit/server/change/DraftCommentResource.java b/java/com/google/gerrit/server/change/DraftCommentResource.java
index ef317251f1..3d3e8f9da8 100644
--- a/java/com/google/gerrit/server/change/DraftCommentResource.java
+++ b/java/com/google/gerrit/server/change/DraftCommentResource.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.TypeLiteral;
diff --git a/java/com/google/gerrit/server/change/EmailReviewComments.java b/java/com/google/gerrit/server/change/EmailReviewComments.java
index 8353501f93..f7e45e70d7 100644
--- a/java/com/google/gerrit/server/change/EmailReviewComments.java
+++ b/java/com/google/gerrit/server/change/EmailReviewComments.java
@@ -18,9 +18,9 @@ import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.SendEmailExecutor;
@@ -129,7 +129,7 @@ public class EmailReviewComments implements Runnable, RequestContext {
cm.setNotify(notify);
cm.send();
} catch (Exception e) {
- logger.atSevere().withCause(e).log("Cannot email comments for %s", patchSet.getId());
+ logger.atSevere().withCause(e).log("Cannot email comments for %s", patchSet.id());
} finally {
requestContext.setContext(old);
}
diff --git a/java/com/google/gerrit/server/change/FileContentUtil.java b/java/com/google/gerrit/server/change/FileContentUtil.java
index a806f94653..5c7946cc69 100644
--- a/java/com/google/gerrit/server/change/FileContentUtil.java
+++ b/java/com/google/gerrit/server/change/FileContentUtil.java
@@ -21,10 +21,10 @@ import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.PatchScript.FileMode;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.mime.FileTypeRegistry;
import com.google.gerrit.server.project.ProjectState;
diff --git a/java/com/google/gerrit/server/change/FileInfoJson.java b/java/com/google/gerrit/server/change/FileInfoJson.java
index 56cc8df4d6..a82397545a 100644
--- a/java/com/google/gerrit/server/change/FileInfoJson.java
+++ b/java/com/google/gerrit/server/change/FileInfoJson.java
@@ -15,13 +15,12 @@
package com.google.gerrit.server.change;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.common.FileInfo;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListEntry;
@@ -44,27 +43,20 @@ public class FileInfoJson {
public Map<String, FileInfo> toFileInfoMap(Change change, PatchSet patchSet)
throws PatchListNotAvailableException {
- return toFileInfoMap(change, patchSet.getRevision(), null);
- }
-
- public Map<String, FileInfo> toFileInfoMap(Change change, RevId revision, @Nullable PatchSet base)
- throws PatchListNotAvailableException {
- ObjectId objectId = ObjectId.fromString(revision.get());
- return toFileInfoMap(change, objectId, base);
+ return toFileInfoMap(change, patchSet.commitId(), null);
}
public Map<String, FileInfo> toFileInfoMap(
Change change, ObjectId objectId, @Nullable PatchSet base)
throws PatchListNotAvailableException {
- ObjectId a = (base == null) ? null : ObjectId.fromString(base.getRevision().get());
+ ObjectId a = base != null ? base.commitId() : null;
return toFileInfoMap(change, PatchListKey.againstCommit(a, objectId, Whitespace.IGNORE_NONE));
}
- public Map<String, FileInfo> toFileInfoMap(Change change, RevId revision, int parent)
+ public Map<String, FileInfo> toFileInfoMap(Change change, ObjectId objectId, int parent)
throws PatchListNotAvailableException {
- ObjectId b = ObjectId.fromString(revision.get());
return toFileInfoMap(
- change, PatchListKey.againstParentNum(parent + 1, b, Whitespace.IGNORE_NONE));
+ change, PatchListKey.againstParentNum(parent + 1, objectId, Whitespace.IGNORE_NONE));
}
private Map<String, FileInfo> toFileInfoMap(Change change, PatchListKey key)
diff --git a/java/com/google/gerrit/server/change/FileResource.java b/java/com/google/gerrit/server/change/FileResource.java
index bd7557f2c2..5402338574 100644
--- a/java/com/google/gerrit/server/change/FileResource.java
+++ b/java/com/google/gerrit/server/change/FileResource.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Patch;
import com.google.inject.TypeLiteral;
public class FileResource implements RestResource {
@@ -29,7 +29,7 @@ public class FileResource implements RestResource {
public FileResource(RevisionResource rev, String name) {
this.rev = rev;
- this.key = new Patch.Key(rev.getPatchSet().getId(), name);
+ this.key = Patch.key(rev.getPatchSet().id(), name);
}
public Patch.Key getPatchKey() {
diff --git a/java/com/google/gerrit/server/change/FixResource.java b/java/com/google/gerrit/server/change/FixResource.java
index 08e278542f..b6b5894f10 100644
--- a/java/com/google/gerrit/server/change/FixResource.java
+++ b/java/com/google/gerrit/server/change/FixResource.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.FixReplacement;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.FixReplacement;
import com.google.inject.TypeLiteral;
import java.util.List;
diff --git a/java/com/google/gerrit/server/change/IncludedIn.java b/java/com/google/gerrit/server/change/IncludedIn.java
index 3ac6959484..3c66c2c8bb 100644
--- a/java/com/google/gerrit/server/change/IncludedIn.java
+++ b/java/com/google/gerrit/server/change/IncludedIn.java
@@ -16,12 +16,12 @@ package com.google.gerrit.server.change;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.config.ExternalIncludedIn;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/change/LabelNormalizer.java b/java/com/google/gerrit/server/change/LabelNormalizer.java
index 1ec17176f3..1ef3aee0e6 100644
--- a/java/com/google/gerrit/server/change/LabelNormalizer.java
+++ b/java/com/google/gerrit/server/change/LabelNormalizer.java
@@ -24,8 +24,8 @@ import com.google.common.collect.Lists;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.LabelValue;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
@@ -88,24 +88,23 @@ public class LabelNormalizer {
List<PatchSetApproval> deleted = Lists.newArrayListWithCapacity(approvals.size());
LabelTypes labelTypes = projectCache.checkedGet(notes.getProjectName()).getLabelTypes(notes);
for (PatchSetApproval psa : approvals) {
- Change.Id changeId = psa.getKey().getParentKey().getParentKey();
+ Change.Id changeId = psa.key().patchSetId().changeId();
checkArgument(
changeId.equals(notes.getChangeId()),
"Approval %s does not match change %s",
- psa.getKey(),
+ psa.key(),
notes.getChange().getKey());
if (psa.isLegacySubmit()) {
unchanged.add(psa);
continue;
}
- LabelType label = labelTypes.byLabel(psa.getLabelId());
+ LabelType label = labelTypes.byLabel(psa.labelId());
if (label == null) {
deleted.add(psa);
continue;
}
- PatchSetApproval copy = copy(psa);
- applyTypeFloor(label, copy);
- if (copy.getValue() != psa.getValue()) {
+ PatchSetApproval copy = applyTypeFloor(label, psa);
+ if (copy.value() != psa.value()) {
updated.add(copy);
} else {
unchanged.add(psa);
@@ -114,18 +113,16 @@ public class LabelNormalizer {
return Result.create(unchanged, updated, deleted);
}
- private PatchSetApproval copy(PatchSetApproval src) {
- return new PatchSetApproval(src.getPatchSetId(), src);
- }
-
- private void applyTypeFloor(LabelType lt, PatchSetApproval a) {
+ private PatchSetApproval applyTypeFloor(LabelType lt, PatchSetApproval a) {
+ PatchSetApproval.Builder b = a.toBuilder();
LabelValue atMin = lt.getMin();
- if (atMin != null && a.getValue() < atMin.getValue()) {
- a.setValue(atMin.getValue());
+ if (atMin != null && a.value() < atMin.getValue()) {
+ b.value(atMin.getValue());
}
LabelValue atMax = lt.getMax();
- if (atMax != null && a.getValue() > atMax.getValue()) {
- a.setValue(atMax.getValue());
+ if (atMax != null && a.value() > atMax.getValue()) {
+ b.value(atMax.getValue());
}
+ return b.build();
}
}
diff --git a/java/com/google/gerrit/server/change/LabelsJson.java b/java/com/google/gerrit/server/change/LabelsJson.java
index 6fde5a5892..c6f49696ca 100644
--- a/java/com/google/gerrit/server/change/LabelsJson.java
+++ b/java/com/google/gerrit/server/change/LabelsJson.java
@@ -36,12 +36,12 @@ import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.common.VotingRangeInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.account.AccountLoader;
@@ -206,8 +206,8 @@ public class LabelsJson {
if (standard) {
for (PatchSetApproval psa : cd.currentApprovals()) {
if (type.matches(psa)) {
- short val = psa.getValue();
- Account.Id accountId = psa.getAccountId();
+ short val = psa.value();
+ Account.Id accountId = psa.accountId();
setLabelScores(accountLoader, type, e.getValue(), val, accountId);
}
}
@@ -260,7 +260,7 @@ public class LabelsJson {
accountId,
null,
null)) {
- result.put(psa.getLabel(), psa.getValue());
+ result.put(psa.label(), psa.value());
}
return result;
}
@@ -279,7 +279,7 @@ public class LabelsJson {
// we aren't including 0 votes for all users below, so we can just look at
// the latest patch set (in the next loop).
for (PatchSetApproval psa : cd.approvals().values()) {
- allUsers.add(psa.getAccountId());
+ allUsers.add(psa.accountId());
}
}
@@ -287,13 +287,13 @@ public class LabelsJson {
SetMultimap<Account.Id, PatchSetApproval> current =
MultimapBuilder.hashKeys().hashSetValues().build();
for (PatchSetApproval a : cd.currentApprovals()) {
- allUsers.add(a.getAccountId());
- LabelType type = labelTypes.byLabel(a.getLabelId());
+ allUsers.add(a.accountId());
+ LabelType type = labelTypes.byLabel(a.labelId());
if (type != null) {
labelNames.add(type.getName());
// Not worth the effort to distinguish between votable/non-votable for 0
// values on closed changes, since they can't vote anyway.
- current.put(a.getAccountId(), a);
+ current.put(a.accountId(), a);
}
}
@@ -335,19 +335,19 @@ public class LabelsJson {
}
}
for (PatchSetApproval psa : current.get(accountId)) {
- LabelType type = labelTypes.byLabel(psa.getLabelId());
+ LabelType type = labelTypes.byLabel(psa.labelId());
if (type == null) {
continue;
}
- short val = psa.getValue();
+ short val = psa.value();
ApprovalInfo info = byLabel.get(type.getName());
if (info != null) {
info.value = Integer.valueOf(val);
info.permittedVotingRange = pvr.getOrDefault(type.getName(), null);
- info.date = psa.getGranted();
- info.tag = psa.getTag();
- if (psa.isPostSubmit()) {
+ info.date = psa.granted();
+ info.tag = psa.tag().orElse(null);
+ if (psa.postSubmit()) {
info.postSubmit = true;
}
}
@@ -441,13 +441,13 @@ public class LabelsJson {
Set<Account.Id> allUsers = new HashSet<>();
allUsers.addAll(cd.reviewers().byState(ReviewerStateInternal.REVIEWER));
for (PatchSetApproval psa : cd.approvals().values()) {
- allUsers.add(psa.getAccountId());
+ allUsers.add(psa.accountId());
}
Table<Account.Id, String, PatchSetApproval> current =
HashBasedTable.create(allUsers.size(), cd.getLabelTypes().getLabelTypes().size());
for (PatchSetApproval psa : cd.currentApprovals()) {
- current.put(psa.getAccountId(), psa.getLabel(), psa);
+ current.put(psa.accountId(), psa.label(), psa);
}
LabelTypes labelTypes = cd.getLabelTypes();
@@ -467,16 +467,16 @@ public class LabelsJson {
Timestamp date = null;
PatchSetApproval psa = current.get(accountId, lt.getName());
if (psa != null) {
- value = Integer.valueOf(psa.getValue());
+ value = Integer.valueOf(psa.value());
if (value == 0) {
// This may be a dummy approval that was inserted when the reviewer
// was added. Explicitly check whether the user can vote on this
// label.
value = perm.test(new LabelPermission(lt)) ? 0 : null;
}
- tag = psa.getTag();
- date = psa.getGranted();
- if (psa.isPostSubmit()) {
+ tag = psa.tag().orElse(null);
+ date = psa.granted();
+ if (psa.postSubmit()) {
logger.atWarning().log("unexpected post-submit approval on open change: %s", psa);
}
} else {
diff --git a/java/com/google/gerrit/server/change/MergeabilityCache.java b/java/com/google/gerrit/server/change/MergeabilityCache.java
index 3a7f3ab2c2..b432bc9bf7 100644
--- a/java/com/google/gerrit/server/change/MergeabilityCache.java
+++ b/java/com/google/gerrit/server/change/MergeabilityCache.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Branch;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -29,7 +29,7 @@ public interface MergeabilityCache {
Ref intoRef,
SubmitType submitType,
String mergeStrategy,
- Branch.NameKey dest,
+ BranchNameKey dest,
Repository repo) {
throw new UnsupportedOperationException("Mergeability checking disabled");
}
@@ -46,7 +46,7 @@ public interface MergeabilityCache {
Ref intoRef,
SubmitType submitType,
String mergeStrategy,
- Branch.NameKey dest,
+ BranchNameKey dest,
Repository repo);
Boolean getIfPresent(ObjectId commit, Ref intoRef, SubmitType submitType, String mergeStrategy);
diff --git a/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java b/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
index d4085191dc..44af1e41e6 100644
--- a/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
+++ b/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
@@ -24,9 +24,9 @@ import com.google.common.cache.Cache;
import com.google.common.cache.Weigher;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.proto.Cache.MergeabilityKeyProto;
import com.google.gerrit.server.cache.serialize.BooleanCacheSerializer;
@@ -191,7 +191,7 @@ public class MergeabilityCacheImpl implements MergeabilityCache {
Ref intoRef,
SubmitType submitType,
String mergeStrategy,
- Branch.NameKey dest,
+ BranchNameKey dest,
Repository repo) {
ObjectId into = intoRef != null ? intoRef.getObjectId() : ObjectId.zeroId();
EntryKey key = new EntryKey(commit, into, submitType, mergeStrategy);
diff --git a/java/com/google/gerrit/server/change/NotifyResolver.java b/java/com/google/gerrit/server/change/NotifyResolver.java
index 5b156846a5..27951cad8e 100644
--- a/java/com/google/gerrit/server/change/NotifyResolver.java
+++ b/java/com/google/gerrit/server/change/NotifyResolver.java
@@ -21,12 +21,12 @@ import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.NotifyInfo;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountResolver;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -99,7 +99,7 @@ public class NotifyResolver {
List<String> problems = new ArrayList<>(inputs.size());
for (String nameOrEmail : inputs) {
try {
- r.add(accountResolver.resolve(nameOrEmail).asUnique().getAccount().getId());
+ r.add(accountResolver.resolve(nameOrEmail).asUnique().account().id());
} catch (UnprocessableEntityException e) {
problems.add(e.getMessage());
}
diff --git a/java/com/google/gerrit/server/change/PatchSetInserter.java b/java/com/google/gerrit/server/change/PatchSetInserter.java
index d3649f660d..71c54b1e8d 100644
--- a/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -20,13 +20,13 @@ import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import static java.util.Objects.requireNonNull;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetInfo;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
@@ -208,7 +208,7 @@ public class PatchSetInserter implements BatchUpdateOp {
if (newGroups.isEmpty()) {
PatchSet prevPs = psUtil.current(ctx.getNotes());
if (prevPs != null) {
- newGroups = prevPs.getGroups();
+ newGroups = prevPs.groups();
}
}
patchSet =
@@ -222,7 +222,7 @@ public class PatchSetInserter implements BatchUpdateOp {
if (message != null) {
changeMessage =
ChangeMessagesUtil.newMessage(
- patchSet.getId(),
+ patchSet.id(),
ctx.getUser(),
ctx.getWhen(),
message,
@@ -288,7 +288,7 @@ public class PatchSetInserter implements BatchUpdateOp {
commitId,
refName.substring(0, refName.lastIndexOf('/') + 1) + "new"),
projectCache.checkedGet(origNotes.getProjectName()).getProject(),
- origNotes.getChange().getDest().get(),
+ origNotes.getChange().getDest().branch(),
ctx.getRevWalk().getObjectReader(),
commitId,
ctx.getIdentifiedUser())) {
diff --git a/java/com/google/gerrit/server/change/PureRevert.java b/java/com/google/gerrit/server/change/PureRevert.java
index acb3dd7879..cb632dc79c 100644
--- a/java/com/google/gerrit/server/change/PureRevert.java
+++ b/java/com/google/gerrit/server/change/PureRevert.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.git.PureRevertCache;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.inject.Inject;
@@ -54,8 +54,6 @@ public class PureRevert {
}
return pureRevertCache.isPureRevert(
- notes.getProjectName(),
- ObjectId.fromString(notes.getCurrentPatchSet().getRevision().get()),
- claimedOriginalObjectId);
+ notes.getProjectName(), notes.getCurrentPatchSet().commitId(), claimedOriginalObjectId);
}
}
diff --git a/java/com/google/gerrit/server/change/RebaseChangeOp.java b/java/com/google/gerrit/server/change/RebaseChangeOp.java
index fccda7c138..4723af8514 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -16,11 +16,10 @@ package com.google.gerrit.server.change;
import static com.google.common.base.Preconditions.checkState;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -151,10 +150,8 @@ public class RebaseChangeOp implements BatchUpdateOp {
NoSuchChangeException, PermissionBackendException {
// Ok that originalPatchSet was not read in a transaction, since we just
// need its revision.
- RevId oldRev = originalPatchSet.getRevision();
-
RevWalk rw = ctx.getRevWalk();
- RevCommit original = rw.parseCommit(ObjectId.fromString(oldRev.get()));
+ RevCommit original = rw.parseCommit(originalPatchSet.commitId());
rw.parseBody(original);
RevCommit baseCommit = rw.parseCommit(baseCommitId);
CurrentUser changeOwner = identifiedUserFactory.create(notes.getChange().getOwner());
@@ -164,7 +161,7 @@ public class RebaseChangeOp implements BatchUpdateOp {
rw.parseBody(baseCommit);
newCommitMessage =
newMergeUtil()
- .createCommitMessageOnSubmit(original, baseCommit, notes, originalPatchSet.getId());
+ .createCommitMessageOnSubmit(original, baseCommit, notes, originalPatchSet.id());
} else {
newCommitMessage = original.getFullMessage();
}
@@ -178,9 +175,7 @@ public class RebaseChangeOp implements BatchUpdateOp {
rebasedPatchSetId =
ChangeUtil.nextPatchSetIdFromChangeRefs(
- ctx.getRepoView()
- .getRefs(originalPatchSet.getId().getParentKey().toRefPrefix())
- .keySet(),
+ ctx.getRepoView().getRefs(originalPatchSet.id().changeId().toRefPrefix()).keySet(),
notes.getChange().currentPatchSetId());
patchSetInserter =
patchSetInserterFactory
@@ -195,14 +190,14 @@ public class RebaseChangeOp implements BatchUpdateOp {
"Patch Set "
+ rebasedPatchSetId.get()
+ ": Patch Set "
- + originalPatchSet.getId().get()
+ + originalPatchSet.id().get()
+ " was rebased");
}
if (base != null && !base.notes().getChange().isMerged()) {
if (!base.notes().getChange().isMerged()) {
// Add to end of relation chain for open base change.
- patchSetInserter.setGroups(base.patchSet().getGroups());
+ patchSetInserter.setGroups(base.patchSet().groups());
} else {
// If the base is merged, start a new relation chain.
patchSetInserter.setGroups(GroupCollector.getDefaultGroups(rebasedCommit));
diff --git a/java/com/google/gerrit/server/change/RebaseUtil.java b/java/com/google/gerrit/server/change/RebaseUtil.java
index a4cf5ba5d5..2d36df204a 100644
--- a/java/com/google/gerrit/server/change/RebaseUtil.java
+++ b/java/com/google/gerrit/server/change/RebaseUtil.java
@@ -17,14 +17,14 @@ package com.google.gerrit.server.change;
import com.google.auto.value.AutoValue;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.query.change.ChangeData;
@@ -56,7 +56,7 @@ public class RebaseUtil {
this.psUtil = psUtil;
}
- public boolean canRebase(PatchSet patchSet, Branch.NameKey dest, Repository git, RevWalk rw) {
+ public boolean canRebase(PatchSet patchSet, BranchNameKey dest, Repository git, RevWalk rw) {
try {
findBaseRevision(patchSet, dest, git, rw);
return true;
@@ -64,7 +64,7 @@ public class RebaseUtil {
return false;
} catch (StorageException | IOException e) {
logger.atWarning().withCause(e).log(
- "Error checking if patch set %s on %s can be rebased", patchSet.getId(), dest);
+ "Error checking if patch set %s on %s can be rebased", patchSet.id(), dest);
return false;
}
}
@@ -87,18 +87,18 @@ public class RebaseUtil {
// Try parsing the base as a ref string.
PatchSet.Id basePatchSetId = PatchSet.Id.fromRef(base);
if (basePatchSetId != null) {
- Change.Id baseChangeId = basePatchSetId.getParentKey();
+ Change.Id baseChangeId = basePatchSetId.changeId();
ChangeNotes baseNotes = notesFor(rsrc, baseChangeId);
if (baseNotes != null) {
return Base.create(
- notesFor(rsrc, basePatchSetId.getParentKey()), psUtil.get(baseNotes, basePatchSetId));
+ notesFor(rsrc, basePatchSetId.changeId()), psUtil.get(baseNotes, basePatchSetId));
}
}
// Try parsing base as a change number (assume current patch set).
Integer baseChangeId = Ints.tryParse(base);
if (baseChangeId != null) {
- ChangeNotes baseNotes = notesFor(rsrc, new Change.Id(baseChangeId));
+ ChangeNotes baseNotes = notesFor(rsrc, Change.id(baseChangeId));
if (baseNotes != null) {
return Base.create(baseNotes, psUtil.current(baseNotes));
}
@@ -108,10 +108,10 @@ public class RebaseUtil {
Base ret = null;
for (ChangeData cd : queryProvider.get().byProjectCommit(rsrc.getProject(), base)) {
for (PatchSet ps : cd.patchSets()) {
- if (!ps.getRevision().matches(base)) {
+ if (!ObjectIds.matchesAbbreviation(ps.commitId(), base)) {
continue;
}
- if (ret == null || ret.patchSet().getId().get() < ps.getId().get()) {
+ if (ret == null || ret.patchSet().id().get() < ps.id().get()) {
ret = Base.create(cd.notes(), ps);
}
}
@@ -141,10 +141,10 @@ public class RebaseUtil {
* @throws IOException if accessing the repository fails.
*/
public ObjectId findBaseRevision(
- PatchSet patchSet, Branch.NameKey destBranch, Repository git, RevWalk rw)
+ PatchSet patchSet, BranchNameKey destBranch, Repository git, RevWalk rw)
throws RestApiException, IOException {
- String baseRev = null;
- RevCommit commit = rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
+ ObjectId baseId = null;
+ RevCommit commit = rw.parseCommit(patchSet.commitId());
if (commit.getParentCount() > 1) {
throw new UnprocessableEntityException("Cannot rebase a change with multiple parents.");
@@ -153,12 +153,12 @@ public class RebaseUtil {
"Cannot rebase a change without any parents (is this the initial commit?).");
}
- RevId parentRev = new RevId(commit.getParent(0).name());
+ ObjectId parentId = commit.getParent(0);
CHANGES:
- for (ChangeData cd : queryProvider.get().byBranchCommit(destBranch, parentRev.get())) {
+ for (ChangeData cd : queryProvider.get().byBranchCommit(destBranch, parentId.name())) {
for (PatchSet depPatchSet : cd.patchSets()) {
- if (!depPatchSet.getRevision().equals(parentRev)) {
+ if (!depPatchSet.commitId().equals(parentId)) {
continue;
}
Change depChange = cd.change();
@@ -168,29 +168,29 @@ public class RebaseUtil {
}
if (depChange.isNew()) {
- if (depPatchSet.getId().equals(depChange.currentPatchSetId())) {
+ if (depPatchSet.id().equals(depChange.currentPatchSetId())) {
throw new ResourceConflictException(
"Change is already based on the latest patch set of the dependent change.");
}
- baseRev = cd.currentPatchSet().getRevision().get();
+ baseId = cd.currentPatchSet().commitId();
}
break CHANGES;
}
}
- if (baseRev == null) {
+ if (baseId == null) {
// We are dependent on a merged PatchSet or have no PatchSet
// dependencies at all.
- Ref destRef = git.getRefDatabase().exactRef(destBranch.get());
+ Ref destRef = git.getRefDatabase().exactRef(destBranch.branch());
if (destRef == null) {
throw new UnprocessableEntityException(
- "The destination branch does not exist: " + destBranch.get());
+ "The destination branch does not exist: " + destBranch.branch());
}
- baseRev = destRef.getObjectId().getName();
- if (baseRev.equals(parentRev.get())) {
+ baseId = destRef.getObjectId();
+ if (baseId.equals(parentId)) {
throw new ResourceConflictException("Change is already up to date.");
}
}
- return ObjectId.fromString(baseRev);
+ return baseId;
}
}
diff --git a/java/com/google/gerrit/server/change/ReviewerAdder.java b/java/com/google/gerrit/server/change/ReviewerAdder.java
index a6ad559fac..ba6ba21009 100644
--- a/java/com/google/gerrit/server/change/ReviewerAdder.java
+++ b/java/com/google/gerrit/server/change/ReviewerAdder.java
@@ -31,6 +31,13 @@ import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -41,13 +48,6 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -237,7 +237,8 @@ public class ReviewerAdder {
revision.getUser(),
ImmutableSet.of(user.getAccountId()),
null,
- true);
+ true,
+ false);
}
@Nullable
@@ -260,7 +261,13 @@ public class ReviewerAdder {
if (isValidReviewer(notes.getChange().getDest(), reviewerUser.getAccount())) {
return new ReviewerAddition(
- input, notes, user, ImmutableSet.of(reviewerUser.getAccountId()), null, exactMatchFound);
+ input,
+ notes,
+ user,
+ ImmutableSet.of(reviewerUser.getAccountId()),
+ null,
+ exactMatchFound,
+ false);
}
return fail(
input,
@@ -340,11 +347,11 @@ public class ReviewerAdder {
for (Account member : members) {
if (isValidReviewer(notes.getChange().getDest(), member)) {
- reviewers.add(member.getId());
+ reviewers.add(member.id());
}
}
- return new ReviewerAddition(input, notes, user, reviewers, null, true);
+ return new ReviewerAddition(input, notes, user, reviewers, null, true, true);
}
@Nullable
@@ -366,16 +373,16 @@ public class ReviewerAdder {
FailureType.NOT_FOUND,
MessageFormat.format(ChangeMessages.get().reviewerInvalid, input.reviewer));
}
- return new ReviewerAddition(input, notes, user, null, ImmutableList.of(adr), true);
+ return new ReviewerAddition(input, notes, user, null, ImmutableList.of(adr), true, false);
}
- private boolean isValidReviewer(Branch.NameKey branch, Account member)
+ private boolean isValidReviewer(BranchNameKey branch, Account member)
throws PermissionBackendException {
try {
// Check ref permission instead of change permission, since change permissions take into
// account the private bit, whereas adding a user as a reviewer is explicitly allowing them to
// see private changes.
- permissionBackend.absentUser(member.getId()).ref(branch).check(RefPermission.READ);
+ permissionBackend.absentUser(member.id()).ref(branch).check(RefPermission.READ);
return true;
} catch (AuthException e) {
return false;
@@ -421,7 +428,8 @@ public class ReviewerAdder {
CurrentUser caller,
@Nullable Iterable<Account.Id> reviewers,
@Nullable Iterable<Address> reviewersByEmail,
- boolean exactMatchFound) {
+ boolean exactMatchFound,
+ boolean forGroup) {
checkArgument(
reviewers != null || reviewersByEmail != null,
"must have either reviewers or reviewersByEmail");
@@ -435,7 +443,7 @@ public class ReviewerAdder {
this.reviewersByEmail =
reviewersByEmail == null ? ImmutableSet.of() : ImmutableSet.copyOf(reviewersByEmail);
this.caller = caller.asIdentifiedUser();
- op = addReviewersOpFactory.create(this.reviewers, this.reviewersByEmail, state());
+ op = addReviewersOpFactory.create(this.reviewers, this.reviewersByEmail, state(), forGroup);
this.exactMatchFound = exactMatchFound;
}
@@ -469,8 +477,8 @@ public class ReviewerAdder {
// New reviewers have value 0, don't bother normalizing.
result.reviewers.add(
json.format(
- new ReviewerInfo(psa.getAccountId().get()),
- psa.getAccountId(),
+ new ReviewerInfo(psa.accountId().get()),
+ psa.accountId(),
cd,
ImmutableList.of(psa)));
}
diff --git a/java/com/google/gerrit/server/change/ReviewerJson.java b/java/com/google/gerrit/server/change/ReviewerJson.java
index 2742bb944d..6686ed8808 100644
--- a/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -21,12 +21,12 @@ import com.google.common.collect.Lists;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.api.changes.ReviewerInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.permissions.LabelPermission;
@@ -111,9 +111,9 @@ public class ReviewerJson {
out.approvals = new TreeMap<>(labelTypes.nameComparator());
for (PatchSetApproval ca : approvals) {
- LabelType at = labelTypes.byLabel(ca.getLabelId());
+ LabelType at = labelTypes.byLabel(ca.labelId());
if (at != null) {
- out.approvals.put(at.getName(), formatValue(ca.getValue()));
+ out.approvals.put(at.getName(), formatValue(ca.value()));
}
}
diff --git a/java/com/google/gerrit/server/change/ReviewerResource.java b/java/com/google/gerrit/server/change/ReviewerResource.java
index 52f35853bf..df0a03f72d 100644
--- a/java/com/google/gerrit/server/change/ReviewerResource.java
+++ b/java/com/google/gerrit/server/change/ReviewerResource.java
@@ -17,11 +17,11 @@ package com.google.gerrit.server.change;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.IdentifiedUser;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.Assisted;
diff --git a/java/com/google/gerrit/server/change/ReviewerSuggestion.java b/java/com/google/gerrit/server/change/ReviewerSuggestion.java
index 198a5fde04..1b2a0086a4 100644
--- a/java/com/google/gerrit/server/change/ReviewerSuggestion.java
+++ b/java/com/google/gerrit/server/change/ReviewerSuggestion.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.change;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import java.util.Set;
/**
@@ -35,9 +35,8 @@ public interface ReviewerSuggestion {
* @param changeId The changeId that the suggestion is for. Can be {@code null}.
* @param query The query as typed by the user. Can be {@code null}.
* @param candidates A set of candidates for the ranking. Can be empty.
- * @return Set of {@link SuggestedReviewer}s. The {@link
- * com.google.gerrit.reviewdb.client.Account.Id}s listed here don't have to be included in
- * {@code candidates}.
+ * @return Set of {@link SuggestedReviewer}s. The {@link com.google.gerrit.entities.Account.Id}s
+ * listed here don't have to be included in {@code candidates}.
*/
Set<SuggestedReviewer> suggestReviewers(
Project.NameKey project,
diff --git a/java/com/google/gerrit/server/change/RevisionJson.java b/java/com/google/gerrit/server/change/RevisionJson.java
index aa733cdfe3..fbd14c4428 100644
--- a/java/com/google/gerrit/server/change/RevisionJson.java
+++ b/java/com/google/gerrit/server/change/RevisionJson.java
@@ -28,10 +28,15 @@ import static com.google.gerrit.extensions.client.ListChangesOption.PUSH_CERTIFI
import static com.google.gerrit.extensions.client.ListChangesOption.WEB_LINKS;
import static com.google.gerrit.server.CommonConverters.toGitPerson;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.CommitInfo;
@@ -44,10 +49,6 @@ import com.google.gerrit.extensions.config.DownloadScheme;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GpgException;
@@ -71,7 +72,6 @@ import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jgit.lib.ObjectId;
@@ -182,7 +182,7 @@ public class RevisionJson {
info.message = commit.getFullMessage();
if (addLinks) {
- List<WebLinkInfo> links = webLinks.getPatchSetLinks(project, commit.name());
+ ImmutableList<WebLinkInfo> links = webLinks.getPatchSetLinks(project, commit.name());
info.webLinks = links.isEmpty() ? null : links;
}
@@ -192,7 +192,7 @@ public class RevisionJson {
i.commit = parent.name();
i.subject = parent.getShortMessage();
if (addLinks) {
- List<WebLinkInfo> parentLinks = webLinks.getParentLinks(project, parent.name());
+ ImmutableList<WebLinkInfo> parentLinks = webLinks.getParentLinks(project, parent.name());
i.webLinks = parentLinks.isEmpty() ? null : parentLinks;
}
info.parents.add(i);
@@ -216,7 +216,7 @@ public class RevisionJson {
try (Repository repo = openRepoIfNecessary(cd.project());
RevWalk rw = newRevWalk(repo)) {
for (PatchSet in : map.values()) {
- PatchSet.Id id = in.getId();
+ PatchSet.Id id = in.id();
boolean want;
if (has(ALL_REVISIONS)) {
want = true;
@@ -227,7 +227,7 @@ public class RevisionJson {
}
if (want) {
res.put(
- in.getRevision().get(),
+ in.commitId().name(),
toRevisionInfo(accountLoader, cd, in, repo, rw, false, changeInfo));
}
}
@@ -251,7 +251,7 @@ public class RevisionJson {
String projectName = cd.project().get();
String url = scheme.getUrl(projectName);
- String refName = in.getRefName();
+ String refName = in.refName();
FetchInfo fetchInfo = new FetchInfo(url, refName);
r.put(schemeName, fetchInfo);
@@ -275,14 +275,14 @@ public class RevisionJson {
throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
Change c = cd.change();
RevisionInfo out = new RevisionInfo();
- out.isCurrent = in.getId().equals(c.currentPatchSetId());
- out._number = in.getId().get();
- out.ref = in.getRefName();
- out.created = in.getCreatedOn();
- out.uploader = accountLoader.get(in.getUploader());
+ out.isCurrent = in.id().equals(c.currentPatchSetId());
+ out._number = in.id().get();
+ out.ref = in.refName();
+ out.created = in.createdOn();
+ out.uploader = accountLoader.get(in.uploader());
out.fetch = makeFetchMap(cd, in);
out.kind = changeKindCache.getChangeKind(rw, repo != null ? repo.getConfig() : null, cd, in);
- out.description = in.getDescription();
+ out.description = in.description().orElse(null);
boolean setCommit = has(ALL_COMMITS) || (out.isCurrent && has(CURRENT_COMMIT));
boolean addFooters = out.isCurrent && has(COMMIT_FOOTERS);
@@ -290,14 +290,14 @@ public class RevisionJson {
checkState(rw != null);
checkState(repo != null);
Project.NameKey project = c.getProject();
- String rev = in.getRevision().get();
+ String rev = in.commitId().name();
RevCommit commit = rw.parseCommit(ObjectId.fromString(rev));
rw.parseBody(commit);
if (setCommit) {
out.commit = getCommitInfo(project, rw, commit, has(WEB_LINKS), fillCommit);
}
if (addFooters) {
- Ref ref = repo.exactRef(cd.change().getDest().get());
+ Ref ref = repo.exactRef(cd.change().getDest().branch());
RevCommit mergeTip = null;
if (ref != null) {
mergeTip = rw.parseCommit(ref.getObjectId());
@@ -306,7 +306,7 @@ public class RevisionJson {
out.commitWithFooters =
mergeUtilFactory
.create(projectCache.get(project))
- .createCommitMessageOnSubmit(commit, mergeTip, cd.notes(), in.getId());
+ .createCommitMessageOnSubmit(commit, mergeTip, cd.notes(), in.id());
}
}
@@ -324,10 +324,10 @@ public class RevisionJson {
}
if (gpgApi.isEnabled() && has(PUSH_CERTIFICATES)) {
- if (in.getPushCertificate() != null) {
+ if (in.pushCertificate().isPresent()) {
out.pushCertificate =
gpgApi.checkPushCertificate(
- in.getPushCertificate(), userFactory.create(in.getUploader()));
+ in.pushCertificate().get(), userFactory.create(in.uploader()));
} else {
out.pushCertificate = new PushCertificateInfo();
}
diff --git a/java/com/google/gerrit/server/change/RevisionResource.java b/java/com/google/gerrit/server/change/RevisionResource.java
index caafe24a57..30fa593b09 100644
--- a/java/com/google/gerrit/server/change/RevisionResource.java
+++ b/java/com/google/gerrit/server/change/RevisionResource.java
@@ -16,15 +16,18 @@ package com.google.gerrit.server.change;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestResource.HasETag;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.edit.ChangeEdit;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.inject.TypeLiteral;
@@ -89,9 +92,18 @@ public class RevisionResource implements RestResource, HasETag {
@Override
public String getETag() {
- Hasher h = Hashing.murmur3_128().newHasher();
- prepareETag(h, getUser());
- return h.hash().toString();
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Compute revision ETag",
+ Metadata.builder()
+ .changeId(change.getId().get())
+ .patchSetId(ps.number())
+ .projectName(change.getProject().get())
+ .build())) {
+ Hasher h = Hashing.murmur3_128().newHasher();
+ prepareETag(h, getUser());
+ return h.hash().toString();
+ }
}
public void prepareETag(Hasher h, CurrentUser user) {
@@ -114,7 +126,7 @@ public class RevisionResource implements RestResource, HasETag {
@Override
public String toString() {
- String s = ps.getId().toString();
+ String s = ps.id().toString();
if (edit.isPresent()) {
s = "edit:" + s;
}
@@ -122,6 +134,6 @@ public class RevisionResource implements RestResource, HasETag {
}
public boolean isCurrent() {
- return ps.getId().equals(getChange().currentPatchSetId());
+ return ps.id().equals(getChange().currentPatchSetId());
}
}
diff --git a/java/com/google/gerrit/server/change/RobotCommentResource.java b/java/com/google/gerrit/server/change/RobotCommentResource.java
index c4fab582e9..b12727d21a 100644
--- a/java/com/google/gerrit/server/change/RobotCommentResource.java
+++ b/java/com/google/gerrit/server/change/RobotCommentResource.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.change;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.inject.TypeLiteral;
public class RobotCommentResource implements RestResource {
diff --git a/java/com/google/gerrit/server/change/SetAssigneeOp.java b/java/com/google/gerrit/server/change/SetAssigneeOp.java
index 8d350c32bd..9848150cd6 100644
--- a/java/com/google/gerrit/server/change/SetAssigneeOp.java
+++ b/java/com/google/gerrit/server/change/SetAssigneeOp.java
@@ -17,10 +17,10 @@ package com.google.gerrit.server.change;
import static java.util.Objects.requireNonNull;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.AssigneeChanged;
diff --git a/java/com/google/gerrit/server/change/SetHashtagsOp.java b/java/com/google/gerrit/server/change/SetHashtagsOp.java
index abc4eeec14..712e1f31fc 100644
--- a/java/com/google/gerrit/server/change/SetHashtagsOp.java
+++ b/java/com/google/gerrit/server/change/SetHashtagsOp.java
@@ -21,12 +21,12 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.extensions.api.changes.HashtagsInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.change.HashtagsUtil.InvalidHashtagException;
import com.google.gerrit.server.extensions.events.HashtagsEdited;
diff --git a/java/com/google/gerrit/server/change/SetPrivateOp.java b/java/com/google/gerrit/server/change/SetPrivateOp.java
index 1600fd592a..28d178dd0f 100644
--- a/java/com/google/gerrit/server/change/SetPrivateOp.java
+++ b/java/com/google/gerrit/server/change/SetPrivateOp.java
@@ -16,11 +16,11 @@ package com.google.gerrit.server.change;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PatchSetUtil;
diff --git a/java/com/google/gerrit/server/change/SuggestedReviewer.java b/java/com/google/gerrit/server/change/SuggestedReviewer.java
index 353bf3b084..1b30199737 100644
--- a/java/com/google/gerrit/server/change/SuggestedReviewer.java
+++ b/java/com/google/gerrit/server/change/SuggestedReviewer.java
@@ -13,7 +13,7 @@
// limitations under the License.
package com.google.gerrit.server.change;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
public class SuggestedReviewer {
diff --git a/java/com/google/gerrit/server/change/WalkSorter.java b/java/com/google/gerrit/server/change/WalkSorter.java
index 5945a0cb9d..816a9042d4 100644
--- a/java/com/google/gerrit/server/change/WalkSorter.java
+++ b/java/com/google/gerrit/server/change/WalkSorter.java
@@ -24,9 +24,9 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Ordering;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
@@ -41,7 +41,6 @@ import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
@@ -223,27 +222,26 @@ public class WalkSorter {
for (ChangeData cd : in) {
PatchSet maxPs = null;
for (PatchSet ps : cd.patchSets()) {
- if (shouldInclude(ps) && (maxPs == null || ps.getId().get() > maxPs.getId().get())) {
+ if (shouldInclude(ps) && (maxPs == null || ps.id().get() > maxPs.id().get())) {
maxPs = ps;
}
}
if (maxPs == null) {
continue; // No patch sets matched.
}
- ObjectId id = ObjectId.fromString(maxPs.getRevision().get());
try {
- RevCommit c = rw.parseCommit(id);
+ RevCommit c = rw.parseCommit(maxPs.commitId());
byCommit.put(c, PatchSetData.create(cd, maxPs, c));
} catch (MissingObjectException | IncorrectObjectTypeException e) {
logger.atWarning().withCause(e).log(
- "missing commit %s for patch set %s", id.name(), maxPs.getId());
+ "missing commit %s for patch set %s", maxPs.commitId().name(), maxPs.id());
}
}
return byCommit;
}
private boolean shouldInclude(PatchSet ps) {
- return includePatchSets.isEmpty() || includePatchSets.contains(ps.getId());
+ return includePatchSets.isEmpty() || includePatchSets.contains(ps.id());
}
private static void markStart(RevWalk rw, Iterable<RevCommit> commits) throws IOException {
diff --git a/java/com/google/gerrit/server/change/WorkInProgressOp.java b/java/com/google/gerrit/server/change/WorkInProgressOp.java
index f3f1a2974d..78edadab82 100644
--- a/java/com/google/gerrit/server/change/WorkInProgressOp.java
+++ b/java/com/google/gerrit/server/change/WorkInProgressOp.java
@@ -17,10 +17,10 @@ package com.google.gerrit.server.change;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.extensions.events.WorkInProgressStateChanged;
diff --git a/java/com/google/gerrit/server/change/testing/TestChangeETagComputation.java b/java/com/google/gerrit/server/change/testing/TestChangeETagComputation.java
new file mode 100644
index 0000000000..344b9b3024
--- /dev/null
+++ b/java/com/google/gerrit/server/change/testing/TestChangeETagComputation.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change.testing;
+
+import com.google.gerrit.server.change.ChangeETagComputation;
+
+public class TestChangeETagComputation {
+
+ public static ChangeETagComputation withETag(String etag) {
+ return (p, id) -> etag;
+ }
+
+ public static ChangeETagComputation withException(RuntimeException e) {
+ return (p, id) -> {
+ throw e;
+ };
+ }
+}
diff --git a/java/com/google/gerrit/server/config/AllProjectsName.java b/java/com/google/gerrit/server/config/AllProjectsName.java
index 7719e38dbb..6d5525c96f 100644
--- a/java/com/google/gerrit/server/config/AllProjectsName.java
+++ b/java/com/google/gerrit/server/config/AllProjectsName.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.config;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
/** Special name of the project that all projects derive from. */
public class AllProjectsName extends Project.NameKey {
diff --git a/java/com/google/gerrit/server/config/AllUsersName.java b/java/com/google/gerrit/server/config/AllUsersName.java
index 22d29a4fef..aa92db899d 100644
--- a/java/com/google/gerrit/server/config/AllUsersName.java
+++ b/java/com/google/gerrit/server/config/AllUsersName.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.config;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
/** Special name of the project in which meta data for all users is stored. */
public class AllUsersName extends Project.NameKey {
diff --git a/java/com/google/gerrit/server/config/DownloadConfig.java b/java/com/google/gerrit/server/config/DownloadConfig.java
index e9d5e5e83e..6dea07d660 100644
--- a/java/com/google/gerrit/server/config/DownloadConfig.java
+++ b/java/com/google/gerrit/server/config/DownloadConfig.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.config;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.CoreDownloadSchemes;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DownloadCommand;
-import com.google.gerrit.reviewdb.client.CoreDownloadSchemes;
import com.google.gerrit.server.change.ArchiveFormat;
import com.google.inject.Inject;
import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 5dad6a80f8..25f2b20269 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -30,6 +30,7 @@ import com.google.gerrit.extensions.config.DownloadScheme;
import com.google.gerrit.extensions.config.ExternalIncludedIn;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.extensions.config.PluginProjectPermissionDefinition;
+import com.google.gerrit.extensions.events.AccountActivationListener;
import com.google.gerrit.extensions.events.AccountIndexedListener;
import com.google.gerrit.extensions.events.AgreementSignupListener;
import com.google.gerrit.extensions.events.AssigneeChangedListener;
@@ -61,6 +62,7 @@ import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.validators.CommentValidator;
import com.google.gerrit.extensions.webui.BranchWebLink;
import com.google.gerrit.extensions.webui.DiffWebLink;
import com.google.gerrit.extensions.webui.FileHistoryWebLink;
@@ -76,7 +78,10 @@ import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.CmdLineParserModule;
import com.google.gerrit.server.CreateGroupPermissionSyncer;
import com.google.gerrit.server.DynamicOptions;
+import com.google.gerrit.server.ExceptionHook;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.RequestListener;
+import com.google.gerrit.server.TraceRequestListener;
import com.google.gerrit.server.account.AccountCacheImpl;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountDeactivator;
@@ -98,6 +103,7 @@ import com.google.gerrit.server.cache.CacheRemovalListener;
import com.google.gerrit.server.change.AbandonOp;
import com.google.gerrit.server.change.AccountPatchReviewStore;
import com.google.gerrit.server.change.ChangeAttributeFactory;
+import com.google.gerrit.server.change.ChangeETagComputation;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeKindCacheImpl;
@@ -135,6 +141,7 @@ import com.google.gerrit.server.git.validators.UploadValidationListener;
import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.group.db.GroupDbModule;
import com.google.gerrit.server.index.change.ReindexAfterRefUpdate;
+import com.google.gerrit.server.logging.PerformanceLogger;
import com.google.gerrit.server.mail.AutoReplyMailFilter;
import com.google.gerrit.server.mail.EmailModule;
import com.google.gerrit.server.mail.ListMailFilter;
@@ -142,7 +149,8 @@ import com.google.gerrit.server.mail.MailFilter;
import com.google.gerrit.server.mail.send.FromAddressGenerator;
import com.google.gerrit.server.mail.send.FromAddressGeneratorProvider;
import com.google.gerrit.server.mail.send.InboundEmailRejectionSender;
-import com.google.gerrit.server.mail.send.MailSoyTofuProvider;
+import com.google.gerrit.server.mail.send.MailSoySauceProvider;
+import com.google.gerrit.server.mail.send.MailSoyTemplateProvider;
import com.google.gerrit.server.mail.send.MailTemplates;
import com.google.gerrit.server.mime.FileTypeRegistry;
import com.google.gerrit.server.mime.MimeUtilFileTypeRegistry;
@@ -190,7 +198,7 @@ import com.google.gitiles.blame.cache.BlameCacheImpl;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.UniqueAnnotations;
-import com.google.template.soy.tofu.SoyTofu;
+import com.google.template.soy.jbcsrc.api.SoySauce;
import java.util.List;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.transport.PostReceiveHook;
@@ -281,7 +289,7 @@ public class GerritGlobalModule extends FactoryModule {
bind(ApprovalsUtil.class);
- bind(SoyTofu.class).annotatedWith(MailTemplates.class).toProvider(MailSoyTofuProvider.class);
+ bind(SoySauce.class).annotatedWith(MailTemplates.class).toProvider(MailSoySauceProvider.class);
bind(FromAddressGenerator.class).toProvider(FromAddressGeneratorProvider.class).in(SINGLETON);
bind(Boolean.class)
.annotatedWith(EnableReverseDnsLookup.class)
@@ -325,6 +333,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), PostReceiveHook.class);
DynamicSet.setOf(binder(), PreUploadHook.class);
DynamicSet.setOf(binder(), PostUploadHook.class);
+ DynamicSet.setOf(binder(), AccountActivationListener.class);
DynamicSet.setOf(binder(), AccountIndexedListener.class);
DynamicSet.setOf(binder(), ChangeIndexedListener.class);
DynamicSet.setOf(binder(), GroupIndexedListener.class);
@@ -341,6 +350,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.bind(binder(), EventListener.class).to(EventsMetrics.class);
DynamicSet.setOf(binder(), UserScopedEventListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class);
+ DynamicSet.setOf(binder(), CommentValidator.class);
DynamicSet.setOf(binder(), ChangeMessageModifier.class);
DynamicSet.setOf(binder(), RefOperationValidationListener.class);
DynamicSet.setOf(binder(), OnSubmitValidationListener.class);
@@ -380,6 +390,12 @@ public class GerritGlobalModule extends FactoryModule {
DynamicItem.itemOf(binder(), ProjectNameLockManager.class);
DynamicSet.setOf(binder(), SubmitRule.class);
DynamicSet.setOf(binder(), QuotaEnforcer.class);
+ DynamicSet.setOf(binder(), PerformanceLogger.class);
+ DynamicSet.setOf(binder(), RequestListener.class);
+ DynamicSet.bind(binder(), RequestListener.class).to(TraceRequestListener.class);
+ DynamicSet.setOf(binder(), ChangeETagComputation.class);
+ DynamicSet.setOf(binder(), ExceptionHook.class);
+ DynamicSet.setOf(binder(), MailSoyTemplateProvider.class);
DynamicMap.mapOf(binder(), MailFilter.class);
bind(MailFilter.class).annotatedWith(Exports.named("ListMailFilter")).to(ListMailFilter.class);
diff --git a/java/com/google/gerrit/server/config/GerritIsReplica.java b/java/com/google/gerrit/server/config/GerritIsReplica.java
new file mode 100644
index 0000000000..154fdcd36d
--- /dev/null
+++ b/java/com/google/gerrit/server/config/GerritIsReplica.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.config;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+
+/* Marker on {@link Boolean} indicating whether Gerrit is run as a read-only replica. */
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface GerritIsReplica {}
diff --git a/java/com/google/gerrit/server/config/GerritIsReplicaProvider.java b/java/com/google/gerrit/server/config/GerritIsReplicaProvider.java
new file mode 100644
index 0000000000..bd07f7df8f
--- /dev/null
+++ b/java/com/google/gerrit/server/config/GerritIsReplicaProvider.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.config;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import org.eclipse.jgit.lib.Config;
+
+/**
+ * Provides {@link Boolean} annotated with {@link GerritIsReplica}.
+ *
+ * <p>The returned boolean indicates whether Gerrit is run as a read-only replica.
+ */
+@Singleton
+public final class GerritIsReplicaProvider implements Provider<Boolean> {
+ public static final String CONFIG_SECTION = "container";
+ public static final String REPLICA_KEY = "replica";
+ public static final String DEPRECATED_REPLICA_KEY = "slave";
+
+ public final boolean isReplica;
+
+ @Inject
+ public GerritIsReplicaProvider(@GerritServerConfig Config config) {
+ this.isReplica =
+ config.getBoolean(CONFIG_SECTION, REPLICA_KEY, false)
+ || config.getBoolean(CONFIG_SECTION, DEPRECATED_REPLICA_KEY, false);
+ }
+
+ @Override
+ public Boolean get() {
+ return isReplica;
+ }
+}
diff --git a/java/com/google/gerrit/server/config/GerritServerConfigModule.java b/java/com/google/gerrit/server/config/GerritServerConfigModule.java
index 25ee7599f3..3777a55e24 100644
--- a/java/com/google/gerrit/server/config/GerritServerConfigModule.java
+++ b/java/com/google/gerrit/server/config/GerritServerConfigModule.java
@@ -78,5 +78,8 @@ public class GerritServerConfigModule extends AbstractModule {
.annotatedWith(GerritServerConfig.class)
.toProvider(GerritServerConfigProvider.class);
bind(SecureStore.class).toProvider(SecureStoreProvider.class).in(SINGLETON);
+ bind(Boolean.class)
+ .annotatedWith(GerritIsReplica.class)
+ .toProvider(GerritIsReplicaProvider.class);
}
}
diff --git a/java/com/google/gerrit/server/config/GroupSetProvider.java b/java/com/google/gerrit/server/config/GroupSetProvider.java
index 2255a67d16..7f487e1b89 100644
--- a/java/com/google/gerrit/server/config/GroupSetProvider.java
+++ b/java/com/google/gerrit/server/config/GroupSetProvider.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.config;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.util.RequestContext;
diff --git a/java/com/google/gerrit/server/config/PluginConfigFactory.java b/java/com/google/gerrit/server/config/PluginConfigFactory.java
index 2164a86d1f..9e4570186c 100644
--- a/java/com/google/gerrit/server/config/PluginConfigFactory.java
+++ b/java/com/google/gerrit/server/config/PluginConfigFactory.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.config;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.plugins.Plugin;
import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.project.NoSuchProjectException;
diff --git a/java/com/google/gerrit/server/config/ProjectConfigEntry.java b/java/com/google/gerrit/server/config/ProjectConfigEntry.java
index 92ae10ade5..fcfa5e9d49 100644
--- a/java/com/google/gerrit/server/config/ProjectConfigEntry.java
+++ b/java/com/google/gerrit/server/config/ProjectConfigEntry.java
@@ -17,14 +17,14 @@ package com.google.gerrit.server.config;
import static java.util.stream.Collectors.toList;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.google.gerrit.extensions.api.projects.ConfigValue;
import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectState;
@@ -316,7 +316,7 @@ public class ProjectConfigEntry {
@Override
public void onGitReferenceUpdated(Event event) {
- Project.NameKey p = new Project.NameKey(event.getProjectName());
+ Project.NameKey p = Project.nameKey(event.getProjectName());
if (!event.getRefName().equals(RefNames.REFS_CONFIG)) {
return;
}
diff --git a/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java b/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
index a2e0356683..c2538ac44e 100644
--- a/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
+++ b/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.config;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.util.ServerRequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
diff --git a/java/com/google/gerrit/server/config/RepositoryConfig.java b/java/com/google/gerrit/server/config/RepositoryConfig.java
index d8c8468854..f7223216e7 100644
--- a/java/com/google/gerrit/server/config/RepositoryConfig.java
+++ b/java/com/google/gerrit/server/config/RepositoryConfig.java
@@ -19,8 +19,8 @@ import static java.util.Comparator.comparing;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.nio.file.Path;
diff --git a/java/com/google/gerrit/server/config/SitePaths.java b/java/com/google/gerrit/server/config/SitePaths.java
index 47b6336ff3..ee95c6f73f 100644
--- a/java/com/google/gerrit/server/config/SitePaths.java
+++ b/java/com/google/gerrit/server/config/SitePaths.java
@@ -54,6 +54,8 @@ public final class SitePaths {
public final Path secure_config;
public final Path notedb_config;
+ public final Path jgit_config;
+
public final Path ssl_keystore;
public final Path ssh_key;
public final Path ssh_rsa;
@@ -99,6 +101,8 @@ public final class SitePaths {
secure_config = etc_dir.resolve("secure.config");
notedb_config = etc_dir.resolve("notedb.config");
+ jgit_config = etc_dir.resolve("jgit.config");
+
ssl_keystore = etc_dir.resolve("keystore");
ssh_key = etc_dir.resolve("ssh_host_key");
ssh_rsa = etc_dir.resolve("ssh_host_rsa_key");
diff --git a/java/com/google/gerrit/server/config/UrlFormatter.java b/java/com/google/gerrit/server/config/UrlFormatter.java
index eb1e0b256b..6b2510e480 100644
--- a/java/com/google/gerrit/server/config/UrlFormatter.java
+++ b/java/com/google/gerrit/server/config/UrlFormatter.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.config;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import java.util.Optional;
/**
diff --git a/java/com/google/gerrit/server/data/ChangeAttribute.java b/java/com/google/gerrit/server/data/ChangeAttribute.java
index fde592200f..a6da2b98bc 100644
--- a/java/com/google/gerrit/server/data/ChangeAttribute.java
+++ b/java/com/google/gerrit/server/data/ChangeAttribute.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.data;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.common.PluginDefinedInfo;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gson.annotations.SerializedName;
import java.util.List;
diff --git a/java/com/google/gerrit/server/data/PatchAttribute.java b/java/com/google/gerrit/server/data/PatchAttribute.java
index 22f18afaae..2428c54775 100644
--- a/java/com/google/gerrit/server/data/PatchAttribute.java
+++ b/java/com/google/gerrit/server/data/PatchAttribute.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.data;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
+import com.google.gerrit.entities.Patch.ChangeType;
public class PatchAttribute {
public String file;
diff --git a/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java b/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java
index 5d3d1f2013..59ae6f872f 100644
--- a/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java
+++ b/java/com/google/gerrit/server/documentation/QueryDocumentationExecutor.java
@@ -80,8 +80,7 @@ public class QueryDocumentationExecutor {
}
Query query = parser.parse(q);
try {
- // TODO(fishywang): Currently as we don't have much documentation, we just use MAX_VALUE here
- // and skipped paging. Maybe add paging later.
+ // We don't have much documentation, so we just use MAX_VALUE here and skip paging.
TopDocs results = searcher.search(query, Integer.MAX_VALUE);
ScoreDoc[] hits = results.scoreDocs;
long totalHits = results.totalHits;
diff --git a/java/com/google/gerrit/server/edit/ChangeEdit.java b/java/com/google/gerrit/server/edit/ChangeEdit.java
index 11dc380151..c652289a68 100644
--- a/java/com/google/gerrit/server/edit/ChangeEdit.java
+++ b/java/com/google/gerrit/server/edit/ChangeEdit.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.edit;
import static java.util.Objects.requireNonNull;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import org.eclipse.jgit.revwalk.RevCommit;
/**
diff --git a/java/com/google/gerrit/server/edit/ChangeEditJson.java b/java/com/google/gerrit/server/edit/ChangeEditJson.java
index 55e0aefbbf..25dcae06a9 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditJson.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditJson.java
@@ -51,8 +51,8 @@ public class ChangeEditJson {
public EditInfo toEditInfo(ChangeEdit edit, boolean downloadCommands) {
EditInfo out = new EditInfo();
out.commit = fillCommit(edit.getEditCommit());
- out.baseRevision = edit.getBasePatchSet().getRevision().get();
- out.basePatchSetNumber = edit.getBasePatchSet().getPatchSetId();
+ out.baseRevision = edit.getBasePatchSet().commitId().name();
+ out.basePatchSetNumber = edit.getBasePatchSet().number();
out.ref = edit.getRefName();
if (downloadCommands) {
out.fetch = fillFetchMap(edit);
diff --git a/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index 246a53f124..ee93fbf182 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -15,14 +15,14 @@
package com.google.gerrit.server.edit;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -125,7 +125,7 @@ public class ChangeEditModifier {
}
PatchSet currentPatchSet = lookupCurrentPatchSet(notes);
- ObjectId patchSetCommitId = getPatchSetCommitId(currentPatchSet);
+ ObjectId patchSetCommitId = currentPatchSet.commitId();
createEdit(repository, notes, currentPatchSet, patchSetCommitId, TimeUtil.nowTs());
}
@@ -158,7 +158,7 @@ public class ChangeEditModifier {
throw new InvalidChangeOperationException(
String.format(
"Change edit for change %s is already based on latest patch set %s",
- notes.getChangeId(), currentPatchSet.getId()));
+ notes.getChangeId(), currentPatchSet.id()));
}
rebase(repository, changeEdit, currentPatchSet);
@@ -375,7 +375,7 @@ public class ChangeEditModifier {
if (optionalChangeEdit.isPresent()) {
ChangeEdit changeEdit = optionalChangeEdit.get();
newTreeId = merge(repository, changeEdit, newTreeId);
- if (ObjectId.equals(newTreeId, changeEdit.getEditCommit().getTree())) {
+ if (ObjectId.isEqual(newTreeId, changeEdit.getEditCommit().getTree())) {
// Modifications are already contained in the change edit.
return changeEdit;
}
@@ -425,10 +425,10 @@ public class ChangeEditModifier {
String.format(
"Only the patch set %s on which the existing change edit is based may be modified "
+ "(specified patch set: %s)",
- changeEdit.getBasePatchSet().getId(), patchSet.getId()));
+ changeEdit.getBasePatchSet().id(), patchSet.id()));
}
} else {
- PatchSet.Id patchSetId = patchSet.getId();
+ PatchSet.Id patchSetId = patchSet.id();
PatchSet.Id currentPatchSetId = notes.getChange().currentPatchSetId();
if (!patchSetId.equals(currentPatchSetId)) {
throw new InvalidChangeOperationException(
@@ -455,12 +455,12 @@ public class ChangeEditModifier {
private static boolean isBasedOn(ChangeEdit changeEdit, PatchSet patchSet) {
PatchSet editBasePatchSet = changeEdit.getBasePatchSet();
- return editBasePatchSet.getId().equals(patchSet.getId());
+ return editBasePatchSet.id().equals(patchSet.id());
}
private static RevCommit lookupCommit(Repository repository, PatchSet patchSet)
throws IOException {
- ObjectId patchSetCommitId = getPatchSetCommitId(patchSet);
+ ObjectId patchSetCommitId = patchSet.commitId();
return lookupCommit(repository, patchSetCommitId);
}
@@ -483,7 +483,7 @@ public class ChangeEditModifier {
throw new BadRequestException(e.getMessage());
}
- if (ObjectId.equals(newTreeId, baseCommit.getTree())) {
+ if (ObjectId.isEqual(newTreeId, baseCommit.getTree())) {
throw new InvalidChangeOperationException("no changes were made");
}
return newTreeId;
@@ -492,7 +492,7 @@ public class ChangeEditModifier {
private static ObjectId merge(Repository repository, ChangeEdit changeEdit, ObjectId newTreeId)
throws IOException, MergeConflictException {
PatchSet basePatchSet = changeEdit.getBasePatchSet();
- ObjectId basePatchSetCommitId = getPatchSetCommitId(basePatchSet);
+ ObjectId basePatchSetCommitId = basePatchSet.commitId();
ObjectId editCommitId = changeEdit.getEditCommit();
ThreeWayMerger threeWayMerger = MergeStrategy.RESOLVE.newMerger(repository, true);
@@ -531,10 +531,6 @@ public class ChangeEditModifier {
return user.newCommitterIdent(commitTimestamp, tz);
}
- private static ObjectId getPatchSetCommitId(PatchSet patchSet) {
- return ObjectId.fromString(patchSet.getRevision().get());
- }
-
private ChangeEdit createEdit(
Repository repository,
ChangeNotes notes,
@@ -553,7 +549,7 @@ public class ChangeEditModifier {
private String getEditRefName(Change change, PatchSet basePatchSet) {
IdentifiedUser me = currentUser.get().asIdentifiedUser();
- return RefNames.refsEdit(me.getAccountId(), change.getId(), basePatchSet.getId());
+ return RefNames.refsEdit(me.getAccountId(), change.getId(), basePatchSet.id());
}
private ChangeEdit updateEdit(
diff --git a/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index ef1d8803dd..ea34b768fc 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -16,14 +16,14 @@ package com.google.gerrit.server.edit;
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -122,7 +122,7 @@ public class ChangeEditUtil {
String[] refNames = new String[n];
for (int i = n; i > 0; i--) {
refNames[i - 1] =
- RefNames.refsEdit(u.getAccountId(), change.getId(), new PatchSet.Id(change.getId(), i));
+ RefNames.refsEdit(u.getAccountId(), change.getId(), PatchSet.id(change.getId(), i));
}
Ref ref = repo.getRefDatabase().firstExactRef(refNames);
if (ref == null) {
@@ -162,7 +162,7 @@ public class ChangeEditUtil {
ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader)) {
PatchSet basePatchSet = edit.getBasePatchSet();
- if (!basePatchSet.getId().equals(change.currentPatchSetId())) {
+ if (!basePatchSet.id().equals(change.currentPatchSetId())) {
throw new ResourceConflictException("only edit for current patch set can be published");
}
@@ -177,17 +177,14 @@ public class ChangeEditUtil {
new StringBuilder("Patch Set ").append(inserter.getPatchSetId().get()).append(": ");
// Previously checked that the base patch set is the current patch set.
- ObjectId prior = ObjectId.fromString(basePatchSet.getRevision().get());
+ ObjectId prior = basePatchSet.commitId();
ChangeKind kind =
changeKindCache.getChangeKind(change.getProject(), rw, repo.getConfig(), prior, squashed);
if (kind == ChangeKind.NO_CODE_CHANGE) {
message.append("Commit message was updated.");
inserter.setDescription("Edit commit message");
} else {
- message
- .append("Published edit on patch set ")
- .append(basePatchSet.getPatchSetId())
- .append(".");
+ message.append("Published edit on patch set ").append(basePatchSet.number()).append(".");
}
try (BatchUpdate bu = updateFactory.create(change.getProject(), user, TimeUtil.nowTs())) {
@@ -226,7 +223,7 @@ public class ChangeEditUtil {
int pos = ref.getName().lastIndexOf('/');
checkArgument(pos > 0, "invalid edit ref: %s", ref.getName());
String psId = ref.getName().substring(pos + 1);
- return psUtil.get(notes, new PatchSet.Id(notes.getChange().getId(), Integer.parseInt(psId)));
+ return psUtil.get(notes, PatchSet.id(notes.getChange().getId(), Integer.parseInt(psId)));
} catch (StorageException | NumberFormatException e) {
throw new IOException(e);
}
@@ -235,7 +232,7 @@ public class ChangeEditUtil {
private RevCommit squashEdit(
RevWalk rw, ObjectInserter inserter, RevCommit edit, PatchSet basePatchSet)
throws IOException, ResourceConflictException {
- RevCommit parent = rw.parseCommit(ObjectId.fromString(basePatchSet.getRevision().get()));
+ RevCommit parent = rw.parseCommit(basePatchSet.commitId());
if (parent.getTree().equals(edit.getTree())
&& edit.getFullMessage().equals(parent.getFullMessage())) {
throw new ResourceConflictException("identical tree and message");
diff --git a/java/com/google/gerrit/server/events/AssigneeChangedEvent.java b/java/com/google/gerrit/server/events/AssigneeChangedEvent.java
index 60a0935008..490d6d1435 100644
--- a/java/com/google/gerrit/server/events/AssigneeChangedEvent.java
+++ b/java/com/google/gerrit/server/events/AssigneeChangedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class AssigneeChangedEvent extends ChangeEvent {
diff --git a/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java b/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java
index 32b5b021c0..066f398375 100644
--- a/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java
+++ b/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class ChangeAbandonedEvent extends PatchSetEvent {
diff --git a/java/com/google/gerrit/server/events/ChangeDeletedEvent.java b/java/com/google/gerrit/server/events/ChangeDeletedEvent.java
index 63142fd149..017a839bc5 100644
--- a/java/com/google/gerrit/server/events/ChangeDeletedEvent.java
+++ b/java/com/google/gerrit/server/events/ChangeDeletedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class ChangeDeletedEvent extends ChangeEvent {
diff --git a/java/com/google/gerrit/server/events/ChangeEvent.java b/java/com/google/gerrit/server/events/ChangeEvent.java
index 6029dedc0d..d39099cc03 100644
--- a/java/com/google/gerrit/server/events/ChangeEvent.java
+++ b/java/com/google/gerrit/server/events/ChangeEvent.java
@@ -15,9 +15,9 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.data.ChangeAttribute;
public abstract class ChangeEvent extends RefEvent {
@@ -29,7 +29,7 @@ public abstract class ChangeEvent extends RefEvent {
protected ChangeEvent(String type, Change change) {
super(type);
this.project = change.getProject();
- this.refName = RefNames.fullName(change.getDest().get());
+ this.refName = RefNames.fullName(change.getDest().branch());
this.changeKey = change.getKey();
}
diff --git a/java/com/google/gerrit/server/events/ChangeMergedEvent.java b/java/com/google/gerrit/server/events/ChangeMergedEvent.java
index 3fb2ac8b03..3b9632863b 100644
--- a/java/com/google/gerrit/server/events/ChangeMergedEvent.java
+++ b/java/com/google/gerrit/server/events/ChangeMergedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class ChangeMergedEvent extends PatchSetEvent {
diff --git a/java/com/google/gerrit/server/events/ChangeRestoredEvent.java b/java/com/google/gerrit/server/events/ChangeRestoredEvent.java
index 7c86d70722..48100a1fb1 100644
--- a/java/com/google/gerrit/server/events/ChangeRestoredEvent.java
+++ b/java/com/google/gerrit/server/events/ChangeRestoredEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class ChangeRestoredEvent extends PatchSetEvent {
diff --git a/java/com/google/gerrit/server/events/CommentAddedEvent.java b/java/com/google/gerrit/server/events/CommentAddedEvent.java
index bb1ac4d7e7..dbbebe8931 100644
--- a/java/com/google/gerrit/server/events/CommentAddedEvent.java
+++ b/java/com/google/gerrit/server/events/CommentAddedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
diff --git a/java/com/google/gerrit/server/events/CommitReceivedEvent.java b/java/com/google/gerrit/server/events/CommitReceivedEvent.java
index c0f9c29785..6e43621c6e 100644
--- a/java/com/google/gerrit/server/events/CommitReceivedEvent.java
+++ b/java/com/google/gerrit/server/events/CommitReceivedEvent.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.events;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.IdentifiedUser;
import java.io.IOException;
import org.eclipse.jgit.lib.ObjectId;
diff --git a/java/com/google/gerrit/server/events/EventBroker.java b/java/com/google/gerrit/server/events/EventBroker.java
index d5f548ff4b..a6db081e4f 100644
--- a/java/com/google/gerrit/server/events/EventBroker.java
+++ b/java/com/google/gerrit/server/events/EventBroker.java
@@ -15,13 +15,13 @@
package com.google.gerrit.server.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -34,6 +34,7 @@ import com.google.gerrit.server.plugincontext.PluginSetEntryContext;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gson.Gson;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -47,6 +48,8 @@ public class EventBroker implements EventDispatcher {
protected void configure() {
DynamicItem.itemOf(binder(), EventDispatcher.class);
DynamicItem.bind(binder(), EventDispatcher.class).to(EventBroker.class);
+
+ bind(Gson.class).annotatedWith(EventGson.class).toProvider(EventGsonProvider.class);
}
}
@@ -81,7 +84,7 @@ public class EventBroker implements EventDispatcher {
}
@Override
- public void postEvent(Branch.NameKey branchName, RefEvent event)
+ public void postEvent(BranchNameKey branchName, RefEvent event)
throws PermissionBackendException {
fireEvent(branchName, event);
}
@@ -120,7 +123,7 @@ public class EventBroker implements EventDispatcher {
fireEventForUnrestrictedListeners(event);
}
- protected void fireEvent(Branch.NameKey branchName, RefEvent event)
+ protected void fireEvent(BranchNameKey branchName, RefEvent event)
throws PermissionBackendException {
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
CurrentUser user = c.call(UserScopedEventListener::getUser);
@@ -174,9 +177,9 @@ public class EventBroker implements EventDispatcher {
}
}
- protected boolean isVisibleTo(Branch.NameKey branchName, CurrentUser user)
+ protected boolean isVisibleTo(BranchNameKey branchName, CurrentUser user)
throws PermissionBackendException {
- ProjectState pe = projectCache.get(branchName.getParentKey());
+ ProjectState pe = projectCache.get(branchName.project());
if (pe == null || !pe.statePermitsRead()) {
return false;
}
@@ -194,13 +197,13 @@ public class EventBroker implements EventDispatcher {
RefEvent refEvent = (RefEvent) event;
String ref = refEvent.getRefName();
if (PatchSet.isChangeRef(ref)) {
- Change.Id cid = PatchSet.Id.fromRef(ref).getParentKey();
+ Change.Id cid = PatchSet.Id.fromRef(ref).changeId();
try {
Change change = notesFactory.createChecked(refEvent.getProjectNameKey(), cid).getChange();
return isVisibleTo(change, user);
} catch (NoSuchChangeException e) {
logger.atFine().log(
- "Change %s cannot be found, falling back on ref visibility check", cid.id);
+ "Change %s cannot be found, falling back on ref visibility check", cid.get());
}
}
return isVisibleTo(refEvent.getBranchNameKey(), user);
diff --git a/java/com/google/gerrit/server/events/EventDispatcher.java b/java/com/google/gerrit/server/events/EventDispatcher.java
index e6735f2960..a358aee451 100644
--- a/java/com/google/gerrit/server/events/EventDispatcher.java
+++ b/java/com/google/gerrit/server/events/EventDispatcher.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.events;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.permissions.PermissionBackendException;
/** Interface for posting (dispatching) Events */
@@ -37,7 +37,7 @@ public interface EventDispatcher {
* @param event The event to post
* @throws PermissionBackendException on failure of permission checks
*/
- void postEvent(Branch.NameKey branchName, RefEvent event) throws PermissionBackendException;
+ void postEvent(BranchNameKey branchName, RefEvent event) throws PermissionBackendException;
/**
* Post a stream event that is related to a project.
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index efd9bb9cea..3f22d7f8ab 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -24,18 +24,18 @@ import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.UserIdentity;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.index.IndexConfig;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountCache;
@@ -68,7 +68,6 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -127,7 +126,7 @@ public class EventFactory {
public ChangeAttribute asChangeAttribute(Change change) {
ChangeAttribute a = new ChangeAttribute();
a.project = change.getProject().get();
- a.branch = change.getDest().getShortName();
+ a.branch = change.getDest().shortName();
a.topic = change.getTopic();
a.id = change.getKey().get();
a.number = change.getId().get();
@@ -174,12 +173,12 @@ public class EventFactory {
* @return object suitable for serialization to JSON
*/
public RefUpdateAttribute asRefUpdateAttribute(
- ObjectId oldId, ObjectId newId, Branch.NameKey refName) {
+ ObjectId oldId, ObjectId newId, BranchNameKey refName) {
RefUpdateAttribute ru = new RefUpdateAttribute();
ru.newRev = newId != null ? newId.getName() : ObjectId.zeroId().getName();
ru.oldRev = oldId != null ? oldId.getName() : ObjectId.zeroId().getName();
- ru.project = refName.getParentKey().get();
- ru.refName = refName.get();
+ ru.project = refName.project().get();
+ ru.refName = refName.branch();
return ru;
}
@@ -285,7 +284,7 @@ public class EventFactory {
private void addDependsOn(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs)
throws IOException {
- RevCommit commit = rw.parseCommit(ObjectId.fromString(currentPs.getRevision().get()));
+ RevCommit commit = rw.parseCommit(currentPs.commitId());
final List<String> parentNames = new ArrayList<>(commit.getParentCount());
for (RevCommit p : commit.getParents()) {
parentNames.add(p.name());
@@ -296,7 +295,7 @@ public class EventFactory {
for (ChangeData cd : queryProvider.get().byProjectCommits(change.getProject(), parentNames)) {
for (PatchSet ps : cd.patchSets()) {
for (String p : parentNames) {
- if (!ps.getRevision().get().equals(p)) {
+ if (!ps.commitId().name().equals(p)) {
continue;
}
ca.dependsOn.add(newDependsOn(requireNonNull(cd.change()), ps));
@@ -318,18 +317,18 @@ public class EventFactory {
private void addNeededBy(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs)
throws IOException {
- if (currentPs.getGroups().isEmpty()) {
+ if (currentPs.groups().isEmpty()) {
return;
}
- String rev = currentPs.getRevision().get();
+ String rev = currentPs.commitId().name();
// Find changes in the same related group as this patch set, having a patch
// set whose parent matches this patch set's revision.
for (ChangeData cd :
InternalChangeQuery.byProjectGroups(
- queryProvider, indexConfig, change.getProject(), currentPs.getGroups())) {
+ queryProvider, indexConfig, change.getProject(), currentPs.groups())) {
PATCH_SETS:
for (PatchSet ps : cd.patchSets()) {
- RevCommit commit = rw.parseCommit(ObjectId.fromString(ps.getRevision().get()));
+ RevCommit commit = rw.parseCommit(ps.commitId());
for (RevCommit p : commit.getParents()) {
if (!p.name().equals(rev)) {
continue;
@@ -343,7 +342,7 @@ public class EventFactory {
private DependencyAttribute newDependsOn(Change c, PatchSet ps) {
DependencyAttribute d = newDependencyAttribute(c, ps);
- d.isCurrentPatchSet = ps.getId().equals(c.currentPatchSetId());
+ d.isCurrentPatchSet = ps.id().equals(c.currentPatchSetId());
return d;
}
@@ -355,8 +354,8 @@ public class EventFactory {
DependencyAttribute d = new DependencyAttribute();
d.number = c.getId().get();
d.id = c.getKey().toString();
- d.revision = ps.getRevision().get();
- d.ref = ps.getRefName();
+ d.revision = ps.commitId().name();
+ d.ref = ps.refName();
return d;
}
@@ -400,7 +399,7 @@ public class EventFactory {
for (PatchSet p : ps) {
PatchSetAttribute psa = asPatchSetAttribute(revWalk, change, p);
if (approvals != null) {
- addApprovals(psa, p.getId(), approvals, labelTypes);
+ addApprovals(psa, p.id(), approvals, labelTypes);
}
ca.patchSets.add(psa);
if (includeFiles) {
@@ -463,12 +462,12 @@ public class EventFactory {
*/
public PatchSetAttribute asPatchSetAttribute(RevWalk revWalk, Change change, PatchSet patchSet) {
PatchSetAttribute p = new PatchSetAttribute();
- p.revision = patchSet.getRevision().get();
- p.number = patchSet.getPatchSetId();
- p.ref = patchSet.getRefName();
- p.uploader = asAccountAttribute(patchSet.getUploader());
- p.createdOn = patchSet.getCreatedOn().getTime() / 1000L;
- PatchSet.Id pId = patchSet.getId();
+ p.revision = patchSet.commitId().name();
+ p.number = patchSet.number();
+ p.ref = patchSet.refName();
+ p.uploader = asAccountAttribute(patchSet.uploader());
+ p.createdOn = patchSet.createdOn().getTime() / 1000L;
+ PatchSet.Id pId = patchSet.id();
try {
p.parents = new ArrayList<>();
RevCommit c = revWalk.parseCommit(ObjectId.fromString(p.revision));
@@ -476,7 +475,7 @@ public class EventFactory {
p.parents.add(parent.name());
}
- UserIdentity author = toUserIdentity(c.getAuthorIdent());
+ UserIdentity author = emails.toUserIdentity(c.getAuthorIdent());
if (author.getAccount() == null) {
p.author = new AccountAttribute();
p.author.email = author.getEmail();
@@ -495,7 +494,7 @@ public class EventFactory {
}
p.kind = changeKindCache.getChangeKind(change, patchSet);
} catch (IOException | StorageException e) {
- logger.atSevere().withCause(e).log("Cannot load patch set data for %s", patchSet.getId());
+ logger.atSevere().withCause(e).log("Cannot load patch set data for %s", patchSet.id());
} catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Cannot get size information for %s: %s", pId, e.getMessage());
} catch (PatchListNotAvailableException e) {
@@ -504,26 +503,6 @@ public class EventFactory {
return p;
}
- // TODO: The same method exists in PatchSetInfoFactory, find a common place
- // for it
- private UserIdentity toUserIdentity(PersonIdent who) throws IOException {
- UserIdentity u = new UserIdentity();
- u.setName(who.getName());
- u.setEmail(who.getEmailAddress());
- u.setDate(new Timestamp(who.getWhen().getTime()));
- u.setTimeZone(who.getTimeZoneOffset());
-
- // If only one account has access to this email address, select it
- // as the identity of the user.
- //
- Set<Account.Id> a = emails.getAccountFor(u.getEmail());
- if (a.size() == 1) {
- u.setAccount(a.iterator().next());
- }
-
- return u;
- }
-
public void addApprovals(
PatchSetAttribute p,
PatchSet.Id id,
@@ -540,7 +519,7 @@ public class EventFactory {
if (!list.isEmpty()) {
p.approvals = new ArrayList<>(list.size());
for (PatchSetApproval a : list) {
- if (a.getValue() != 0) {
+ if (a.value() != 0) {
p.approvals.add(asApprovalAttribute(a, labelTypes));
}
}
@@ -571,9 +550,9 @@ public class EventFactory {
*/
public AccountAttribute asAccountAttribute(AccountState accountState) {
AccountAttribute who = new AccountAttribute();
- who.name = accountState.getAccount().getFullName();
- who.email = accountState.getAccount().getPreferredEmail();
- who.username = accountState.getUserName().orElse(null);
+ who.name = accountState.account().fullName();
+ who.email = accountState.account().preferredEmail();
+ who.username = accountState.userName().orElse(null);
return who;
}
@@ -599,13 +578,13 @@ public class EventFactory {
*/
public ApprovalAttribute asApprovalAttribute(PatchSetApproval approval, LabelTypes labelTypes) {
ApprovalAttribute a = new ApprovalAttribute();
- a.type = approval.getLabelId().get();
- a.value = Short.toString(approval.getValue());
- a.by = asAccountAttribute(approval.getAccountId());
- a.grantedOn = approval.getGranted().getTime() / 1000L;
+ a.type = approval.labelId().get();
+ a.value = Short.toString(approval.value());
+ a.by = asAccountAttribute(approval.accountId());
+ a.grantedOn = approval.granted().getTime() / 1000L;
a.oldValue = null;
- LabelType lt = labelTypes.byLabel(approval.getLabelId());
+ LabelType lt = labelTypes.byLabel(approval.labelId());
if (lt != null) {
a.description = lt.getName();
}
diff --git a/java/com/google/gerrit/server/events/EventGson.java b/java/com/google/gerrit/server/events/EventGson.java
new file mode 100644
index 0000000000..87b45f6581
--- /dev/null
+++ b/java/com/google/gerrit/server/events/EventGson.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@BindingAnnotation
+@Retention(RUNTIME)
+@Target({PARAMETER, FIELD})
+public @interface EventGson {}
diff --git a/java/com/google/gerrit/server/events/EventGsonProvider.java b/java/com/google/gerrit/server/events/EventGsonProvider.java
new file mode 100644
index 0000000000..688507bfe3
--- /dev/null
+++ b/java/com/google/gerrit/server/events/EventGsonProvider.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+import com.google.common.base.Supplier;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.change.ChangeKeyAdapter;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.inject.Provider;
+
+public class EventGsonProvider implements Provider<Gson> {
+ @Override
+ public Gson get() {
+ return new GsonBuilder()
+ .registerTypeAdapter(Event.class, new EventDeserializer())
+ .registerTypeAdapter(Supplier.class, new SupplierSerializer())
+ .registerTypeAdapter(Supplier.class, new SupplierDeserializer())
+ .registerTypeAdapter(Change.Key.class, new ChangeKeyAdapter())
+ .registerTypeAdapter(Project.NameKey.class, new ProjectNameKeyAdapter())
+ .create();
+ }
+}
diff --git a/java/com/google/gerrit/server/events/EventsMetrics.java b/java/com/google/gerrit/server/events/EventsMetrics.java
index f73d6de081..3c87cca27b 100644
--- a/java/com/google/gerrit/server/events/EventsMetrics.java
+++ b/java/com/google/gerrit/server/events/EventsMetrics.java
@@ -18,6 +18,7 @@ import com.google.gerrit.metrics.Counter1;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.logging.Metadata;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -31,7 +32,7 @@ public class EventsMetrics implements EventListener {
metricMaker.newCounter(
"events",
new Description("Triggered events").setRate().setUnit("triggered events"),
- Field.ofString("type"));
+ Field.ofString("type", Metadata.Builder::eventType).build());
}
@Override
diff --git a/java/com/google/gerrit/server/events/HashtagsChangedEvent.java b/java/com/google/gerrit/server/events/HashtagsChangedEvent.java
index 1de0fc3c19..d9e0445ecf 100644
--- a/java/com/google/gerrit/server/events/HashtagsChangedEvent.java
+++ b/java/com/google/gerrit/server/events/HashtagsChangedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class HashtagsChangedEvent extends ChangeEvent {
diff --git a/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java b/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java
index 8cea85602b..24f4709013 100644
--- a/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java
+++ b/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class PatchSetCreatedEvent extends PatchSetEvent {
diff --git a/java/com/google/gerrit/server/events/PatchSetEvent.java b/java/com/google/gerrit/server/events/PatchSetEvent.java
index f9dde66d94..c8e45910ed 100644
--- a/java/com/google/gerrit/server/events/PatchSetEvent.java
+++ b/java/com/google/gerrit/server/events/PatchSetEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.PatchSetAttribute;
public class PatchSetEvent extends ChangeEvent {
diff --git a/java/com/google/gerrit/server/events/PrivateStateChangedEvent.java b/java/com/google/gerrit/server/events/PrivateStateChangedEvent.java
index d03eda4655..2e6fe835ea 100644
--- a/java/com/google/gerrit/server/events/PrivateStateChangedEvent.java
+++ b/java/com/google/gerrit/server/events/PrivateStateChangedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class PrivateStateChangedEvent extends PatchSetEvent {
diff --git a/java/com/google/gerrit/server/events/ProjectCreatedEvent.java b/java/com/google/gerrit/server/events/ProjectCreatedEvent.java
index dc979cae1e..d092b3934b 100644
--- a/java/com/google/gerrit/server/events/ProjectCreatedEvent.java
+++ b/java/com/google/gerrit/server/events/ProjectCreatedEvent.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.events;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
public class ProjectCreatedEvent extends ProjectEvent {
static final String TYPE = "project-created";
@@ -27,7 +27,7 @@ public class ProjectCreatedEvent extends ProjectEvent {
@Override
public Project.NameKey getProjectNameKey() {
- return new Project.NameKey(projectName);
+ return Project.nameKey(projectName);
}
public String getHeadName() {
diff --git a/java/com/google/gerrit/server/events/ProjectEvent.java b/java/com/google/gerrit/server/events/ProjectEvent.java
index cba8e90ffc..77085e6182 100644
--- a/java/com/google/gerrit/server/events/ProjectEvent.java
+++ b/java/com/google/gerrit/server/events/ProjectEvent.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.events;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
public abstract class ProjectEvent extends Event {
protected ProjectEvent(String type) {
diff --git a/java/com/google/gerrit/server/events/ProjectNameKeySerializer.java b/java/com/google/gerrit/server/events/ProjectNameKeyAdapter.java
index 743b3145d8..eeaa2386b3 100644
--- a/java/com/google/gerrit/server/events/ProjectNameKeySerializer.java
+++ b/java/com/google/gerrit/server/events/ProjectNameKeyAdapter.java
@@ -14,17 +14,31 @@
package com.google.gerrit.server.events;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
-public class ProjectNameKeySerializer implements JsonSerializer<Project.NameKey> {
+public class ProjectNameKeyAdapter
+ implements JsonSerializer<Project.NameKey>, JsonDeserializer<Project.NameKey> {
@Override
public JsonElement serialize(
Project.NameKey project, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(project.get());
}
+
+ @Override
+ public Project.NameKey deserialize(
+ JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ if (!json.isJsonPrimitive() || !json.getAsJsonPrimitive().isString()) {
+ throw new JsonParseException("Key is not a string: " + json);
+ }
+ return Project.nameKey(json.getAsString());
+ }
}
diff --git a/java/com/google/gerrit/server/events/RefEvent.java b/java/com/google/gerrit/server/events/RefEvent.java
index 951940f0ec..7a6bf6f330 100644
--- a/java/com/google/gerrit/server/events/RefEvent.java
+++ b/java/com/google/gerrit/server/events/RefEvent.java
@@ -14,15 +14,15 @@
package com.google.gerrit.server.events;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.entities.BranchNameKey;
public abstract class RefEvent extends ProjectEvent {
protected RefEvent(String type) {
super(type);
}
- public Branch.NameKey getBranchNameKey() {
- return new Branch.NameKey(getProjectNameKey(), getRefName());
+ public BranchNameKey getBranchNameKey() {
+ return BranchNameKey.create(getProjectNameKey(), getRefName());
}
public abstract String getRefName();
diff --git a/java/com/google/gerrit/server/events/RefReceivedEvent.java b/java/com/google/gerrit/server/events/RefReceivedEvent.java
index aa02d11b9f..18783aaca5 100644
--- a/java/com/google/gerrit/server/events/RefReceivedEvent.java
+++ b/java/com/google/gerrit/server/events/RefReceivedEvent.java
@@ -13,7 +13,7 @@
// limitations under the License.
package com.google.gerrit.server.events;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.IdentifiedUser;
import org.eclipse.jgit.transport.ReceiveCommand;
diff --git a/java/com/google/gerrit/server/events/RefUpdatedEvent.java b/java/com/google/gerrit/server/events/RefUpdatedEvent.java
index d74054344e..1ca63924e8 100644
--- a/java/com/google/gerrit/server/events/RefUpdatedEvent.java
+++ b/java/com/google/gerrit/server/events/RefUpdatedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
@@ -30,7 +30,7 @@ public class RefUpdatedEvent extends RefEvent {
@Override
public Project.NameKey getProjectNameKey() {
- return new Project.NameKey(refUpdate.get().project);
+ return Project.nameKey(refUpdate.get().project);
}
@Override
diff --git a/java/com/google/gerrit/server/events/ReviewerAddedEvent.java b/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
index ea6bda3e74..5d2b7a51ed 100644
--- a/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
+++ b/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class ReviewerAddedEvent extends PatchSetEvent {
diff --git a/java/com/google/gerrit/server/events/ReviewerDeletedEvent.java b/java/com/google/gerrit/server/events/ReviewerDeletedEvent.java
index 02f9d438c2..2b0b6ac368 100644
--- a/java/com/google/gerrit/server/events/ReviewerDeletedEvent.java
+++ b/java/com/google/gerrit/server/events/ReviewerDeletedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
diff --git a/java/com/google/gerrit/server/events/StreamEventsApiListener.java b/java/com/google/gerrit/server/events/StreamEventsApiListener.java
index f6f1d57850..18b6a5e453 100644
--- a/java/com/google/gerrit/server/events/StreamEventsApiListener.java
+++ b/java/com/google/gerrit/server/events/StreamEventsApiListener.java
@@ -20,6 +20,10 @@ import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ApprovalInfo;
@@ -42,10 +46,6 @@ import com.google.gerrit.extensions.events.TopicEditedListener;
import com.google.gerrit.extensions.events.VoteDeletedListener;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
@@ -137,7 +137,7 @@ public class StreamEventsApiListener
private ChangeNotes getNotes(ChangeInfo info) {
try {
- return changeNotesFactory.createChecked(new Change.Id(info._number));
+ return changeNotesFactory.createChecked(Change.id(info._number));
} catch (NoSuchChangeException e) {
throw new StorageException(e);
}
@@ -162,7 +162,7 @@ public class StreamEventsApiListener
return Suppliers.memoize(
() ->
account != null
- ? eventFactory.asAccountAttribute(new Account.Id(account._accountId))
+ ? eventFactory.asAccountAttribute(Account.id(account._accountId))
: null);
}
@@ -361,7 +361,7 @@ public class StreamEventsApiListener
if (ev.getUpdater() != null) {
event.submitter = accountAttributeSupplier(ev.getUpdater());
}
- final Branch.NameKey refName = new Branch.NameKey(ev.getProjectName(), ev.getRefName());
+ final BranchNameKey refName = BranchNameKey.create(ev.getProjectName(), ev.getRefName());
event.refUpdate =
Suppliers.memoize(
() ->
diff --git a/java/com/google/gerrit/server/events/TopicChangedEvent.java b/java/com/google/gerrit/server/events/TopicChangedEvent.java
index 0b6ecc5c96..b9cce6643c 100644
--- a/java/com/google/gerrit/server/events/TopicChangedEvent.java
+++ b/java/com/google/gerrit/server/events/TopicChangedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class TopicChangedEvent extends ChangeEvent {
diff --git a/java/com/google/gerrit/server/events/VoteDeletedEvent.java b/java/com/google/gerrit/server/events/VoteDeletedEvent.java
index 87c4c055e3..180b4a8f3c 100644
--- a/java/com/google/gerrit/server/events/VoteDeletedEvent.java
+++ b/java/com/google/gerrit/server/events/VoteDeletedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
diff --git a/java/com/google/gerrit/server/events/WorkInProgressStateChangedEvent.java b/java/com/google/gerrit/server/events/WorkInProgressStateChangedEvent.java
index 5e52c7b337..74f997b2c7 100644
--- a/java/com/google/gerrit/server/events/WorkInProgressStateChangedEvent.java
+++ b/java/com/google/gerrit/server/events/WorkInProgressStateChangedEvent.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.events;
import com.google.common.base.Supplier;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class WorkInProgressStateChangedEvent extends PatchSetEvent {
diff --git a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
index fdce1da8f6..a76b69bdbb 100644
--- a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.AssigneeChangedListener;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
index a8c08b991f..b27ffb9511 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
@@ -15,14 +15,14 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ChangeAbandonedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java b/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
index 9e3e979196..df71f27675 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.ChangeDeletedListener;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
index 756f3832ff..add1c51dda 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
@@ -15,14 +15,14 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ChangeMergedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
index e8bed56854..fa91dbd380 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
@@ -15,14 +15,14 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ChangeRestoredListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
index ccb17d5cfd..5fb004f65e 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
@@ -15,11 +15,11 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.ChangeRevertedListener;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/extensions/events/CommentAdded.java b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
index ea9ae312a4..e45b206953 100644
--- a/java/com/google/gerrit/server/extensions/events/CommentAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
@@ -22,8 +24,6 @@ import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.CommentAddedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/events/EventUtil.java b/java/com/google/gerrit/server/extensions/events/EventUtil.java
index d00eb31721..3dcf3b8228 100644
--- a/java/com/google/gerrit/server/extensions/events/EventUtil.java
+++ b/java/com/google/gerrit/server/extensions/events/EventUtil.java
@@ -16,19 +16,21 @@ package com.google.gerrit.server.extensions.events;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.RevisionJson;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
@@ -39,42 +41,51 @@ import java.sql.Timestamp;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
+import org.eclipse.jgit.lib.Config;
+/**
+ * Formats change and revision info objects to serve as payload for Gerrit events.
+ *
+ * <p>Uses configurable options ({@code event.payload.listChangeOptions}) to decide which fields to
+ * populate.
+ */
@Singleton
public class EventUtil {
- private static final ImmutableSet<ListChangesOption> CHANGE_OPTIONS;
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private static final ImmutableSet<ListChangesOption> DEFAULT_CHANGE_OPTIONS;
static {
EnumSet<ListChangesOption> opts = EnumSet.allOf(ListChangesOption.class);
-
// Some options, like actions, are expensive to compute because they potentially have to walk
// lots of history and inspect lots of other changes.
opts.remove(ListChangesOption.CHANGE_ACTIONS);
opts.remove(ListChangesOption.CURRENT_ACTIONS);
-
// CHECK suppresses some exceptions on corrupt changes, which is not appropriate for passing
// through the event system as we would rather let them propagate.
opts.remove(ListChangesOption.CHECK);
-
- CHANGE_OPTIONS = Sets.immutableEnumSet(opts);
+ DEFAULT_CHANGE_OPTIONS = Sets.immutableEnumSet(opts);
}
private final ChangeData.Factory changeDataFactory;
private final ChangeJson.Factory changeJsonFactory;
private final RevisionJson.Factory revisionJsonFactory;
+ private final ImmutableSet<ListChangesOption> changeOptions;
@Inject
EventUtil(
ChangeJson.Factory changeJsonFactory,
RevisionJson.Factory revisionJsonFactory,
- ChangeData.Factory changeDataFactory) {
+ ChangeData.Factory changeDataFactory,
+ @GerritServerConfig Config gerritConfig) {
this.changeDataFactory = changeDataFactory;
this.changeJsonFactory = changeJsonFactory;
this.revisionJsonFactory = revisionJsonFactory;
+ this.changeOptions = parseChangeListOptions(gerritConfig);
}
public ChangeInfo changeInfo(Change change) {
- return changeJsonFactory.create(CHANGE_OPTIONS).format(change);
+ return changeJsonFactory.create(changeOptions).format(change);
}
public RevisionInfo revisionInfo(Project project, PatchSet ps)
@@ -84,19 +95,19 @@ public class EventUtil {
public RevisionInfo revisionInfo(Project.NameKey project, PatchSet ps)
throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
- ChangeData cd = changeDataFactory.create(project, ps.getId().getParentKey());
- return revisionJsonFactory.create(CHANGE_OPTIONS).getRevisionInfo(cd, ps);
+ ChangeData cd = changeDataFactory.create(project, ps.id().changeId());
+ return revisionJsonFactory.create(changeOptions).getRevisionInfo(cd, ps);
}
public AccountInfo accountInfo(AccountState accountState) {
- if (accountState == null || accountState.getAccount().getId() == null) {
+ if (accountState == null || accountState.account().id() == null) {
return null;
}
- Account account = accountState.getAccount();
- AccountInfo accountInfo = new AccountInfo(account.getId().get());
- accountInfo.email = account.getPreferredEmail();
- accountInfo.name = account.getFullName();
- accountInfo.username = accountState.getUserName().orElse(null);
+ Account account = accountState.account();
+ AccountInfo accountInfo = new AccountInfo(account.id().get());
+ accountInfo.email = account.preferredEmail();
+ accountInfo.name = account.fullName();
+ accountInfo.username = accountState.userName().orElse(null);
return accountInfo;
}
@@ -106,9 +117,25 @@ public class EventUtil {
for (Map.Entry<String, Short> e : approvals.entrySet()) {
Integer value = e.getValue() != null ? Integer.valueOf(e.getValue()) : null;
result.put(
- e.getKey(),
- new ApprovalInfo(accountState.getAccount().getId().get(), value, null, null, ts));
+ e.getKey(), new ApprovalInfo(accountState.account().id().get(), value, null, null, ts));
}
return result;
}
+
+ private static ImmutableSet<ListChangesOption> parseChangeListOptions(Config gerritConfig) {
+ String[] config = gerritConfig.getStringList("event", "payload", "listChangeOptions");
+ if (config.length == 0) {
+ return DEFAULT_CHANGE_OPTIONS;
+ }
+
+ ImmutableSet.Builder<ListChangesOption> result = ImmutableSet.builder();
+ for (String c : config) {
+ try {
+ result.add(ListChangesOption.valueOf(c));
+ } catch (IllegalArgumentException e) {
+ logger.atWarning().withCause(e).log("could not parse list change option %s", c);
+ }
+ }
+ return result.build();
+ }
}
diff --git a/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java b/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
index bae17e7b8d..99f105ec9b 100644
--- a/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
+++ b/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.extensions.events;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
index 65f5b8bfa2..df60ec08cc 100644
--- a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
@@ -16,12 +16,12 @@ package com.google.gerrit.server.extensions.events;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.HashtagsEditedListener;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
index 49a609124e..72adff7670 100644
--- a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
@@ -15,14 +15,14 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.PrivateStateChangedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
index f9f67f6c4b..1af428cb8c 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
@@ -16,14 +16,14 @@ package com.google.gerrit.server.extensions.events;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ReviewerAddedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
index b92f3e6937..61632f2be7 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
@@ -22,8 +24,6 @@ import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.ReviewerDeletedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
index 6fddcfe4ad..bdfa8c157f 100644
--- a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
+++ b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
@@ -15,14 +15,14 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.RevisionCreatedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.change.NotifyResolver;
diff --git a/java/com/google/gerrit/server/extensions/events/TopicEdited.java b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
index 9e1ae44560..cb982a1c8b 100644
--- a/java/com/google/gerrit/server/extensions/events/TopicEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.TopicEditedListener;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
index bd6873a65c..8533a650df 100644
--- a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
@@ -22,8 +24,6 @@ import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.VoteDeletedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
index 785d6feae8..16c5e25e3b 100644
--- a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
@@ -15,14 +15,14 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
diff --git a/java/com/google/gerrit/server/extensions/webui/UiActions.java b/java/com/google/gerrit/server/extensions/webui/UiActions.java
index 3ca2bdb93d..0bc3d5c9ac 100644
--- a/java/com/google/gerrit/server/extensions/webui/UiActions.java
+++ b/java/com/google/gerrit/server/extensions/webui/UiActions.java
@@ -19,7 +19,6 @@ import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
import static java.util.stream.Collectors.toList;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Predicate;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
@@ -38,6 +37,7 @@ import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendCondition;
@@ -50,6 +50,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Predicate;
@Singleton
public class UiActions {
@@ -71,7 +72,7 @@ public class UiActions {
new com.google.gerrit.metrics.Description("Latency for RestView#getDescription calls")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- Field.ofString("view"));
+ Field.ofString("view", Metadata.Builder::restViewName).build());
}
public <R extends RestResource> Iterable<UiAction.Description> from(
@@ -143,7 +144,7 @@ public class UiActions {
String name = e.getExportName().substring(d + 1);
UiAction.Description dsc;
- try (Timer1.Context ignored = uiActionLatency.start(name)) {
+ try (Timer1.Context<String> ignored = uiActionLatency.start(name)) {
dsc = ((UiAction<R>) view).getDescription(resource);
}
diff --git a/java/com/google/gerrit/server/fixes/FixReplacementInterpreter.java b/java/com/google/gerrit/server/fixes/FixReplacementInterpreter.java
index 65f14db27a..a1682fe4be 100644
--- a/java/com/google/gerrit/server/fixes/FixReplacementInterpreter.java
+++ b/java/com/google/gerrit/server/fixes/FixReplacementInterpreter.java
@@ -18,11 +18,11 @@ import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.groupingBy;
import com.google.gerrit.common.RawInputUtil;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.FixReplacement;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.FixReplacement;
import com.google.gerrit.server.change.FileContentUtil;
import com.google.gerrit.server.edit.tree.ChangeFileContentModification;
import com.google.gerrit.server.edit.tree.TreeModification;
diff --git a/java/com/google/gerrit/server/git/BanCommit.java b/java/com/google/gerrit/server/git/BanCommit.java
index d8aeeceb86..4473ab710b 100644
--- a/java/com/google/gerrit/server/git/BanCommit.java
+++ b/java/com/google/gerrit/server/git/BanCommit.java
@@ -14,13 +14,13 @@
package com.google.gerrit.server.git;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_REJECT_COMMITS;
+import static com.google.gerrit.entities.RefNames.REFS_REJECT_COMMITS;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.git.LockFailureException;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/git/BranchOrderSection.java b/java/com/google/gerrit/server/git/BranchOrderSection.java
index d4b537e268..4c77b61122 100644
--- a/java/com/google/gerrit/server/git/BranchOrderSection.java
+++ b/java/com/google/gerrit/server/git/BranchOrderSection.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.git;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.RefNames;
import java.util.List;
public class BranchOrderSection {
diff --git a/java/com/google/gerrit/server/git/ChangeMessageModifier.java b/java/com/google/gerrit/server/git/ChangeMessageModifier.java
index 7d4edcf317..580c0b9cce 100644
--- a/java/com/google/gerrit/server/git/ChangeMessageModifier.java
+++ b/java/com/google/gerrit/server/git/ChangeMessageModifier.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.git;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Branch;
import org.eclipse.jgit.revwalk.RevCommit;
/**
@@ -47,5 +47,5 @@ public interface ChangeMessageModifier {
* @return a new not null commit message.
*/
String onSubmit(
- String newCommitMessage, RevCommit original, RevCommit mergeTip, Branch.NameKey destination);
+ String newCommitMessage, RevCommit original, RevCommit mergeTip, BranchNameKey destination);
}
diff --git a/java/com/google/gerrit/server/git/ChangeReportFormatter.java b/java/com/google/gerrit/server/git/ChangeReportFormatter.java
index 3ce6b2b085..f897a1dedf 100644
--- a/java/com/google/gerrit/server/git/ChangeReportFormatter.java
+++ b/java/com/google/gerrit/server/git/ChangeReportFormatter.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.git;
import com.google.auto.value.AutoValue;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
public interface ChangeReportFormatter {
@AutoValue
diff --git a/java/com/google/gerrit/server/git/CodeReviewCommit.java b/java/com/google/gerrit/server/git/CodeReviewCommit.java
index c210dcdaaa..d7538ba3fb 100644
--- a/java/com/google/gerrit/server/git/CodeReviewCommit.java
+++ b/java/com/google/gerrit/server/git/CodeReviewCommit.java
@@ -19,8 +19,8 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.submit.CommitMergeStatus;
import java.io.IOException;
@@ -48,7 +48,7 @@ public class CodeReviewCommit extends RevCommit {
Ordering.natural()
.onResultOf(
(CodeReviewCommit c) ->
- c.getPatchsetId() != null ? c.getPatchsetId().getParentKey().get() : null)
+ c.getPatchsetId() != null ? c.getPatchsetId().changeId().get() : null)
.nullsFirst();
public static CodeReviewRevWalk newRevWalk(Repository repo) {
diff --git a/java/com/google/gerrit/server/git/CommitUtil.java b/java/com/google/gerrit/server/git/CommitUtil.java
index b0f10f2bc8..476037bd1a 100644
--- a/java/com/google/gerrit/server/git/CommitUtil.java
+++ b/java/com/google/gerrit/server/git/CommitUtil.java
@@ -14,16 +14,49 @@
package com.google.gerrit.server.git;
+import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.server.CommonConverters;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.change.ChangeMessages;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
import java.io.IOException;
+import java.sql.Timestamp;
+import java.text.MessageFormat;
import java.util.ArrayList;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.ChangeIdUtil;
/** Static utilities for working with {@link RevCommit}s. */
+@Singleton
public class CommitUtil {
+ private final GitRepositoryManager repoManager;
+ private final Provider<PersonIdent> serverIdent;
+
+ @Inject
+ CommitUtil(
+ GitRepositoryManager repoManager, @GerritPersonIdent Provider<PersonIdent> serverIdent) {
+ this.repoManager = repoManager;
+ this.serverIdent = serverIdent;
+ }
+
public static CommitInfo toCommitInfo(RevCommit commit) throws IOException {
return toCommitInfo(commit, null);
}
@@ -47,5 +80,84 @@ public class CommitUtil {
return info;
}
- private CommitUtil() {}
+ /**
+ * Allows creating a revert commit.
+ *
+ * @param message Commit message for the revert commit.
+ * @param notes ChangeNotes of the change being reverted.
+ * @param user Current User performing the revert.
+ * @return ObjectId that represents the newly created commit.
+ * @throws ResourceConflictException Can't revert the initial commit.
+ * @throws IOException Thrown in case of I/O errors.
+ */
+ public ObjectId createRevertCommit(String message, ChangeNotes notes, CurrentUser user)
+ throws ResourceConflictException, IOException {
+ message = Strings.emptyToNull(message);
+
+ Project.NameKey project = notes.getProjectName();
+ try (Repository git = repoManager.openRepository(project);
+ ObjectInserter oi = git.newObjectInserter();
+ ObjectReader reader = oi.newReader();
+ RevWalk revWalk = new RevWalk(reader)) {
+ return createRevertCommit(message, notes, user, null, TimeUtil.nowTs(), oi, revWalk);
+ }
+ }
+
+ /**
+ * @param message Commit message for the revert commit.
+ * @param notes ChangeNotes of the change being reverted.
+ * @param user Current User performing the revert.
+ * @param generatedChangeId The changeId for the commit message, can be null since it is not
+ * needed for commits, only for changes.
+ * @param ts Timestamp of creation for the commit.
+ * @param oi ObjectInserter for inserting the newly created commit.
+ * @param revWalk Used for parsing the original commit.
+ * @return ObjectId that represents the newly created commit.
+ * @throws ResourceConflictException Can't revert the initial commit.
+ * @throws IOException Thrown in case of I/O errors.
+ */
+ public ObjectId createRevertCommit(
+ String message,
+ ChangeNotes notes,
+ CurrentUser user,
+ @Nullable ObjectId generatedChangeId,
+ Timestamp ts,
+ ObjectInserter oi,
+ RevWalk revWalk)
+ throws ResourceConflictException, IOException {
+
+ PatchSet patch = notes.getCurrentPatchSet();
+ RevCommit commitToRevert = revWalk.parseCommit(patch.commitId());
+ if (commitToRevert.getParentCount() == 0) {
+ throw new ResourceConflictException("Cannot revert initial commit");
+ }
+
+ PersonIdent committerIdent = serverIdent.get();
+ PersonIdent authorIdent =
+ user.asIdentifiedUser().newCommitterIdent(ts, committerIdent.getTimeZone());
+
+ RevCommit parentToCommitToRevert = commitToRevert.getParent(0);
+ revWalk.parseHeaders(parentToCommitToRevert);
+
+ CommitBuilder revertCommitBuilder = new CommitBuilder();
+ revertCommitBuilder.addParentId(commitToRevert);
+ revertCommitBuilder.setTreeId(parentToCommitToRevert.getTree());
+ revertCommitBuilder.setAuthor(authorIdent);
+ revertCommitBuilder.setCommitter(authorIdent);
+
+ Change changeToRevert = notes.getChange();
+ if (message == null) {
+ message =
+ MessageFormat.format(
+ ChangeMessages.get().revertChangeDefaultMessage,
+ changeToRevert.getSubject(),
+ patch.commitId().name());
+ }
+ if (generatedChangeId != null) {
+ revertCommitBuilder.setMessage(ChangeIdUtil.insertId(message, generatedChangeId, true));
+ }
+ ObjectId id = oi.insert(revertCommitBuilder);
+ oi.flush();
+ return id;
+ }
}
diff --git a/java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java b/java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java
deleted file mode 100644
index a8594e7622..0000000000
--- a/java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.git;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackendException;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.AbstractAdvertiseRefsHook;
-import org.eclipse.jgit.transport.ServiceMayNotContinueException;
-
-/**
- * Wrapper around {@link com.google.gerrit.server.permissions.PermissionBackend.ForProject} that
- * implements {@link org.eclipse.jgit.transport.AdvertiseRefsHook}.
- */
-public class DefaultAdvertiseRefsHook extends AbstractAdvertiseRefsHook {
- private final PermissionBackend.ForProject perm;
- private final PermissionBackend.RefFilterOptions opts;
-
- public DefaultAdvertiseRefsHook(
- PermissionBackend.ForProject perm, PermissionBackend.RefFilterOptions opts) {
- this.perm = perm;
- this.opts = opts;
- }
-
- @Override
- protected Map<String, Ref> getAdvertisedRefs(Repository repo, RevWalk revWalk)
- throws ServiceMayNotContinueException {
- try {
- List<String> prefixes =
- !opts.prefixes().isEmpty() ? opts.prefixes() : ImmutableList.of(RefDatabase.ALL);
- return perm.filter(
- repo.getRefDatabase().getRefsByPrefix(prefixes.toArray(new String[0])), repo, opts);
- } catch (IOException | PermissionBackendException e) {
- ServiceMayNotContinueException ex = new ServiceMayNotContinueException();
- ex.initCause(e);
- throw ex;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java b/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
index 5b9dffce4e..5866c578f4 100644
--- a/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
+++ b/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.git;
import static com.google.common.base.Preconditions.checkState;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.config.UrlFormatter;
import com.google.inject.Inject;
import java.util.Optional;
diff --git a/java/com/google/gerrit/server/git/DelegateRefDatabase.java b/java/com/google/gerrit/server/git/DelegateRefDatabase.java
new file mode 100644
index 0000000000..34dd6a9a67
--- /dev/null
+++ b/java/com/google/gerrit/server/git/DelegateRefDatabase.java
@@ -0,0 +1,86 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Wrapper around {@link RefDatabase} that delegates all calls to the wrapped {@link RefDatabase}.
+ */
+public class DelegateRefDatabase extends RefDatabase {
+
+ private Repository delegate;
+
+ DelegateRefDatabase(Repository delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void create() throws IOException {
+ delegate.getRefDatabase().create();
+ }
+
+ @Override
+ public void close() {
+ delegate.close();
+ }
+
+ @Override
+ public boolean isNameConflicting(String name) throws IOException {
+ return delegate.getRefDatabase().isNameConflicting(name);
+ }
+
+ @Override
+ public RefUpdate newUpdate(String name, boolean detach) throws IOException {
+ return delegate.getRefDatabase().newUpdate(name, detach);
+ }
+
+ @Override
+ public RefRename newRename(String fromName, String toName) throws IOException {
+ return delegate.getRefDatabase().newRename(fromName, toName);
+ }
+
+ @Override
+ public Ref exactRef(String name) throws IOException {
+ return delegate.getRefDatabase().exactRef(name);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public Map<String, Ref> getRefs(String prefix) throws IOException {
+ return delegate.getRefDatabase().getRefs(prefix);
+ }
+
+ @Override
+ public List<Ref> getAdditionalRefs() throws IOException {
+ return delegate.getRefDatabase().getAdditionalRefs();
+ }
+
+ @Override
+ public Ref peel(Ref ref) throws IOException {
+ return delegate.getRefDatabase().peel(ref);
+ }
+
+ Repository getDelegate() {
+ return delegate;
+ }
+}
diff --git a/java/com/google/gerrit/server/git/DelegateRepository.java b/java/com/google/gerrit/server/git/DelegateRepository.java
new file mode 100644
index 0000000000..800490d4c4
--- /dev/null
+++ b/java/com/google/gerrit/server/git/DelegateRepository.java
@@ -0,0 +1,89 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import java.io.IOException;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
+import org.eclipse.jgit.lib.BaseRepositoryBuilder;
+import org.eclipse.jgit.lib.ObjectDatabase;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.ReflogReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+
+/** Wrapper around {@link Repository} that delegates all calls to the wrapped {@link Repository}. */
+class DelegateRepository extends Repository {
+
+ private final Repository delegate;
+
+ DelegateRepository(Repository delegate) {
+ super(toBuilder(delegate));
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void create(boolean bare) throws IOException {
+ delegate.create(bare);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return delegate.getIdentifier();
+ }
+
+ @Override
+ public ObjectDatabase getObjectDatabase() {
+ return delegate.getObjectDatabase();
+ }
+
+ @Override
+ public RefDatabase getRefDatabase() {
+ return delegate.getRefDatabase();
+ }
+
+ @Override
+ public StoredConfig getConfig() {
+ return delegate.getConfig();
+ }
+
+ @Override
+ public AttributesNodeProvider createAttributesNodeProvider() {
+ return delegate.createAttributesNodeProvider();
+ }
+
+ @Override
+ public void scanForRepoChanges() throws IOException {
+ delegate.scanForRepoChanges();
+ }
+
+ @Override
+ public void notifyIndexChanged(boolean internal) {
+ delegate.notifyIndexChanged(internal);
+ }
+
+ @Override
+ public ReflogReader getReflogReader(String refName) throws IOException {
+ return delegate.getReflogReader(refName);
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static BaseRepositoryBuilder toBuilder(Repository repo) {
+ if (!repo.isBare()) {
+ throw new IllegalArgumentException("non-bare repository is not supported");
+ }
+
+ return new BaseRepositoryBuilder<>().setFS(repo.getFS()).setGitDir(repo.getDirectory());
+ }
+}
diff --git a/java/com/google/gerrit/server/git/GarbageCollection.java b/java/com/google/gerrit/server/git/GarbageCollection.java
index 75c90126ef..090d4397aa 100644
--- a/java/com/google/gerrit/server/git/GarbageCollection.java
+++ b/java/com/google/gerrit/server/git/GarbageCollection.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.git;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GarbageCollectionResult;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.events.GarbageCollectorListener;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GcConfig;
import com.google.gerrit.server.extensions.events.AbstractNoNotifyEvent;
import com.google.gerrit.server.plugincontext.PluginSetContext;
diff --git a/java/com/google/gerrit/server/git/GarbageCollectionQueue.java b/java/com/google/gerrit/server/git/GarbageCollectionQueue.java
index 31cd880d1e..e3a923b5c2 100644
--- a/java/com/google/gerrit/server/git/GarbageCollectionQueue.java
+++ b/java/com/google/gerrit/server/git/GarbageCollectionQueue.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.git;
import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.inject.Singleton;
import java.util.Collection;
import java.util.HashSet;
diff --git a/java/com/google/gerrit/server/git/GitRepositoryManager.java b/java/com/google/gerrit/server/git/GitRepositoryManager.java
index f8d8a49062..2697eee213 100644
--- a/java/com/google/gerrit/server/git/GitRepositoryManager.java
+++ b/java/com/google/gerrit/server/git/GitRepositoryManager.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.git;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.inject.ImplementedBy;
import com.google.inject.Singleton;
import java.io.IOException;
diff --git a/java/com/google/gerrit/server/git/GroupCollector.java b/java/com/google/gerrit/server/git/GroupCollector.java
index e4fd803b27..dd3919870b 100644
--- a/java/com/google/gerrit/server/git/GroupCollector.java
+++ b/java/com/google/gerrit/server/git/GroupCollector.java
@@ -28,8 +28,8 @@ import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.SortedSetMultimap;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -82,9 +82,9 @@ public class GroupCollector {
if (rsrc.getEdit().isPresent()) {
// Groups for an edit are just the base revision's groups, since they have
// the same parent.
- return rsrc.getEdit().get().getBasePatchSet().getGroups();
+ return rsrc.getEdit().get().getBasePatchSet().groups();
}
- return rsrc.getPatchSet().getGroups();
+ return rsrc.getPatchSet().groups();
}
private interface Lookup {
@@ -107,9 +107,9 @@ public class GroupCollector {
transformRefs(changeRefsById),
psId -> {
// TODO(dborowitz): Reuse open repository from caller.
- ChangeNotes notes = notesFactory.createChecked(project, psId.getParentKey());
+ ChangeNotes notes = notesFactory.createChecked(project, psId.changeId());
PatchSet ps = psUtil.get(notes, psId);
- return ps != null ? ps.getGroups() : null;
+ return ps != null ? ps.groups() : null;
});
}
diff --git a/java/com/google/gerrit/server/git/HookUtil.java b/java/com/google/gerrit/server/git/HookUtil.java
index 3bef7cccb2..fd29c8debc 100644
--- a/java/com/google/gerrit/server/git/HookUtil.java
+++ b/java/com/google/gerrit/server/git/HookUtil.java
@@ -19,8 +19,9 @@ import static java.util.stream.Collectors.toMap;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.transport.BaseReceivePack;
+import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
+import org.eclipse.jgit.transport.UploadPack;
/** Static utilities for writing git protocol hooks. */
public class HookUtil {
@@ -32,8 +33,7 @@ public class HookUtil {
* @return map of refs that were advertised.
* @throws ServiceMayNotContinueException if a problem occurred.
*/
- @SuppressWarnings("deprecation")
- public static Map<String, Ref> ensureAllRefsAdvertised(BaseReceivePack rp)
+ public static Map<String, Ref> ensureAllRefsAdvertised(ReceivePack rp)
throws ServiceMayNotContinueException {
Map<String, Ref> refs = rp.getAdvertisedRefs();
if (refs != null) {
@@ -52,5 +52,32 @@ public class HookUtil {
return refs;
}
+ /**
+ * Scan and advertise all refs in the repo if refs have not already been advertised; otherwise,
+ * just return the advertised map.
+ *
+ * @param up upload-pack handler.
+ * @return map of refs that were advertised.
+ * @throws ServiceMayNotContinueException if a problem occurred.
+ */
+ public static Map<String, Ref> ensureAllRefsAdvertised(UploadPack up)
+ throws ServiceMayNotContinueException {
+ Map<String, Ref> refs = up.getAdvertisedRefs();
+ if (refs != null) {
+ return refs;
+ }
+ try {
+ refs =
+ up.getRepository().getRefDatabase().getRefs().stream()
+ .collect(toMap(Ref::getName, r -> r));
+ } catch (ServiceMayNotContinueException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new ServiceMayNotContinueException(e);
+ }
+ up.setAdvertisedRefs(refs);
+ return refs;
+ }
+
private HookUtil() {}
}
diff --git a/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
index 85822a8997..44d1077a92 100644
--- a/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
+++ b/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.git;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
@@ -254,7 +254,7 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
int newLen = projectName.length() - Constants.DOT_GIT_EXT.length();
projectName = projectName.substring(0, newLen);
}
- return new Project.NameKey(projectName);
+ return Project.nameKey(projectName);
}
protected class ProjectVisitor extends SimpleFileVisitor<Path> {
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index c1d8ddd1b4..ad878433fa 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -17,19 +17,30 @@ package com.google.gerrit.server.git;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Comparator.naturalOrder;
import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.joining;
-import com.google.common.base.Joiner;
import com.google.common.base.Strings;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.exceptions.InvalidMergeStrategyException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -38,13 +49,6 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -67,7 +71,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -117,6 +120,13 @@ import org.eclipse.jgit.util.TemporaryBuffer;
public class MergeUtil {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ /**
+ * Length of abbreviated hex SHA-1s in merged filenames.
+ *
+ * <p>This is a constant so output is stable over time even if the SHA-1 prefix becomes ambiguous.
+ */
+ private static final int NAME_ABBREV_LEN = 6;
+
static class PluggableCommitMessageGenerator {
private final DynamicSet<ChangeMessageModifier> changeMessageModifiers;
@@ -126,7 +136,7 @@ public class MergeUtil {
}
public String generate(
- RevCommit original, RevCommit mergeTip, Branch.NameKey dest, String originalMessage) {
+ RevCommit original, RevCommit mergeTip, BranchNameKey dest, String originalMessage) {
requireNonNull(original.getRawBuffer());
if (mergeTip != null) {
requireNonNull(mergeTip.getRawBuffer());
@@ -256,7 +266,8 @@ public class MergeUtil {
boolean ignoreIdenticalTree,
boolean allowConflicts)
throws MissingObjectException, IncorrectObjectTypeException, IOException,
- MergeIdenticalTreeException, MergeConflictException, MethodNotAllowedException {
+ MergeIdenticalTreeException, MergeConflictException, MethodNotAllowedException,
+ InvalidMergeStrategyException {
ThreeWayMerger m = newThreeWayMerger(inserter, repoConfig);
m.setBase(originalCommit.getParent(parentIndex));
@@ -337,13 +348,13 @@ public class MergeUtil {
String.format(
"%0$-" + nameLength + "s (%s %s)",
oursName,
- ours.abbreviate(6).name(),
+ abbreviateName(ours, NAME_ABBREV_LEN),
oursMsg.substring(0, Math.min(oursMsg.length(), 60)));
String theirsNameFormatted =
String.format(
"%0$-" + nameLength + "s (%s %s)",
theirsName,
- theirs.abbreviate(6).name(),
+ abbreviateName(theirs, NAME_ABBREV_LEN),
theirsMsg.substring(0, Math.min(theirsMsg.length(), 60)));
MergeFormatter fmt = new MergeFormatter();
@@ -422,7 +433,8 @@ public class MergeUtil {
PersonIdent committerIndent,
String commitMsg,
RevWalk rw)
- throws IOException, MergeIdenticalTreeException, MergeConflictException {
+ throws IOException, MergeIdenticalTreeException, MergeConflictException,
+ InvalidMergeStrategyException {
if (!MergeStrategy.THEIRS.getName().equals(mergeStrategy)
&& rw.isMergedInto(originalCommit, mergeTip)) {
@@ -450,7 +462,7 @@ public class MergeUtil {
}
public static String createConflictMessage(List<String> conflicts) {
- StringBuilder sb = new StringBuilder("merge conflict(s)");
+ StringBuilder sb = new StringBuilder("merge conflict(s):");
for (String c : conflicts) {
sb.append('\n').append(c);
}
@@ -514,7 +526,7 @@ public class MergeUtil {
PatchSetApproval submitAudit = null;
for (PatchSetApproval a : safeGetApprovals(notes, psId)) {
- if (a.getValue() <= 0) {
+ if (a.value() <= 0) {
// Negative votes aren't counted.
continue;
}
@@ -522,29 +534,29 @@ public class MergeUtil {
if (a.isLegacySubmit()) {
// Submit is treated specially, below (becomes committer)
//
- if (submitAudit == null || a.getGranted().compareTo(submitAudit.getGranted()) > 0) {
+ if (submitAudit == null || a.granted().compareTo(submitAudit.granted()) > 0) {
submitAudit = a;
}
continue;
}
- final Account acc = identifiedUserFactory.create(a.getAccountId()).getAccount();
+ final Account acc = identifiedUserFactory.create(a.accountId()).getAccount();
final StringBuilder identbuf = new StringBuilder();
- if (acc.getFullName() != null && acc.getFullName().length() > 0) {
+ if (acc.fullName() != null && acc.fullName().length() > 0) {
if (identbuf.length() > 0) {
identbuf.append(' ');
}
- identbuf.append(acc.getFullName());
+ identbuf.append(acc.fullName());
}
- if (acc.getPreferredEmail() != null && acc.getPreferredEmail().length() > 0) {
- if (isSignedOffBy(footers, acc.getPreferredEmail())) {
+ if (acc.preferredEmail() != null && acc.preferredEmail().length() > 0) {
+ if (isSignedOffBy(footers, acc.preferredEmail())) {
continue;
}
if (identbuf.length() > 0) {
identbuf.append(' ');
}
identbuf.append('<');
- identbuf.append(acc.getPreferredEmail());
+ identbuf.append(acc.preferredEmail());
identbuf.append('>');
}
if (identbuf.length() == 0) {
@@ -553,12 +565,12 @@ public class MergeUtil {
}
final String tag;
- if (isCodeReview(a.getLabelId())) {
+ if (isCodeReview(a.labelId())) {
tag = "Reviewed-by";
- } else if (isVerified(a.getLabelId())) {
+ } else if (isVerified(a.labelId())) {
tag = "Tested-by";
} else {
- final LabelType lt = project.getLabelTypes().byLabel(a.getLabelId());
+ final LabelType lt = project.getLabelTypes().byLabel(a.labelId());
if (lt == null) {
continue;
}
@@ -733,10 +745,10 @@ public class MergeUtil {
CodeReviewRevWalk rw,
ObjectInserter inserter,
Config repoConfig,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
CodeReviewCommit mergeTip,
CodeReviewCommit n)
- throws IntegrationException {
+ throws IntegrationException, InvalidMergeStrategyException {
ThreeWayMerger m = newThreeWayMerger(inserter, repoConfig);
try {
if (m.merge(mergeTip, n)) {
@@ -789,7 +801,7 @@ public class MergeUtil {
PersonIdent committer,
CodeReviewRevWalk rw,
ObjectInserter inserter,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
CodeReviewCommit mergeTip,
ObjectId treeId,
CodeReviewCommit n)
@@ -806,9 +818,9 @@ public class MergeUtil {
}
StringBuilder msgbuf = new StringBuilder().append(summarize(rw, merged));
- if (!R_HEADS_MASTER.equals(destBranch.get())) {
+ if (!R_HEADS_MASTER.equals(destBranch.branch())) {
msgbuf.append(" into ");
- msgbuf.append(destBranch.getShortName());
+ msgbuf.append(destBranch.shortName());
}
if (merged.size() > 1) {
@@ -840,29 +852,26 @@ public class MergeUtil {
return String.format("Merge \"%s\"", c.getShortMessage());
}
- LinkedHashSet<String> topics = new LinkedHashSet<>(4);
- for (CodeReviewCommit c : merged) {
- if (!Strings.isNullOrEmpty(c.change().getTopic())) {
- topics.add(c.change().getTopic());
- }
- }
+ ImmutableSortedSet<String> topics =
+ merged.stream()
+ .map(c -> c.change().getTopic())
+ .filter(t -> !Strings.isNullOrEmpty(t))
+ .map(t -> "\"" + t + "\"")
+ .collect(toImmutableSortedSet(naturalOrder()));
- if (topics.size() == 1) {
- return String.format("Merge changes from topic \"%s\"", Iterables.getFirst(topics, null));
- } else if (topics.size() > 1) {
- return String.format("Merge changes from topics \"%s\"", Joiner.on("\", \"").join(topics));
- } else {
+ if (!topics.isEmpty()) {
return String.format(
- "Merge changes %s%s",
- FluentIterable.from(merged)
- .limit(5)
- .transform(c -> c.change().getKey().abbreviate())
- .join(Joiner.on(',')),
- merged.size() > 5 ? ", ..." : "");
+ "Merge changes from topic%s %s",
+ topics.size() > 1 ? "s" : "", topics.stream().collect(joining(", ")));
}
+ return merged.stream()
+ .limit(5)
+ .map(c -> c.change().getKey().abbreviate())
+ .collect(joining(",", "Merge changes ", merged.size() > 5 ? ", ..." : ""));
}
- public ThreeWayMerger newThreeWayMerger(ObjectInserter inserter, Config repoConfig) {
+ public ThreeWayMerger newThreeWayMerger(ObjectInserter inserter, Config repoConfig)
+ throws InvalidMergeStrategyException {
return newThreeWayMerger(inserter, repoConfig, mergeStrategyName());
}
@@ -886,7 +895,8 @@ public class MergeUtil {
}
public static ThreeWayMerger newThreeWayMerger(
- ObjectInserter inserter, Config repoConfig, String strategyName) {
+ ObjectInserter inserter, Config repoConfig, String strategyName)
+ throws InvalidMergeStrategyException {
Merger m = newMerger(inserter, repoConfig, strategyName);
checkArgument(
m instanceof ThreeWayMerger,
@@ -895,9 +905,12 @@ public class MergeUtil {
return (ThreeWayMerger) m;
}
- public static Merger newMerger(ObjectInserter inserter, Config repoConfig, String strategyName) {
+ public static Merger newMerger(ObjectInserter inserter, Config repoConfig, String strategyName)
+ throws InvalidMergeStrategyException {
MergeStrategy strategy = MergeStrategy.get(strategyName);
- checkArgument(strategy != null, "invalid merge strategy: %s", strategyName);
+ if (strategy == null) {
+ throw new InvalidMergeStrategyException(strategyName);
+ }
return strategy.newMerger(
new ObjectInserter.Filter() {
@Override
@@ -975,7 +988,7 @@ public class MergeUtil {
if (c.getPatchsetId() == null) {
continue;
}
- Change.Id id = c.getPatchsetId().getParentKey();
+ Change.Id id = c.getPatchsetId().changeId();
if (!expected.contains(id)) {
continue;
}
diff --git a/java/com/google/gerrit/server/git/MergedByPushOp.java b/java/com/google/gerrit/server/git/MergedByPushOp.java
index 3d64b82542..858a55aa5c 100644
--- a/java/com/google/gerrit/server/git/MergedByPushOp.java
+++ b/java/com/google/gerrit/server/git/MergedByPushOp.java
@@ -17,13 +17,11 @@ package com.google.gerrit.server.git;
import static java.util.Objects.requireNonNull;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetInfo;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.config.SendEmailExecutor;
@@ -42,7 +40,6 @@ import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -112,7 +109,7 @@ public class MergedByPushOp implements BatchUpdateOp {
@Override
public boolean updateChange(ChangeContext ctx) throws IOException {
change = ctx.getChange();
- correctBranch = refName.equals(change.getDest().get());
+ correctBranch = refName.equals(change.getDest().branch());
if (!correctBranch) {
return false;
}
@@ -145,7 +142,7 @@ public class MergedByPushOp implements BatchUpdateOp {
}
StringBuilder msgBuf = new StringBuilder();
msgBuf.append("Change has been successfully pushed");
- if (!refName.equals(change.getDest().get())) {
+ if (!refName.equals(change.getDest().branch())) {
msgBuf.append(" into ");
if (refName.startsWith(Constants.R_HEADS)) {
msgBuf.append("branch ");
@@ -159,12 +156,7 @@ public class MergedByPushOp implements BatchUpdateOp {
ChangeMessagesUtil.newMessage(
psId, ctx.getUser(), ctx.getWhen(), msgBuf.toString(), ChangeMessagesUtil.TAG_MERGED);
cmUtil.addChangeMessage(update, msg);
-
- PatchSetApproval submitter =
- ApprovalsUtil.newApproval(
- change.currentPatchSetId(), ctx.getUser(), LabelId.legacySubmit(), 1, ctx.getWhen());
- update.putApproval(submitter.getLabel(), submitter.getValue());
-
+ update.putApproval(LabelId.legacySubmit().get(), (short) 1);
return true;
}
@@ -182,7 +174,7 @@ public class MergedByPushOp implements BatchUpdateOp {
public void run() {
try {
MergedSender cm =
- mergedSenderFactory.create(ctx.getProject(), psId.getParentKey());
+ mergedSenderFactory.create(ctx.getProject(), psId.changeId());
cm.setFrom(ctx.getAccountId());
cm.setPatchSet(patchSet, info);
cm.send();
@@ -203,8 +195,7 @@ public class MergedByPushOp implements BatchUpdateOp {
private PatchSetInfo getPatchSetInfo(ChangeContext ctx) throws IOException {
RevWalk rw = ctx.getRevWalk();
- RevCommit commit =
- rw.parseCommit(ObjectId.fromString(requireNonNull(patchSet).getRevision().get()));
+ RevCommit commit = rw.parseCommit(requireNonNull(patchSet).commitId());
return patchSetInfoFactory.get(rw, commit, psId);
}
}
diff --git a/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java b/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
index 01e85cf332..32b5bb83c1 100644
--- a/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
+++ b/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.git;
import static com.google.common.base.Preconditions.checkState;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.RepositoryConfig;
import com.google.gerrit.server.config.SitePaths;
diff --git a/java/com/google/gerrit/server/git/MultiProgressMonitor.java b/java/com/google/gerrit/server/git/MultiProgressMonitor.java
index b72ea92c0c..bfc51350b0 100644
--- a/java/com/google/gerrit/server/git/MultiProgressMonitor.java
+++ b/java/com/google/gerrit/server/git/MultiProgressMonitor.java
@@ -346,6 +346,8 @@ public class MultiProgressMonitor {
out.write(Constants.encode(s.toString()));
out.flush();
} catch (IOException e) {
+ logger.atWarning().withCause(e).log(
+ "Sending progress to client failed. Stop sending updates for task %s", taskName);
write = false;
}
}
diff --git a/java/com/google/gerrit/server/git/NotesBranchUtil.java b/java/com/google/gerrit/server/git/NotesBranchUtil.java
index 1636b85571..8cb2b40634 100644
--- a/java/com/google/gerrit/server/git/NotesBranchUtil.java
+++ b/java/com/google/gerrit/server/git/NotesBranchUtil.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.git;
import static com.google.common.base.MoreObjects.firstNonNull;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/git/NotifyConfig.java b/java/com/google/gerrit/server/git/NotifyConfig.java
index 7f74f54983..429f15a95f 100644
--- a/java/com/google/gerrit/server/git/NotifyConfig.java
+++ b/java/com/google/gerrit/server/git/NotifyConfig.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.git;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.mail.Address;
@@ -113,6 +114,13 @@ public class NotifyConfig implements Comparable<NotifyConfig> {
@Override
public String toString() {
- return "NotifyConfig[" + name + " = " + addresses + " + " + groups + "]";
+ return MoreObjects.toStringHelper(this)
+ .add("name", name)
+ .add("addresses", addresses)
+ .add("groups", groups)
+ .add("header", header)
+ .add("types", types)
+ .add("filter", filter)
+ .toString();
}
}
diff --git a/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java b/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java
new file mode 100644
index 0000000000..c68842de02
--- /dev/null
+++ b/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java
@@ -0,0 +1,185 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Wrapper around {@link DelegateRefDatabase} that filters all refs using {@link
+ * com.google.gerrit.server.permissions.PermissionBackend}.
+ */
+public class PermissionAwareReadOnlyRefDatabase extends DelegateRefDatabase {
+
+ private final PermissionBackend.ForProject forProject;
+
+ @Inject
+ PermissionAwareReadOnlyRefDatabase(
+ Repository delegateRepository, PermissionBackend.ForProject forProject) {
+ super(delegateRepository);
+ this.forProject = forProject;
+ }
+
+ @Override
+ public boolean isNameConflicting(String name) {
+ throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only");
+ }
+
+ @Override
+ public RefUpdate newUpdate(String name, boolean detach) {
+ throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only");
+ }
+
+ @Override
+ public RefRename newRename(String fromName, String toName) {
+ throw new UnsupportedOperationException("PermissionAwareReadOnlyRefDatabase is read-only");
+ }
+
+ @Override
+ public Ref exactRef(String name) throws IOException {
+ Ref ref = getDelegate().getRefDatabase().exactRef(name);
+ if (ref == null) {
+ return null;
+ }
+
+ Map<String, Ref> result;
+ try {
+ result =
+ forProject.filter(ImmutableMap.of(name, ref), getDelegate(), RefFilterOptions.defaults());
+ } catch (PermissionBackendException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ }
+ throw new IOException(e);
+ }
+ if (result.isEmpty()) {
+ return null;
+ }
+
+ Preconditions.checkState(
+ result.size() == 1, "Only one element expected, but was: " + result.size());
+ return Iterables.getOnlyElement(result.values());
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public Map<String, Ref> getRefs(String prefix) throws IOException {
+ Map<String, Ref> refs = getDelegate().getRefDatabase().getRefs(prefix);
+ if (refs.isEmpty()) {
+ return refs;
+ }
+
+ Map<String, Ref> result;
+ try {
+ // The security filtering assumes to receive the same refMap, independently from the ref
+ // prefix offset
+ result =
+ forProject.filter(
+ prefixIndependentRefMap(prefix, refs), getDelegate(), RefFilterOptions.defaults());
+ } catch (PermissionBackendException e) {
+ throw new IOException("", e);
+ }
+ return applyPrefixRefMap(prefix, result);
+ }
+
+ private Map<String, Ref> prefixIndependentRefMap(String prefix, Map<String, Ref> refs) {
+ if (prefix.length() > 0) {
+ return refs.values().stream().collect(Collectors.toMap(Ref::getName, Function.identity()));
+ }
+
+ return refs;
+ }
+
+ private Map<String, Ref> applyPrefixRefMap(String prefix, Map<String, Ref> refs) {
+ int prefixSlashPos = prefix.lastIndexOf('/') + 1;
+ if (prefixSlashPos > 0) {
+ return refs.values().stream()
+ .collect(
+ Collectors.toMap(
+ (Ref ref) -> ref.getName().substring(prefixSlashPos), Function.identity()));
+ }
+
+ return refs;
+ }
+
+ @Override
+ public List<Ref> getRefsByPrefix(String prefix) throws IOException {
+ Map<String, Ref> coarseRefs;
+ int lastSlash = prefix.lastIndexOf('/');
+ if (lastSlash == -1) {
+ coarseRefs = getRefs(ALL);
+ } else {
+ coarseRefs = getRefs(prefix.substring(0, lastSlash + 1));
+ }
+
+ List<Ref> result;
+ if (lastSlash + 1 == prefix.length()) {
+ result = coarseRefs.values().stream().collect(toList());
+ } else {
+ String p = prefix.substring(lastSlash + 1);
+ result =
+ coarseRefs.entrySet().stream()
+ .filter(e -> e.getKey().startsWith(p))
+ .map(e -> e.getValue())
+ .collect(toList());
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ @Override
+ @NonNull
+ public Map<String, Ref> exactRef(String... refs) throws IOException {
+ Map<String, Ref> result = new HashMap<>(refs.length);
+ for (String name : refs) {
+ Ref ref = exactRef(name);
+ if (ref != null) {
+ result.put(name, ref);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ @Nullable
+ public Ref firstExactRef(String... refs) throws IOException {
+ for (String name : refs) {
+ Ref ref = exactRef(name);
+ if (ref != null) {
+ return ref;
+ }
+ }
+ return null;
+ }
+}
diff --git a/java/com/google/gerrit/server/git/PermissionAwareRepository.java b/java/com/google/gerrit/server/git/PermissionAwareRepository.java
new file mode 100644
index 0000000000..bb80cb59a7
--- /dev/null
+++ b/java/com/google/gerrit/server/git/PermissionAwareRepository.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import com.google.gerrit.server.permissions.PermissionBackend;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Wrapper around {@link DelegateRepository} that overwrites {@link #getRefDatabase()} to return a
+ * {@link PermissionAwareReadOnlyRefDatabase}.
+ */
+public class PermissionAwareRepository extends DelegateRepository {
+
+ private final PermissionAwareReadOnlyRefDatabase permissionAwareReadOnlyRefDatabase;
+
+ public PermissionAwareRepository(Repository delegate, PermissionBackend.ForProject forProject) {
+ super(delegate);
+ this.permissionAwareReadOnlyRefDatabase =
+ new PermissionAwareReadOnlyRefDatabase(delegate, forProject);
+ }
+
+ @Override
+ public RefDatabase getRefDatabase() {
+ return permissionAwareReadOnlyRefDatabase;
+ }
+}
diff --git a/java/com/google/gerrit/server/git/PermissionAwareRepositoryManager.java b/java/com/google/gerrit/server/git/PermissionAwareRepositoryManager.java
new file mode 100644
index 0000000000..b11aa4967e
--- /dev/null
+++ b/java/com/google/gerrit/server/git/PermissionAwareRepositoryManager.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import com.google.common.base.Preconditions;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Wraps and unwraps existing repositories and makes them permission-aware by returning a {@link
+ * PermissionAwareReadOnlyRefDatabase}.
+ */
+public class PermissionAwareRepositoryManager {
+ public static Repository wrap(Repository delegate, PermissionBackend.ForProject forProject) {
+ Preconditions.checkState(
+ !(delegate instanceof PermissionAwareRepository),
+ "Cannot wrap PermissionAwareRepository instance");
+ return new PermissionAwareRepository(delegate, forProject);
+ }
+}
diff --git a/java/com/google/gerrit/server/git/ProjectRunnable.java b/java/com/google/gerrit/server/git/ProjectRunnable.java
index 23d2326dee..e74bf2d1ca 100644
--- a/java/com/google/gerrit/server/git/ProjectRunnable.java
+++ b/java/com/google/gerrit/server/git/ProjectRunnable.java
@@ -13,7 +13,7 @@
// limitations under the License.
package com.google.gerrit.server.git;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
/** Used to retrieve the project name from an operation * */
public interface ProjectRunnable extends Runnable {
diff --git a/java/com/google/gerrit/server/git/PureRevertCache.java b/java/com/google/gerrit/server/git/PureRevertCache.java
index 16ac87ff38..9f9530c2df 100644
--- a/java/com/google/gerrit/server/git/PureRevertCache.java
+++ b/java/com/google/gerrit/server/git/PureRevertCache.java
@@ -18,15 +18,16 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.proto.Cache;
import com.google.gerrit.server.cache.proto.Cache.PureRevertKeyProto;
import com.google.gerrit.server.cache.serialize.BooleanCacheSerializer;
import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import com.google.gerrit.server.cache.serialize.ProtobufSerializer;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ProjectCache;
@@ -35,7 +36,6 @@ import com.google.inject.Module;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.google.protobuf.ByteString;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -49,6 +49,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.merge.ThreeWayMerger;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.io.DisabledOutputStream;
/** Computes and caches if a change is a pure revert of another change. */
@Singleton
@@ -97,8 +98,8 @@ public class PureRevertCache {
claimedRevert.getProjectName(), claimedRevert.getChange().getRevertOf());
return isPureRevert(
claimedRevert.getProjectName(),
- ObjectId.fromString(claimedRevert.getCurrentPatchSet().getRevision().get()),
- ObjectId.fromString(claimedOriginal.getCurrentPatchSet().getRevision().get()));
+ claimedRevert.getCurrentPatchSet().commitId(),
+ claimedOriginal.getCurrentPatchSet().commitId());
}
/**
@@ -151,10 +152,12 @@ public class PureRevertCache {
@Override
public Boolean load(PureRevertKeyProto key) throws BadRequestException, IOException {
try (TraceContext.TraceTimer ignored =
- TraceContext.newTimer("Loading pure revert for %s", key)) {
+ TraceContext.newTimer(
+ "Loading pure revert",
+ Metadata.builder().cacheKey(key.toString()).projectName(key.getProject()).build())) {
ObjectId original = ObjectIdConverter.create().fromByteString(key.getClaimedOriginal());
ObjectId revert = ObjectIdConverter.create().fromByteString(key.getClaimedRevert());
- Project.NameKey project = new Project.NameKey(key.getProject());
+ Project.NameKey project = Project.nameKey(key.getProject());
try (Repository repo = repoManager.openRepository(project);
ObjectInserter oi = repo.newObjectInserter();
@@ -185,9 +188,8 @@ public class PureRevertCache {
}
// Any differences between claimed original's parent and the rebase result indicate that
- // the
- // claimedRevert is not a pure revert but made content changes
- try (DiffFormatter df = new DiffFormatter(new ByteArrayOutputStream())) {
+ // the claimedRevert is not a pure revert but made content changes
+ try (DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE)) {
df.setReader(oi.newReader(), repo.getConfig());
List<DiffEntry> entries =
df.scan(claimedOriginalCommit.getParent(0), merger.getResultTreeId());
diff --git a/java/com/google/gerrit/server/git/ReceivePackInitializer.java b/java/com/google/gerrit/server/git/ReceivePackInitializer.java
index f3742156e3..d7af2802b6 100644
--- a/java/com/google/gerrit/server/git/ReceivePackInitializer.java
+++ b/java/com/google/gerrit/server/git/ReceivePackInitializer.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.git;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Project;
import org.eclipse.jgit.transport.ReceivePack;
@ExtensionPoint
diff --git a/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java b/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java
index 3a7a1256c2..8535cd2607 100644
--- a/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java
+++ b/java/com/google/gerrit/server/git/RepositoryCaseMismatchException.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.git;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
/**
diff --git a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
index d7f8982e40..196fc61f10 100644
--- a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
+++ b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
@@ -20,14 +20,15 @@ import com.google.common.cache.LoadingCache;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.index.change.ChangeField;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.query.change.ChangeData;
@@ -136,7 +137,7 @@ public class SearchingChangeCacheImpl implements GitReferenceUpdatedListener {
@Override
public void onGitReferenceUpdated(GitReferenceUpdatedListener.Event event) {
if (event.getRefName().startsWith(RefNames.REFS_CHANGES)) {
- cache.invalidate(new Project.NameKey(event.getProjectName()));
+ cache.invalidate(Project.nameKey(event.getProjectName()));
}
}
@@ -152,7 +153,9 @@ public class SearchingChangeCacheImpl implements GitReferenceUpdatedListener {
@Override
public List<CachedChange> load(Project.NameKey key) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading changes of project %s", key);
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading changes of project", Metadata.builder().projectName(key.get()).build());
ManualRequestContext ctx = requestContext.open()) {
List<ChangeData> cds =
queryProvider
diff --git a/java/com/google/gerrit/server/git/SystemReaderInstaller.java b/java/com/google/gerrit/server/git/SystemReaderInstaller.java
new file mode 100644
index 0000000000..b6cc108bdf
--- /dev/null
+++ b/java/com/google/gerrit/server/git/SystemReaderInstaller.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.util.git.DelegateSystemReader;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
+
+@Singleton
+public class SystemReaderInstaller implements LifecycleListener {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final SitePaths site;
+
+ @Inject
+ SystemReaderInstaller(SitePaths site) {
+ this.site = site;
+ }
+
+ @Override
+ public void start() {
+ SystemReader.setInstance(customReader());
+ logger.atInfo().log("Set JGit's SystemReader to read system config from %s", site.jgit_config);
+ }
+
+ @Override
+ public void stop() {}
+
+ private SystemReader customReader() {
+ SystemReader current = SystemReader.getInstance();
+
+ return new DelegateSystemReader(current) {
+ @Override
+ public FileBasedConfig openSystemConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, site.jgit_config.toFile(), FS.DETECTED);
+ }
+ };
+ }
+}
diff --git a/java/com/google/gerrit/server/git/TagCache.java b/java/com/google/gerrit/server/git/TagCache.java
index 535644d8f8..daf5ea5fbe 100644
--- a/java/com/google/gerrit/server/git/TagCache.java
+++ b/java/com/google/gerrit/server/git/TagCache.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.git;
import com.google.common.cache.Cache;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.serialize.StringCacheSerializer;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/git/TagSet.java b/java/com/google/gerrit/server/git/TagSet.java
index 57637c897b..43483bfb38 100644
--- a/java/com/google/gerrit/server/git/TagSet.java
+++ b/java/com/google/gerrit/server/git/TagSet.java
@@ -18,9 +18,9 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto.TagSetProto;
import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto.TagSetProto.CachedRefProto;
import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto.TagSetProto.TagProto;
@@ -232,7 +232,7 @@ class TagSet {
new Tag(
idConverter.fromByteString(t.getId()),
BitSet.valueOf(t.getFlags().asReadOnlyByteBuffer()))));
- return new TagSet(new Project.NameKey(proto.getProjectName()), refs, tags);
+ return new TagSet(Project.nameKey(proto.getProjectName()), refs, tags);
}
TagSetProto toProto() {
diff --git a/java/com/google/gerrit/server/git/TagSetHolder.java b/java/com/google/gerrit/server/git/TagSetHolder.java
index 194283e7af..a574308525 100644
--- a/java/com/google/gerrit/server/git/TagSetHolder.java
+++ b/java/com/google/gerrit/server/git/TagSetHolder.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.git;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
import java.util.Collection;
@@ -117,7 +117,7 @@ public class TagSetHolder {
@Override
public TagSetHolder deserialize(byte[] in) {
TagSetHolderProto proto = Protos.parseUnchecked(TagSetHolderProto.parser(), in);
- TagSetHolder holder = new TagSetHolder(new Project.NameKey(proto.getProjectName()));
+ TagSetHolder holder = new TagSetHolder(Project.nameKey(proto.getProjectName()));
if (proto.hasTags()) {
holder.tags = TagSet.fromProto(proto.getTags());
}
diff --git a/java/com/google/gerrit/server/git/TracingHook.java b/java/com/google/gerrit/server/git/TracingHook.java
new file mode 100644
index 0000000000..56eded05cd
--- /dev/null
+++ b/java/com/google/gerrit/server/git/TracingHook.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import com.google.gerrit.server.logging.TraceContext;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jgit.transport.FetchV2Request;
+import org.eclipse.jgit.transport.LsRefsV2Request;
+import org.eclipse.jgit.transport.ProtocolV2Hook;
+
+/**
+ * Git hook for ls-refs and fetch that enables Gerrit request tracing if the user sets the 'trace'
+ * server option.
+ *
+ * <p>This hook is only invoked if Git protocol v2 is used.
+ *
+ * <p>If the 'trace' server option is specified without value, this means without providing a trace
+ * ID, a trace ID is generated, but it's not returned to the client. Hence users are advised to
+ * always provide a trace ID.
+ */
+public class TracingHook implements ProtocolV2Hook, AutoCloseable {
+ private TraceContext traceContext;
+
+ @Override
+ public void onLsRefs(LsRefsV2Request req) {
+ maybeStartTrace(req.getServerOptions());
+ }
+
+ @Override
+ public void onFetch(FetchV2Request req) {
+ maybeStartTrace(req.getServerOptions());
+ }
+
+ @Override
+ public void close() {
+ if (traceContext != null) {
+ traceContext.close();
+ }
+ }
+
+ /**
+ * Starts request tracing if 'trace' server option is set.
+ *
+ * @param serverOptionList list of provided server options
+ */
+ private void maybeStartTrace(List<String> serverOptionList) {
+ if (traceContext != null) {
+ // Trace was already started
+ return;
+ }
+
+ Optional<String> traceOption = parseTraceOption(serverOptionList);
+ traceContext =
+ TraceContext.newTrace(
+ traceOption.isPresent(),
+ traceOption.orElse(null),
+ (tagName, traceId) -> {
+ // TODO(ekempin): Return trace ID to client
+ });
+ }
+
+ private Optional<String> parseTraceOption(List<String> serverOptionList) {
+ if (serverOptionList == null || serverOptionList.isEmpty()) {
+ return Optional.empty();
+ }
+
+ Optional<String> traceOption =
+ serverOptionList.stream().filter(o -> o.startsWith("trace")).findAny();
+ if (!traceOption.isPresent()) {
+ return Optional.empty();
+ }
+
+ int e = traceOption.get().indexOf('=');
+ if (e > 0) {
+ // trace option was specified with trace ID: "--trace=<trace-ID>"
+ return Optional.of(traceOption.get().substring(e + 1));
+ }
+
+ // trace option was specified without trace ID: "--trace",
+ // return an empty string so that a trace ID is generated
+ return Optional.of("");
+ }
+}
diff --git a/java/com/google/gerrit/server/git/UploadPackInitializer.java b/java/com/google/gerrit/server/git/UploadPackInitializer.java
index b63c5b34e4..7e97d872a9 100644
--- a/java/com/google/gerrit/server/git/UploadPackInitializer.java
+++ b/java/com/google/gerrit/server/git/UploadPackInitializer.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.git;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Project;
import org.eclipse.jgit.transport.UploadPack;
@ExtensionPoint
diff --git a/java/com/google/gerrit/server/git/UploadPackMetricsHook.java b/java/com/google/gerrit/server/git/UploadPackMetricsHook.java
index aa02fba99a..4afff2bdf9 100644
--- a/java/com/google/gerrit/server/git/UploadPackMetricsHook.java
+++ b/java/com/google/gerrit/server/git/UploadPackMetricsHook.java
@@ -23,6 +23,7 @@ import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Histogram1;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.server.logging.Metadata;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.storage.pack.PackStatistics;
@@ -43,14 +44,15 @@ public class UploadPackMetricsHook implements PostUploadHook {
@Inject
UploadPackMetricsHook(MetricMaker metricMaker) {
- Field<Operation> operation = Field.ofEnum(Operation.class, "operation");
+ Field<Operation> operationField =
+ Field.ofEnum(Operation.class, "operation", Metadata.Builder::gitOperation).build();
requestCount =
metricMaker.newCounter(
"git/upload-pack/request_count",
new Description("Total number of git-upload-pack requests")
.setRate()
.setUnit("requests"),
- operation);
+ operationField);
counting =
metricMaker.newTimer(
@@ -58,7 +60,7 @@ public class UploadPackMetricsHook implements PostUploadHook {
new Description("Time spent in the 'Counting...' phase")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- operation);
+ operationField);
compressing =
metricMaker.newTimer(
@@ -66,7 +68,7 @@ public class UploadPackMetricsHook implements PostUploadHook {
new Description("Time spent in the 'Compressing...' phase")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- operation);
+ operationField);
writing =
metricMaker.newTimer(
@@ -74,7 +76,7 @@ public class UploadPackMetricsHook implements PostUploadHook {
new Description("Time spent transferring bytes to client")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- operation);
+ operationField);
packBytes =
metricMaker.newHistogram(
@@ -82,7 +84,7 @@ public class UploadPackMetricsHook implements PostUploadHook {
new Description("Distribution of sizes of packs sent to clients")
.setCumulative()
.setUnit(Units.BYTES),
- operation);
+ operationField);
}
@Override
diff --git a/java/com/google/gerrit/server/git/UsersSelfAdvertiseRefsHook.java b/java/com/google/gerrit/server/git/UsersSelfAdvertiseRefsHook.java
new file mode 100644
index 0000000000..6c1879e1e5
--- /dev/null
+++ b/java/com/google/gerrit/server/git/UsersSelfAdvertiseRefsHook.java
@@ -0,0 +1,93 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.server.CurrentUser;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import java.util.Map;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.transport.AdvertiseRefsHook;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
+import org.eclipse.jgit.transport.UploadPack;
+
+/**
+ * Advertises {@code refs/users/self} for authenticated users when interacting with the {@code
+ * All-Users} repository.
+ */
+@Singleton
+public class UsersSelfAdvertiseRefsHook implements AdvertiseRefsHook {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final Provider<CurrentUser> userProvider;
+
+ @Inject
+ public UsersSelfAdvertiseRefsHook(Provider<CurrentUser> userProvider) {
+ this.userProvider = userProvider;
+ }
+
+ @Override
+ public void advertiseRefs(UploadPack uploadPack) throws ServiceMayNotContinueException {
+ CurrentUser user = userProvider.get();
+ if (!user.isIdentifiedUser()) {
+ return;
+ }
+
+ addSelfSymlinkIfNecessary(
+ uploadPack.getRepository().getRefDatabase(),
+ HookUtil.ensureAllRefsAdvertised(uploadPack),
+ user.getAccountId());
+ }
+
+ @Override
+ public void advertiseRefs(ReceivePack receivePack) throws ServiceMayNotContinueException {
+ CurrentUser user = userProvider.get();
+ if (!user.isIdentifiedUser()) {
+ return;
+ }
+
+ addSelfSymlinkIfNecessary(
+ receivePack.getRepository().getRefDatabase(),
+ HookUtil.ensureAllRefsAdvertised(receivePack),
+ user.getAccountId());
+ }
+
+ private static void addSelfSymlinkIfNecessary(
+ RefDatabase refDatabase, Map<String, Ref> advertisedRefs, Account.Id accountId)
+ throws ServiceMayNotContinueException {
+ String refName = RefNames.refsUsers(accountId);
+ try {
+ Ref r = refDatabase.exactRef(refName);
+ if (r == null) {
+ logger.atWarning().log("User ref %s not found", refName);
+ return;
+ }
+
+ SymbolicRef s = new SymbolicRef(RefNames.REFS_USERS_SELF, r);
+ advertisedRefs.put(s.getName(), s);
+ logger.atFinest().log("Added %s as alias for user ref %s", RefNames.REFS_USERS_SELF, refName);
+ } catch (IOException e) {
+ throw new ServiceMayNotContinueException(e);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/git/WorkQueue.java b/java/com/google/gerrit/server/git/WorkQueue.java
index 4522b0d80f..f2a0ff1a8c 100644
--- a/java/com/google/gerrit/server/git/WorkQueue.java
+++ b/java/com/google/gerrit/server/git/WorkQueue.java
@@ -18,11 +18,11 @@ import static java.util.stream.Collectors.toList;
import com.google.common.base.CaseFormat;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.ScheduleConfig.Schedule;
import com.google.gerrit.server.logging.LoggingContext;
diff --git a/java/com/google/gerrit/server/git/meta/MetaDataUpdate.java b/java/com/google/gerrit/server/git/meta/MetaDataUpdate.java
index 97beefdeb2..e90f58b203 100644
--- a/java/com/google/gerrit/server/git/meta/MetaDataUpdate.java
+++ b/java/com/google/gerrit/server/git/meta/MetaDataUpdate.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.git.meta;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
diff --git a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
index 86942998a6..8ab2779a45 100644
--- a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
+++ b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
@@ -19,8 +19,11 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.MoreObjects;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.git.GitUpdateFailureException;
import com.google.gerrit.git.LockFailureException;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.git.ObjectIds;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.util.CommitMessageUtil;
@@ -114,7 +117,7 @@ public abstract class VersionedMetaData {
/** @return revision of the metadata that was loaded. */
@Nullable
public ObjectId getRevision() {
- return revision != null ? revision.copy() : null;
+ return ObjectIds.copyOrNull(revision);
}
/**
@@ -433,18 +436,18 @@ public abstract class VersionedMetaData {
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
default:
- throw new IOException(errorMsg(ru, db.getDirectory()));
+ throw new GitUpdateFailureException(errorMsg(ru, db.getDirectory()), ru);
}
}
-
- private String errorMsg(RefUpdate ru, File location) {
- return String.format(
- "Cannot update %s in %s: %s (%s)",
- ru.getName(), location, ru.getResult(), ru.getRefLogMessage());
- }
};
}
+ private String errorMsg(RefUpdate ru, File location) {
+ return String.format(
+ "Cannot update %s in %s: %s (%s)",
+ ru.getName(), location, ru.getResult(), ru.getRefLogMessage());
+ }
+
protected DirCache readTree(RevTree tree)
throws IOException, MissingObjectException, IncorrectObjectTypeException {
DirCache dc = DirCache.newInCore();
@@ -494,8 +497,13 @@ public abstract class VersionedMetaData {
try (TraceTimer timer =
TraceContext.newTimer(
- "Read file '%s' from ref '%s' of project '%s' from revision '%s'",
- fileName, getRefName(), projectName, revision.name());
+ "Read file",
+ Metadata.builder()
+ .projectName(projectName.get())
+ .noteDbRefName(getRefName())
+ .revision(revision.name())
+ .noteDbFilePath(fileName)
+ .build());
TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree())) {
if (tw != null) {
ObjectLoader obj = reader.open(tw.getObjectId(0), Constants.OBJ_BLOB);
@@ -570,7 +578,12 @@ public abstract class VersionedMetaData {
protected void saveFile(String fileName, byte[] raw) throws IOException {
try (TraceTimer timer =
TraceContext.newTimer(
- "Save file '%s' in ref '%s' of project '%s'", fileName, getRefName(), projectName)) {
+ "Save file",
+ Metadata.builder()
+ .projectName(projectName.get())
+ .noteDbRefName(getRefName())
+ .noteDbFilePath(fileName)
+ .build())) {
DirCacheEditor editor = newTree.editor();
if (raw != null && 0 < raw.length) {
final ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, raw);
diff --git a/java/com/google/gerrit/server/git/receive/AllRefsWatcher.java b/java/com/google/gerrit/server/git/receive/AllRefsWatcher.java
index c092c432d5..13ae54aecb 100644
--- a/java/com/google/gerrit/server/git/receive/AllRefsWatcher.java
+++ b/java/com/google/gerrit/server/git/receive/AllRefsWatcher.java
@@ -20,7 +20,7 @@ import com.google.gerrit.server.git.HookUtil;
import java.util.Map;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.AdvertiseRefsHook;
-import org.eclipse.jgit.transport.BaseReceivePack;
+import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.UploadPack;
@@ -34,7 +34,7 @@ class AllRefsWatcher implements AdvertiseRefsHook {
private Map<String, Ref> allRefs;
@Override
- public void advertiseRefs(BaseReceivePack rp) throws ServiceMayNotContinueException {
+ public void advertiseRefs(ReceivePack rp) throws ServiceMayNotContinueException {
allRefs = HookUtil.ensureAllRefsAdvertised(rp);
}
diff --git a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
index da2887f907..68d2010a3f 100644
--- a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
@@ -21,6 +21,8 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.data.Capable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.metrics.Counter0;
@@ -30,18 +32,18 @@ import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Histogram1;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer1;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.ReceiveCommitsExecutor;
-import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
import com.google.gerrit.server.git.MultiProgressMonitor;
+import com.google.gerrit.server.git.PermissionAwareRepositoryManager;
import com.google.gerrit.server.git.ProjectRunnable;
import com.google.gerrit.server.git.TransferConfig;
+import com.google.gerrit.server.git.UsersSelfAdvertiseRefsHook;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
@@ -62,7 +64,6 @@ import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.name.Named;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -70,8 +71,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.AdvertiseRefsHook;
-import org.eclipse.jgit.transport.AdvertiseRefsHookChain;
import org.eclipse.jgit.transport.PreReceiveHook;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
@@ -203,11 +202,20 @@ public class AsyncReceiveCommits implements PreReceiveHook {
@Inject
Metrics(MetricMaker metricMaker) {
+ // For the changes metric the push type field is never set to PushType.NORMAL, hence it is not
+ // mentioned in the field description.
changes =
metricMaker.newHistogram(
"receivecommits/changes_per_push",
new Description("number of changes uploaded in a single push.").setCumulative(),
- Field.ofEnum(PushType.class, "type", "type of push (create/replace, autoclose)"));
+ Field.ofEnum(PushType.class, "type", Metadata.Builder::pushType)
+ .description("type of push (create/replace, autoclose)")
+ .build());
+
+ Field<PushType> pushTypeField =
+ Field.ofEnum(PushType.class, "type", Metadata.Builder::pushType)
+ .description("type of push (create/replace, autoclose, normal)")
+ .build();
latencyPerChange =
metricMaker.newTimer(
@@ -217,7 +225,7 @@ public class AsyncReceiveCommits implements PreReceiveHook {
+ "(Only includes pushes which contain changes.)")
.setUnit(Units.MILLISECONDS)
.setCumulative(),
- Field.ofEnum(PushType.class, "type", "type of push (create/replace, autoclose)"));
+ pushTypeField);
latencyPerPush =
metricMaker.newTimer(
@@ -225,8 +233,7 @@ public class AsyncReceiveCommits implements PreReceiveHook {
new Description("processing delay for a processing single push")
.setUnit(Units.MILLISECONDS)
.setCumulative(),
- Field.ofEnum(
- PushType.class, "type", "type of push (create/replace, autoclose, normal)"));
+ pushTypeField);
timeouts =
metricMaker.newCounter(
@@ -262,6 +269,8 @@ public class AsyncReceiveCommits implements PreReceiveHook {
ContributorAgreementsChecker contributorAgreements,
Metrics metrics,
QuotaBackend quotaBackend,
+ UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook,
+ AllUsersName allUsersName,
@Named(TIMEOUT_NAME) long timeoutMillis,
@Assisted ProjectState projectState,
@Assisted IdentifiedUser user,
@@ -277,9 +286,12 @@ public class AsyncReceiveCommits implements PreReceiveHook {
this.user = user;
this.repo = repo;
this.metrics = metrics;
-
+ // If the user lacks READ permission, some references may be filtered and hidden from view.
+ // Check objects mentioned inside the incoming pack file are reachable from visible refs.
Project.NameKey projectName = projectState.getNameKey();
- receivePack = new ReceivePack(repo);
+ this.perm = permissionBackend.user(user).project(projectName);
+
+ receivePack = new ReceivePack(PermissionAwareRepositoryManager.wrap(repo, perm));
receivePack.setAllowCreates(true);
receivePack.setAllowDeletes(true);
receivePack.setAllowNonFastForwards(true);
@@ -292,9 +304,6 @@ public class AsyncReceiveCommits implements PreReceiveHook {
receivePack.setPreReceiveHook(this);
receivePack.setPostReceiveHook(lazyPostReceive.create(user, projectName));
- // If the user lacks READ permission, some references may be filtered and hidden from view.
- // Check objects mentioned inside the incoming pack file are reachable from visible refs.
- this.perm = permissionBackend.user(user).project(projectName);
try {
projectState.checkStatePermitsRead();
this.perm.check(ProjectPermission.READ);
@@ -303,19 +312,14 @@ public class AsyncReceiveCommits implements PreReceiveHook {
receiveConfig.checkReferencedObjectsAreReachable);
}
- List<AdvertiseRefsHook> advHooks = new ArrayList<>(4);
allRefsWatcher = new AllRefsWatcher();
- advHooks.add(allRefsWatcher);
- advHooks.add(
- new DefaultAdvertiseRefsHook(perm, RefFilterOptions.builder().setFilterMeta(true).build()));
- advHooks.add(new ReceiveCommitsAdvertiseRefsHook(queryProvider, projectName));
- advHooks.add(new HackPushNegotiateHook());
- receivePack.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks));
-
+ receivePack.setAdvertiseRefsHook(
+ ReceiveCommitsAdvertiseRefsHookChain.create(
+ allRefsWatcher, usersSelfAdvertiseRefsHook, allUsersName, queryProvider, projectName));
resultChangeIds = new ResultChangeIds();
receiveCommits =
factory.create(
- projectState, user, receivePack, allRefsWatcher, messageSender, resultChangeIds);
+ projectState, user, receivePack, repo, allRefsWatcher, messageSender, resultChangeIds);
receiveCommits.init();
QuotaResponse.Aggregated availableTokens =
quotaBackend.user(user).project(projectName).availableTokens(REPOSITORY_SIZE_GROUP);
diff --git a/java/com/google/gerrit/server/git/receive/BUILD b/java/com/google/gerrit/server/git/receive/BUILD
index b1bf933374..d89bb6358a 100644
--- a/java/com/google/gerrit/server/git/receive/BUILD
+++ b/java/com/google/gerrit/server/git/receive/BUILD
@@ -7,21 +7,22 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
+ "//java/com/google/gerrit/git",
"//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/util/cli",
"//lib:args4j",
"//lib:guava",
+ "//lib:jgit",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java b/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java
index a2d8e94406..7b5f90bdc0 100644
--- a/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java
+++ b/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java
@@ -14,27 +14,29 @@
package com.google.gerrit.server.git.receive;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.events.CommitReceivedEvent;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.git.validators.CommitValidators;
-import com.google.gerrit.server.git.validators.ValidationMessage;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
-import java.util.List;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -48,12 +50,29 @@ public class BranchCommitValidator {
private final IdentifiedUser user;
private final PermissionBackend.ForProject permissions;
private final Project project;
- private final Branch.NameKey branch;
+ private final BranchNameKey branch;
private final SshInfo sshInfo;
interface Factory {
BranchCommitValidator create(
- ProjectState projectState, Branch.NameKey branch, IdentifiedUser user);
+ ProjectState projectState, BranchNameKey branch, IdentifiedUser user);
+ }
+
+ /** A boolean validation status and a list of additional messages. */
+ @AutoValue
+ abstract static class Result {
+ static Result create(boolean isValid, ImmutableList<CommitValidationMessage> messages) {
+ return new AutoValue_BranchCommitValidator_Result(isValid, messages);
+ }
+
+ /** Whether the commit is valid. */
+ abstract boolean isValid();
+
+ /**
+ * A list of messages related to the validation. Messages may be present regardless of the
+ * {@link #isValid()} status.
+ */
+ abstract ImmutableList<CommitValidationMessage> messages();
}
@Inject
@@ -62,7 +81,7 @@ public class BranchCommitValidator {
PermissionBackend permissionBackend,
SshInfo sshInfo,
@Assisted ProjectState projectState,
- @Assisted Branch.NameKey branch,
+ @Assisted BranchNameKey branch,
@Assisted IdentifiedUser user) {
this.sshInfo = sshInfo;
this.user = user;
@@ -80,17 +99,17 @@ public class BranchCommitValidator {
* @param commit the commit being validated.
* @param isMerged whether this is a merge commit created by magicBranch --merge option
* @param change the change for which this is a new patchset.
+ * @return The validation {@link Result}.
*/
- public boolean validCommit(
+ Result validateCommit(
ObjectReader objectReader,
ReceiveCommand cmd,
RevCommit commit,
boolean isMerged,
- List<ValidationMessage> messages,
NoteMap rejectCommits,
@Nullable Change change)
throws IOException {
- return validCommit(objectReader, cmd, commit, isMerged, messages, rejectCommits, change, false);
+ return validateCommit(objectReader, cmd, commit, isMerged, rejectCommits, change, false);
}
/**
@@ -102,55 +121,63 @@ public class BranchCommitValidator {
* @param isMerged whether this is a merge commit created by magicBranch --merge option
* @param change the change for which this is a new patchset.
* @param skipValidation whether 'skip-validation' was requested.
+ * @return The validation {@link Result}.
*/
- public boolean validCommit(
+ Result validateCommit(
ObjectReader objectReader,
ReceiveCommand cmd,
RevCommit commit,
boolean isMerged,
- List<ValidationMessage> messages,
NoteMap rejectCommits,
@Nullable Change change,
boolean skipValidation)
throws IOException {
- try (CommitReceivedEvent receiveEvent =
- new CommitReceivedEvent(cmd, project, branch.get(), objectReader, commit, user)) {
- CommitValidators validators;
- if (isMerged) {
- validators =
- commitValidatorsFactory.forMergedCommits(permissions, branch, user.asIdentifiedUser());
- } else {
- validators =
- commitValidatorsFactory.forReceiveCommits(
- permissions,
- branch,
- user.asIdentifiedUser(),
- sshInfo,
- rejectCommits,
- receiveEvent.revWalk,
- change,
- skipValidation);
- }
+ try (TraceTimer traceTimer = TraceContext.newTimer("BranchCommitValidator#validateCommit")) {
+ ImmutableList.Builder<CommitValidationMessage> messages = new ImmutableList.Builder<>();
+ try (CommitReceivedEvent receiveEvent =
+ new CommitReceivedEvent(cmd, project, branch.branch(), objectReader, commit, user)) {
+ CommitValidators validators;
+ if (isMerged) {
+ validators =
+ commitValidatorsFactory.forMergedCommits(
+ permissions, branch, user.asIdentifiedUser());
+ } else {
+ validators =
+ commitValidatorsFactory.forReceiveCommits(
+ permissions,
+ branch,
+ user.asIdentifiedUser(),
+ sshInfo,
+ rejectCommits,
+ receiveEvent.revWalk,
+ change,
+ skipValidation);
+ }
- for (CommitValidationMessage m : validators.validate(receiveEvent)) {
- messages.add(
- new CommitValidationMessage(messageForCommit(commit, m.getMessage()), m.getType()));
+ for (CommitValidationMessage m : validators.validate(receiveEvent)) {
+ messages.add(
+ new CommitValidationMessage(
+ messageForCommit(commit, m.getMessage(), objectReader), m.getType()));
+ }
+ } catch (CommitValidationException e) {
+ logger.atFine().log("Commit validation failed on %s", commit.name());
+ for (CommitValidationMessage m : e.getMessages()) {
+ // The non-error messages may contain background explanation for the
+ // fatal error, so have to preserve all messages.
+ messages.add(
+ new CommitValidationMessage(
+ messageForCommit(commit, m.getMessage(), objectReader), m.getType()));
+ }
+ cmd.setResult(
+ REJECTED_OTHER_REASON, messageForCommit(commit, e.getMessage(), objectReader));
+ return Result.create(false, messages.build());
}
- } catch (CommitValidationException e) {
- logger.atFine().log("Commit validation failed on %s", commit.name());
- for (CommitValidationMessage m : e.getMessages()) {
- // The non-error messages may contain background explanation for the
- // fatal error, so have to preserve all messages.
- messages.add(
- new CommitValidationMessage(messageForCommit(commit, m.getMessage()), m.getType()));
- }
- cmd.setResult(REJECTED_OTHER_REASON, messageForCommit(commit, e.getMessage()));
- return false;
+ return Result.create(true, messages.build());
}
- return true;
}
- private String messageForCommit(RevCommit c, String msg) {
- return String.format("commit %s: %s", c.abbreviate(RevId.ABBREV_LEN).name(), msg);
+ private String messageForCommit(RevCommit c, String msg, ObjectReader objectReader)
+ throws IOException {
+ return String.format("commit %s: %s", abbreviateName(c, objectReader), msg);
}
}
diff --git a/java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java b/java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java
index 36d0eb7add..72483af881 100644
--- a/java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java
+++ b/java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java
@@ -18,18 +18,17 @@ import static java.util.stream.Collectors.toMap;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.git.ObjectIds;
import java.io.IOException;
import java.util.Collection;
-import java.util.Collections;
import java.util.Map;
import java.util.Set;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.AdvertiseRefsHook;
-import org.eclipse.jgit.transport.BaseReceivePack;
+import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.UploadPack;
@@ -49,7 +48,7 @@ public class HackPushNegotiateHook implements AdvertiseRefsHook {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/** Size of an additional ".have" line. */
- private static final int HAVE_LINE_LEN = 4 + Constants.OBJECT_ID_STRING_LENGTH + 1 + 5 + 1;
+ private static final int HAVE_LINE_LEN = 4 + ObjectIds.STR_LEN + 1 + 5 + 1;
/**
* Maximum number of bytes to "waste" in the advertisement with a peek at this repository's
@@ -73,9 +72,8 @@ public class HackPushNegotiateHook implements AdvertiseRefsHook {
throw new UnsupportedOperationException("HackPushNegotiateHook cannot be used for UploadPack");
}
- @SuppressWarnings("deprecation")
@Override
- public void advertiseRefs(BaseReceivePack rp) throws ServiceMayNotContinueException {
+ public void advertiseRefs(ReceivePack rp) throws ServiceMayNotContinueException {
Map<String, Ref> r = rp.getAdvertisedRefs();
if (r == null) {
try {
@@ -91,38 +89,32 @@ public class HackPushNegotiateHook implements AdvertiseRefsHook {
rp.setAdvertisedRefs(r, history(r.values(), rp));
}
- private Set<ObjectId> history(Collection<Ref> refs, BaseReceivePack rp) {
+ private Set<ObjectId> history(Collection<Ref> refs, ReceivePack rp) {
Set<ObjectId> alreadySending = rp.getAdvertisedObjects();
- if (alreadySending.isEmpty()) {
- alreadySending = idsOf(refs);
- }
-
- int max = MAX_HISTORY - Math.max(0, alreadySending.size() - refs.size());
- if (max <= 0) {
- return Collections.emptySet();
+ if (MAX_HISTORY <= alreadySending.size()) {
+ return alreadySending;
}
// Scan history until the advertisement is full.
- @SuppressWarnings("deprecation")
RevWalk rw = rp.getRevWalk();
rw.reset();
try {
- for (Ref ref : refs) {
+ Set<ObjectId> tips = idsOf(refs);
+ for (ObjectId tip : tips) {
try {
- if (ref.getObjectId() != null) {
- rw.markStart(rw.parseCommit(ref.getObjectId()));
- }
+ rw.markStart(rw.parseCommit(tip));
} catch (IOException badCommit) {
continue;
}
}
- Set<ObjectId> history = Sets.newHashSetWithExpectedSize(max);
+ Set<ObjectId> history = Sets.newHashSetWithExpectedSize(MAX_HISTORY);
+ history.addAll(alreadySending);
try {
int stepCnt = 0;
- for (RevCommit c; history.size() < max && (c = rw.next()) != null; ) {
+ for (RevCommit c; history.size() < MAX_HISTORY && (c = rw.next()) != null; ) {
if (c.getParentCount() <= 1
- && !alreadySending.contains(c)
+ && !tips.contains(c)
&& (history.size() < BASE_COMMITS || (++stepCnt % STEP_COMMITS) == 0)) {
history.add(c);
}
diff --git a/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java b/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
index 17d4a38173..a19dbac486 100644
--- a/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
+++ b/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.git.receive;
import static com.google.gerrit.server.quota.QuotaGroupDefinitions.REPOSITORY_SIZE_GROUP;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.quota.QuotaBackend;
@@ -61,7 +61,7 @@ public class LazyPostReceiveHookChain implements PostReceiveHook {
@Override
public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
hooks.runEach(h -> h.onPostReceive(rp, commands));
- if (affectsSize(rp, commands)) {
+ if (affectsSize(rp)) {
QuotaResponse.Aggregated a =
quotaBackend
.user(user)
@@ -78,21 +78,7 @@ public class LazyPostReceiveHookChain implements PostReceiveHook {
}
}
- public static boolean affectsSize(ReceivePack rp, Collection<ReceiveCommand> commands) {
- long packSize;
- try {
- packSize = rp.getPackSize();
- } catch (IllegalStateException e) {
- // No pack was received, i.e. ref deletion or wind back
- return false;
- }
- if (packSize > 0L) {
- for (ReceiveCommand cmd : commands) {
- if (cmd.getType() != ReceiveCommand.Type.DELETE) {
- return true;
- }
- }
- }
- return false;
+ public static boolean affectsSize(ReceivePack rp) {
+ return rp.hasReceivedPack() && rp.getPackSize() > 0L;
}
}
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 2b127b63c1..b0eac616dd 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -15,13 +15,13 @@
package com.google.gerrit.server.git.receive;
import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.flogger.LazyArgs.lazy;
-import static com.google.gerrit.common.FooterConstants.CHANGE_ID;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
-import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static com.google.gerrit.entities.RefNames.REFS_CHANGES;
+import static com.google.gerrit.entities.RefNames.isConfigRef;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static com.google.gerrit.server.change.HashtagsUtil.cleanupHashtag;
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
import static com.google.gerrit.server.git.receive.ReceiveConstants.COMMAND_REJECTION_MESSAGE_FOOTER;
@@ -40,7 +40,6 @@ import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
@@ -48,6 +47,7 @@ import com.google.common.base.Throwables;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
@@ -62,37 +62,48 @@ import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetInfo;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.HashtagsInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
+import com.google.gerrit.extensions.api.changes.NotifyInfo;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.extensions.validators.CommentForValidation;
+import com.google.gerrit.extensions.validators.CommentForValidation.CommentType;
+import com.google.gerrit.extensions.validators.CommentValidationFailure;
+import com.google.gerrit.extensions.validators.CommentValidator;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CreateGroupPermissionSyncer;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.PublishCommentUtil;
+import com.google.gerrit.server.RequestInfo;
+import com.google.gerrit.server.RequestListener;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.NotifyResolver;
@@ -118,8 +129,12 @@ import com.google.gerrit.server.git.validators.RefOperationValidationException;
import com.google.gerrit.server.git.validators.RefOperationValidators;
import com.google.gerrit.server.git.validators.ValidationMessage;
import com.google.gerrit.server.index.change.ChangeIndexer;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.PerformanceLogContext;
+import com.google.gerrit.server.logging.PerformanceLogger;
import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.mail.MailUtil.MailRecipients;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.Sequences;
@@ -180,11 +195,12 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
-import java.util.regex.Matcher;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -223,9 +239,6 @@ import org.kohsuke.args4j.Option;
class ReceiveCommits {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private static final String CODE_REVIEW_ERROR =
- "You need 'Push' rights to upload code review requests.\n"
- + "Verify that you are pushing to the right branch.";
private static final String CANNOT_DELETE_CHANGES = "Cannot delete from '" + REFS_CHANGES + "'";
private static final String CANNOT_DELETE_CONFIG =
"Cannot delete project configuration from '" + RefNames.REFS_CONFIG + "'";
@@ -236,6 +249,7 @@ class ReceiveCommits {
ProjectState projectState,
IdentifiedUser user,
ReceivePack receivePack,
+ Repository repository,
AllRefsWatcher allRefsWatcher,
MessageSender messageSender,
ResultChangeIds resultChangeIds);
@@ -276,16 +290,14 @@ class ReceiveCommits {
}
}
- private static final Function<Exception, RestApiException> INSERT_EXCEPTION =
- input -> {
- if (input instanceof RestApiException) {
- return (RestApiException) input;
- } else if ((input instanceof ExecutionException)
- && (input.getCause() instanceof RestApiException)) {
- return (RestApiException) input.getCause();
- }
- return new RestApiException("Error inserting change/patchset", input);
- };
+ private static RestApiException asRestApiException(Exception e) {
+ if (e instanceof RestApiException) {
+ return (RestApiException) e;
+ } else if ((e instanceof ExecutionException) && (e.getCause() instanceof RestApiException)) {
+ return (RestApiException) e.getCause();
+ }
+ return new RestApiException("Error inserting change/patchset", e);
+ }
// ReceiveCommits has a lot of fields, sorry. Here and in the constructor they are split up
// somewhat, and kept sorted lexicographically within sections, except where later assignments
@@ -301,7 +313,10 @@ class ReceiveCommits {
private final ChangeNotes.Factory notesFactory;
private final ChangeReportFormatter changeFormatter;
private final CmdLineParser.Factory optionParserFactory;
+ private final CommentsUtil commentsUtil;
+ private final PluginSetContext<CommentValidator> commentValidators;
private final BranchCommitValidator.Factory commitValidatorFactory;
+ private final Config config;
private final CreateGroupPermissionSyncer createGroupPermissionSyncer;
private final CreateRefControl createRefControl;
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
@@ -309,6 +324,7 @@ class ReceiveCommits {
private final MergedByPushOp.Factory mergedByPushOpFactory;
private final PatchSetInfoFactory patchSetInfoFactory;
private final PatchSetUtil psUtil;
+ private final DynamicSet<PerformanceLogger> performanceLoggers;
private final PermissionBackend permissionBackend;
private final ProjectCache projectCache;
private final Provider<InternalChangeQuery> queryProvider;
@@ -317,6 +333,7 @@ class ReceiveCommits {
private final ReceiveConfig receiveConfig;
private final RefOperationValidators.Factory refValidatorsFactory;
private final ReplaceOp.Factory replaceOpFactory;
+ private final PluginSetContext<RequestListener> requestListeners;
private final RetryHelper retryHelper;
private final RequestScopePropagator requestScopePropagator;
private final Sequences seq;
@@ -334,7 +351,6 @@ class ReceiveCommits {
// Immutable fields derived from constructor arguments.
private final boolean allowProjectOwnersToChangeParent;
- private final boolean allowPushToRefsChanges;
private final LabelTypes labelTypes;
private final NoteMap rejectCommits;
private final PermissionBackend.ForProject permissions;
@@ -343,7 +359,7 @@ class ReceiveCommits {
// Collections populated during processing.
private final List<UpdateGroupsRequest> updateGroups;
- private final List<ValidationMessage> messages;
+ private final Queue<ValidationMessage> messages;
/** Multimap of error text to refnames that produced that error. */
private final ListMultimap<String, String> errors;
@@ -363,6 +379,7 @@ class ReceiveCommits {
private MessageSender messageSender;
private ResultChangeIds resultChangeIds;
+ private ImmutableMap<String, String> loggingTags;
@Inject
ReceiveCommits(
@@ -370,21 +387,24 @@ class ReceiveCommits {
AllProjectsName allProjectsName,
BatchUpdate.Factory batchUpdateFactory,
ProjectConfig.Factory projectConfigFactory,
- @GerritServerConfig Config cfg,
+ @GerritServerConfig Config config,
ChangeEditUtil editUtil,
ChangeIndexer indexer,
ChangeInserter.Factory changeInserterFactory,
ChangeNotes.Factory notesFactory,
DynamicItem<ChangeReportFormatter> changeFormatterProvider,
CmdLineParser.Factory optionParserFactory,
+ CommentsUtil commentsUtil,
BranchCommitValidator.Factory commitValidatorFactory,
CreateGroupPermissionSyncer createGroupPermissionSyncer,
CreateRefControl createRefControl,
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
PluginSetContext<ReceivePackInitializer> initializers,
+ PluginSetContext<CommentValidator> commentValidators,
MergedByPushOp.Factory mergedByPushOpFactory,
PatchSetInfoFactory patchSetInfoFactory,
PatchSetUtil psUtil,
+ DynamicSet<PerformanceLogger> performanceLoggers,
PermissionBackend permissionBackend,
ProjectCache projectCache,
Provider<InternalChangeQuery> queryProvider,
@@ -393,6 +413,7 @@ class ReceiveCommits {
ReceiveConfig receiveConfig,
RefOperationValidators.Factory refValidatorsFactory,
ReplaceOp.Factory replaceOpFactory,
+ PluginSetContext<RequestListener> requestListeners,
RetryHelper retryHelper,
RequestScopePropagator requestScopePropagator,
Sequences seq,
@@ -403,6 +424,7 @@ class ReceiveCommits {
@Assisted ProjectState projectState,
@Assisted IdentifiedUser user,
@Assisted ReceivePack rp,
+ @Assisted Repository repository,
@Assisted AllRefsWatcher allRefsWatcher,
@Nullable @Assisted MessageSender messageSender,
@Assisted ResultChangeIds resultChangeIds)
@@ -413,7 +435,10 @@ class ReceiveCommits {
this.batchUpdateFactory = batchUpdateFactory;
this.changeFormatter = changeFormatterProvider.get();
this.changeInserterFactory = changeInserterFactory;
+ this.commentsUtil = commentsUtil;
+ this.commentValidators = commentValidators;
this.commitValidatorFactory = commitValidatorFactory;
+ this.config = config;
this.createRefControl = createRefControl;
this.createGroupPermissionSyncer = createGroupPermissionSyncer;
this.editUtil = editUtil;
@@ -430,10 +455,12 @@ class ReceiveCommits {
this.pluginConfigEntries = pluginConfigEntries;
this.projectCache = projectCache;
this.psUtil = psUtil;
+ this.performanceLoggers = performanceLoggers;
this.queryProvider = queryProvider;
this.receiveConfig = receiveConfig;
this.refValidatorsFactory = refValidatorsFactory;
this.replaceOpFactory = replaceOpFactory;
+ this.requestListeners = requestListeners;
this.retryHelper = retryHelper;
this.requestScopePropagator = requestScopePropagator;
this.seq = seq;
@@ -447,24 +474,25 @@ class ReceiveCommits {
this.projectState = projectState;
this.user = user;
this.receivePack = rp;
+ // This repository instance in unwrapped, while the repository instance in
+ // receivePack.getRepo() is wrapped in PermissionAwareRepository instance.
+ this.repo = repository;
// Immutable fields derived from constructor arguments.
- allowPushToRefsChanges = cfg.getBoolean("receive", "allowPushToRefsChanges", false);
- repo = rp.getRepository();
project = projectState.getProject();
labelTypes = projectState.getLabelTypes();
permissions = permissionBackend.user(user).project(project.getNameKey());
- rejectCommits = BanCommit.loadRejectCommitsMap(rp.getRepository(), rp.getRevWalk());
+ rejectCommits = BanCommit.loadRejectCommitsMap(repo, rp.getRevWalk());
// Collections populated during processing.
errors = MultimapBuilder.linkedHashKeys().arrayListValues().build();
- messages = new ArrayList<>();
+ messages = new ConcurrentLinkedQueue<>();
pushOptions = LinkedListMultimap.create();
replaceByChange = new LinkedHashMap<>();
updateGroups = new ArrayList<>();
this.allowProjectOwnersToChangeParent =
- cfg.getBoolean("receive", "allowProjectOwnersToChangeParent", false);
+ config.getBoolean("receive", "allowProjectOwnersToChangeParent", false);
// Other settings populated during processing.
newChangeForAllNotInTarget =
@@ -473,6 +501,7 @@ class ReceiveCommits {
// Handles for outputting back over the wire to the end user.
this.messageSender = messageSender != null ? messageSender : new ReceivePackMessageSender();
this.resultChangeIds = resultChangeIds;
+ this.loggingTags = ImmutableMap.of();
}
void init() {
@@ -499,125 +528,148 @@ class ReceiveCommits {
addMessage(error, ValidationMessage.Type.ERROR);
}
+ /**
+ * Sends all messages which have been collected while processing the push to the client.
+ *
+ * <p><strong>Attention:</strong>{@link AsyncReceiveCommits} may call this method while {@link
+ * #processCommands(Collection, MultiProgressMonitor)} is still running (if the execution of
+ * processCommands takes too long and AsyncReceiveCommits gets a timeout). This means that local
+ * variables that are accessed in this method must be thread-safe (otherwise we may hit a {@link
+ * java.util.ConcurrentModificationException} if we read a variable here that at the same time is
+ * updated by the background thread that still executes processCommands).
+ */
void sendMessages() {
- for (ValidationMessage m : messages) {
- String msg = m.getType().getPrefix() + m.getMessage();
+ try (TraceContext traceContext =
+ TraceContext.newTrace(
+ loggingTags.containsKey(RequestId.Type.TRACE_ID.name()),
+ loggingTags.get(RequestId.Type.TRACE_ID.name()),
+ (tagName, traceId) -> {})) {
+ loggingTags.forEach((tagName, tagValue) -> traceContext.addTag(tagName, tagValue));
+
+ for (ValidationMessage m : messages) {
+ String msg = m.getType().getPrefix() + m.getMessage();
+ logger.atFine().log("Sending message: %s", msg);
- // Avoid calling sendError which will add its own error: prefix.
- messageSender.sendMessage(msg);
+ // Avoid calling sendError which will add its own error: prefix.
+ messageSender.sendMessage(msg);
+ }
}
}
void processCommands(Collection<ReceiveCommand> commands, MultiProgressMonitor progress) {
- Task commandProgress = progress.beginSubTask("refs", UNKNOWN);
- commands = commands.stream().map(c -> wrapReceiveCommand(c, commandProgress)).collect(toList());
- processCommandsUnsafe(commands, progress);
- rejectRemaining(commands, INTERNAL_SERVER_ERROR);
-
- // This sends error messages before the 'done' string of the progress monitor is sent.
- // Currently, the test framework relies on this ordering to understand if pushes completed
- // successfully.
- sendErrorMessages();
-
- commandProgress.end();
- progress.end();
- }
-
- // Process as many commands as possible, but may leave some commands in state NOT_ATTEMPTED.
- private void processCommandsUnsafe(
- Collection<ReceiveCommand> commands, MultiProgressMonitor progress) {
parsePushOptions();
+ int commandCount = commands.size();
try (TraceContext traceContext =
- TraceContext.newTrace(
- tracePushOption.isPresent(),
- tracePushOption.orElse(null),
- (tagName, traceId) -> addMessage(tagName + ": " + traceId))) {
+ TraceContext.newTrace(
+ tracePushOption.isPresent(),
+ tracePushOption.orElse(null),
+ (tagName, traceId) -> addMessage(tagName + ": " + traceId));
+ TraceTimer traceTimer =
+ newTimer("processCommands", Metadata.builder().resourceCount(commandCount));
+ PerformanceLogContext performanceLogContext =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ RequestInfo requestInfo =
+ RequestInfo.builder(RequestInfo.RequestType.GIT_RECEIVE, user, traceContext)
+ .project(project.getNameKey())
+ .build();
+ requestListeners.runEach(l -> l.onRequest(requestInfo));
traceContext.addTag(RequestId.Type.RECEIVE_ID, new RequestId(project.getNameKey().get()));
- logger.atFinest().log("Calling user: %s", user.getLoggableName());
-
// Log the push options here, rather than in parsePushOptions(), so that they are included
// into the trace if tracing is enabled.
logger.atFine().log("push options: %s", receivePack.getPushOptions());
- if (!projectState.getProject().getState().permitsWrite()) {
- for (ReceiveCommand cmd : commands) {
- reject(cmd, "prohibited by Gerrit: project state does not permit write");
- }
- return;
- }
+ Task commandProgress = progress.beginSubTask("refs", UNKNOWN);
+ commands =
+ commands.stream().map(c -> wrapReceiveCommand(c, commandProgress)).collect(toList());
+ processCommandsUnsafe(commands, progress);
+ rejectRemaining(commands, INTERNAL_SERVER_ERROR);
+
+ // This sends error messages before the 'done' string of the progress monitor is sent.
+ // Currently, the test framework relies on this ordering to understand if pushes completed
+ // successfully.
+ sendErrorMessages();
- logger.atFine().log("Parsing %d commands", commands.size());
+ commandProgress.end();
+ progress.end();
+ loggingTags = traceContext.getTags();
+ logger.atFine().log("Processing commands done.");
+ }
+ }
- List<ReceiveCommand> magicCommands = new ArrayList<>();
- List<ReceiveCommand> directPatchSetPushCommands = new ArrayList<>();
- List<ReceiveCommand> regularCommands = new ArrayList<>();
+ // Process as many commands as possible, but may leave some commands in state NOT_ATTEMPTED.
+ private void processCommandsUnsafe(
+ Collection<ReceiveCommand> commands, MultiProgressMonitor progress) {
+ logger.atFine().log("Calling user: %s", user.getLoggableName());
+ logger.atFine().log("Groups: %s", user.getEffectiveGroups().getKnownGroups());
+ if (!projectState.getProject().getState().permitsWrite()) {
for (ReceiveCommand cmd : commands) {
- if (MagicBranch.isMagicBranch(cmd.getRefName())) {
- magicCommands.add(cmd);
- } else if (isDirectChangesPush(cmd.getRefName())) {
- directPatchSetPushCommands.add(cmd);
- } else {
- regularCommands.add(cmd);
- }
+ reject(cmd, "prohibited by Gerrit: project state does not permit write");
}
+ return;
+ }
- int commandTypes =
- (magicCommands.isEmpty() ? 0 : 1)
- + (directPatchSetPushCommands.isEmpty() ? 0 : 1)
- + (regularCommands.isEmpty() ? 0 : 1);
+ logger.atFine().log("Parsing %d commands", commands.size());
- if (commandTypes > 1) {
- rejectRemaining(commands, "cannot combine normal pushes and magic pushes");
- return;
+ List<ReceiveCommand> magicCommands = new ArrayList<>();
+ List<ReceiveCommand> regularCommands = new ArrayList<>();
+
+ for (ReceiveCommand cmd : commands) {
+ if (MagicBranch.isMagicBranch(cmd.getRefName())) {
+ magicCommands.add(cmd);
+ } else {
+ regularCommands.add(cmd);
}
+ }
- try {
- if (!regularCommands.isEmpty()) {
- handleRegularCommands(regularCommands, progress);
- return;
- }
+ int commandTypes = (magicCommands.isEmpty() ? 0 : 1) + (regularCommands.isEmpty() ? 0 : 1);
- for (ReceiveCommand cmd : directPatchSetPushCommands) {
- parseDirectChangesPush(cmd);
- }
+ if (commandTypes > 1) {
+ rejectRemaining(commands, "cannot combine normal pushes and magic pushes");
+ return;
+ }
- boolean first = true;
- for (ReceiveCommand cmd : magicCommands) {
- if (first) {
- parseMagicBranch(cmd);
- first = false;
- } else {
- reject(cmd, "duplicate request");
- }
- }
- } catch (PermissionBackendException | NoSuchProjectException | IOException err) {
- logger.atSevere().withCause(err).log("Failed to process refs in %s", project.getName());
+ try {
+ if (!regularCommands.isEmpty()) {
+ handleRegularCommands(regularCommands, progress);
return;
}
- Task newProgress = progress.beginSubTask("new", UNKNOWN);
- Task replaceProgress = progress.beginSubTask("updated", UNKNOWN);
-
- List<CreateRequest> newChanges = Collections.emptyList();
- if (magicBranch != null && magicBranch.cmd.getResult() == NOT_ATTEMPTED) {
- newChanges = selectNewAndReplacedChangesFromMagicBranch(newProgress);
+ boolean first = true;
+ for (ReceiveCommand cmd : magicCommands) {
+ if (first) {
+ parseMagicBranch(cmd);
+ first = false;
+ } else {
+ reject(cmd, "duplicate request");
+ }
}
+ } catch (PermissionBackendException | NoSuchProjectException | IOException err) {
+ logger.atSevere().withCause(err).log("Failed to process refs in %s", project.getName());
+ return;
+ }
- // Commit validation has already happened, so any changes without Change-Id are for the
- // deprecated feature.
- warnAboutMissingChangeId(newChanges);
- preparePatchSetsForReplace(newChanges);
- insertChangesAndPatchSets(newChanges, replaceProgress);
- newProgress.end();
- replaceProgress.end();
- queueSuccessMessages(newChanges);
+ Task newProgress = progress.beginSubTask("new", UNKNOWN);
+ Task replaceProgress = progress.beginSubTask("updated", UNKNOWN);
- logger.atFine().log(
- "Command results: %s",
- lazy(() -> commands.stream().map(ReceiveCommits::commandToString).collect(joining(","))));
+ List<CreateRequest> newChanges = Collections.emptyList();
+ if (magicBranch != null && magicBranch.cmd.getResult() == NOT_ATTEMPTED) {
+ newChanges = selectNewAndReplacedChangesFromMagicBranch(newProgress);
}
+
+ // Commit validation has already happened, so any changes without Change-Id are for the
+ // deprecated feature.
+ warnAboutMissingChangeId(newChanges);
+ preparePatchSetsForReplace(newChanges);
+ insertChangesAndPatchSets(newChanges, replaceProgress);
+ newProgress.end();
+ replaceProgress.end();
+ queueSuccessMessages(newChanges);
+
+ logger.atFine().log(
+ "Command results: %s",
+ lazy(() -> commands.stream().map(ReceiveCommits::commandToString).collect(joining(","))));
}
private void sendErrorMessages() {
@@ -633,67 +685,70 @@ class ReceiveCommits {
private void handleRegularCommands(List<ReceiveCommand> cmds, MultiProgressMonitor progress)
throws PermissionBackendException, IOException, NoSuchProjectException {
- resultChangeIds.setMagicPush(false);
- for (ReceiveCommand cmd : cmds) {
- parseRegularCommand(cmd);
- }
-
- try (BatchUpdate bu =
- batchUpdateFactory.create(
- project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
- ObjectInserter ins = repo.newObjectInserter();
- ObjectReader reader = ins.newReader();
- RevWalk rw = new RevWalk(reader)) {
- bu.setRepository(repo, rw, ins);
- bu.setRefLogMessage("push");
-
- int added = 0;
+ try (TraceTimer traceTimer =
+ newTimer("handleRegularCommands", Metadata.builder().resourceCount(cmds.size()))) {
+ resultChangeIds.setMagicPush(false);
for (ReceiveCommand cmd : cmds) {
- if (cmd.getResult() == NOT_ATTEMPTED) {
- bu.addRepoOnlyOp(new UpdateOneRefOp(cmd));
- added++;
+ parseRegularCommand(cmd);
+ }
+
+ try (BatchUpdate bu =
+ batchUpdateFactory.create(
+ project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
+ ObjectInserter ins = repo.newObjectInserter();
+ ObjectReader reader = ins.newReader();
+ RevWalk rw = new RevWalk(reader)) {
+ bu.setRepository(repo, rw, ins);
+ bu.setRefLogMessage("push");
+
+ int added = 0;
+ for (ReceiveCommand cmd : cmds) {
+ if (cmd.getResult() == NOT_ATTEMPTED) {
+ bu.addRepoOnlyOp(new UpdateOneRefOp(cmd));
+ added++;
+ }
}
+ logger.atFine().log("Added %d additional ref updates", added);
+ bu.execute();
+ } catch (UpdateException | RestApiException e) {
+ rejectRemaining(cmds, INTERNAL_SERVER_ERROR);
+ logger.atSevere().withCause(e).log("update failed:");
}
- logger.atFine().log("Added %d additional ref updates", added);
- bu.execute();
- } catch (UpdateException | RestApiException e) {
- rejectRemaining(cmds, INTERNAL_SERVER_ERROR);
- logger.atSevere().withCause(e).log("update failed:");
- }
- Set<Branch.NameKey> branches = new HashSet<>();
- for (ReceiveCommand c : cmds) {
- // Most post-update steps should happen in UpdateOneRefOp#postUpdate. The only steps that
- // should happen in this loop are things that can't happen within one BatchUpdate because
- // they involve kicking off an additional BatchUpdate.
- if (c.getResult() != OK) {
- continue;
- }
- if (isHead(c) || isConfig(c)) {
- switch (c.getType()) {
- case CREATE:
- case UPDATE:
- case UPDATE_NONFASTFORWARD:
- Task closeProgress = progress.beginSubTask("closed", UNKNOWN);
- autoCloseChanges(c, closeProgress);
- closeProgress.end();
- branches.add(new Branch.NameKey(project.getNameKey(), c.getRefName()));
- break;
+ Set<BranchNameKey> branches = new HashSet<>();
+ for (ReceiveCommand c : cmds) {
+ // Most post-update steps should happen in UpdateOneRefOp#postUpdate. The only steps that
+ // should happen in this loop are things that can't happen within one BatchUpdate because
+ // they involve kicking off an additional BatchUpdate.
+ if (c.getResult() != OK) {
+ continue;
+ }
+ if (isHead(c) || isConfig(c)) {
+ switch (c.getType()) {
+ case CREATE:
+ case UPDATE:
+ case UPDATE_NONFASTFORWARD:
+ Task closeProgress = progress.beginSubTask("closed", UNKNOWN);
+ autoCloseChanges(c, closeProgress);
+ closeProgress.end();
+ branches.add(BranchNameKey.create(project.getNameKey(), c.getRefName()));
+ break;
- case DELETE:
- break;
+ case DELETE:
+ break;
+ }
}
}
- }
- // Update superproject gitlinks if required.
- if (!branches.isEmpty()) {
- try (MergeOpRepoManager orm = ormProvider.get()) {
- orm.setContext(TimeUtil.nowTs(), user, NotifyResolver.Result.none());
- SubmoduleOp op = subOpFactory.create(branches, orm);
- op.updateSuperProjects();
- } catch (SubmoduleException e) {
- logger.atSevere().withCause(e).log("Can't update the superprojects");
+ // Update superproject gitlinks if required.
+ if (!branches.isEmpty()) {
+ try (MergeOpRepoManager orm = ormProvider.get()) {
+ orm.setContext(TimeUtil.nowTs(), user, NotifyResolver.Result.none());
+ SubmoduleOp op = subOpFactory.create(branches, orm);
+ op.updateSuperProjects();
+ } catch (SubmoduleException e) {
+ logger.atSevere().withCause(e).log("Can't update the superprojects");
+ }
}
}
}
@@ -755,7 +810,7 @@ class ReceiveCommits {
Boolean isPrivate = null;
Boolean wip = null;
if (!updated.isEmpty()) {
- edit = magicBranch != null && (magicBranch.edit || magicBranch.draft);
+ edit = magicBranch != null && magicBranch.edit;
if (magicBranch != null) {
if (magicBranch.isPrivate) {
isPrivate = true;
@@ -814,97 +869,102 @@ class ReceiveCommits {
}
private void insertChangesAndPatchSets(List<CreateRequest> newChanges, Task replaceProgress) {
- ReceiveCommand magicBranchCmd = magicBranch != null ? magicBranch.cmd : null;
- if (magicBranchCmd != null && magicBranchCmd.getResult() != NOT_ATTEMPTED) {
- logger.atWarning().log(
- "Skipping change updates on %s because ref update failed: %s %s",
- project.getName(),
- magicBranchCmd.getResult(),
- Strings.nullToEmpty(magicBranchCmd.getMessage()));
- return;
- }
-
- try (BatchUpdate bu =
- batchUpdateFactory.create(
- project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
- ObjectInserter ins = repo.newObjectInserter();
- ObjectReader reader = ins.newReader();
- RevWalk rw = new RevWalk(reader)) {
- bu.setRepository(repo, rw, ins);
- bu.setRefLogMessage("push");
- if (magicBranch != null) {
- bu.setNotify(magicBranch.getNotifyForNewChange());
+ try (TraceTimer traceTimer =
+ newTimer(
+ "insertChangesAndPatchSets", Metadata.builder().resourceCount(newChanges.size()))) {
+ ReceiveCommand magicBranchCmd = magicBranch != null ? magicBranch.cmd : null;
+ if (magicBranchCmd != null && magicBranchCmd.getResult() != NOT_ATTEMPTED) {
+ logger.atWarning().log(
+ "Skipping change updates on %s because ref update failed: %s %s",
+ project.getName(),
+ magicBranchCmd.getResult(),
+ Strings.nullToEmpty(magicBranchCmd.getMessage()));
+ return;
}
- logger.atFine().log("Adding %d replace requests", newChanges.size());
- for (ReplaceRequest replace : replaceByChange.values()) {
+ try (BatchUpdate bu =
+ batchUpdateFactory.create(
+ project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
+ ObjectInserter ins = repo.newObjectInserter();
+ ObjectReader reader = ins.newReader();
+ RevWalk rw = new RevWalk(reader)) {
+ bu.setRepository(repo, rw, ins);
+ bu.setRefLogMessage("push");
if (magicBranch != null) {
- bu.setNotifyHandling(replace.ontoChange, magicBranch.getNotifyHandling(replace.notes));
+ bu.setNotify(magicBranch.getNotifyForNewChange());
}
- replace.addOps(bu, replaceProgress);
- }
- logger.atFine().log("Adding %d create requests", newChanges.size());
- for (CreateRequest create : newChanges) {
- create.addOps(bu);
- }
+ logger.atFine().log("Adding %d replace requests", newChanges.size());
+ for (ReplaceRequest replace : replaceByChange.values()) {
+ if (magicBranch != null) {
+ bu.setNotifyHandling(replace.ontoChange, magicBranch.getNotifyHandling(replace.notes));
+ }
+ replace.addOps(bu, replaceProgress);
+ }
- logger.atFine().log("Adding %d group update requests", newChanges.size());
- updateGroups.forEach(r -> r.addOps(bu));
+ logger.atFine().log("Adding %d create requests", newChanges.size());
+ for (CreateRequest create : newChanges) {
+ create.addOps(bu);
+ }
- logger.atFine().log("Executing batch");
- try {
- bu.execute();
- } catch (UpdateException e) {
- throw INSERT_EXCEPTION.apply(e);
- }
+ logger.atFine().log("Adding %d group update requests", newChanges.size());
+ updateGroups.forEach(r -> r.addOps(bu));
- replaceByChange.values().stream()
- .forEach(req -> resultChangeIds.add(ResultChangeIds.Key.REPLACED, req.ontoChange));
- newChanges.stream()
- .forEach(req -> resultChangeIds.add(ResultChangeIds.Key.CREATED, req.changeId));
+ logger.atFine().log("Executing batch");
+ try {
+ bu.execute();
+ } catch (UpdateException e) {
+ throw asRestApiException(e);
+ }
- if (magicBranchCmd != null) {
- magicBranchCmd.setResult(OK);
- }
- for (ReplaceRequest replace : replaceByChange.values()) {
- String rejectMessage = replace.getRejectMessage();
- if (rejectMessage == null) {
- if (replace.inputCommand.getResult() == NOT_ATTEMPTED) {
- // Not necessarily the magic branch, so need to set OK on the original value.
- replace.inputCommand.setResult(OK);
+ replaceByChange.values().stream()
+ .forEach(req -> resultChangeIds.add(ResultChangeIds.Key.REPLACED, req.ontoChange));
+ newChanges.stream()
+ .forEach(req -> resultChangeIds.add(ResultChangeIds.Key.CREATED, req.changeId));
+
+ if (magicBranchCmd != null) {
+ magicBranchCmd.setResult(OK);
+ }
+ for (ReplaceRequest replace : replaceByChange.values()) {
+ String rejectMessage = replace.getRejectMessage();
+ if (rejectMessage == null) {
+ if (replace.inputCommand.getResult() == NOT_ATTEMPTED) {
+ // Not necessarily the magic branch, so need to set OK on the original value.
+ replace.inputCommand.setResult(OK);
+ }
+ } else {
+ logger.atFine().log("Rejecting due to message from ReplaceOp");
+ reject(replace.inputCommand, rejectMessage);
}
- } else {
- logger.atFine().log("Rejecting due to message from ReplaceOp");
- reject(replace.inputCommand, rejectMessage);
}
- }
-
- } catch (ResourceConflictException e) {
- addError(e.getMessage());
- reject(magicBranchCmd, "conflict");
- } catch (BadRequestException | UnprocessableEntityException | AuthException e) {
- logger.atFine().withCause(e).log("Rejecting due to client error");
- reject(magicBranchCmd, e.getMessage());
- } catch (RestApiException | IOException e) {
- logger.atSevere().withCause(e).log("Can't insert change/patch set for %s", project.getName());
- reject(magicBranchCmd, String.format("%s: %s", INTERNAL_SERVER_ERROR, e.getMessage()));
- }
- if (magicBranch != null && magicBranch.submit) {
- try {
- submit(newChanges, replaceByChange.values());
} catch (ResourceConflictException e) {
addError(e.getMessage());
reject(magicBranchCmd, "conflict");
- } catch (RestApiException
- | StorageException
- | UpdateException
- | IOException
- | ConfigInvalidException
- | PermissionBackendException e) {
- logger.atSevere().withCause(e).log("Error submitting changes to %s", project.getName());
- reject(magicBranchCmd, "error during submit");
+ } catch (BadRequestException | UnprocessableEntityException | AuthException e) {
+ logger.atFine().withCause(e).log("Rejecting due to client error");
+ reject(magicBranchCmd, e.getMessage());
+ } catch (RestApiException | IOException e) {
+ logger.atSevere().withCause(e).log(
+ "Can't insert change/patch set for %s", project.getName());
+ reject(magicBranchCmd, String.format("%s: %s", INTERNAL_SERVER_ERROR, e.getMessage()));
+ }
+
+ if (magicBranch != null && magicBranch.submit) {
+ try {
+ submit(newChanges, replaceByChange.values());
+ } catch (ResourceConflictException e) {
+ addError(e.getMessage());
+ reject(magicBranchCmd, "conflict");
+ } catch (RestApiException
+ | StorageException
+ | UpdateException
+ | IOException
+ | ConfigInvalidException
+ | PermissionBackendException e) {
+ logger.atSevere().withCause(e).log("Error submitting changes to %s", project.getName());
+ reject(magicBranchCmd, "error during submit");
+ }
}
}
}
@@ -938,7 +998,7 @@ class ReceiveCommits {
if (!noteDbValues.isEmpty()) {
// These semantics for duplicates/errors are somewhat arbitrary and may not match e.g. the
// CmdLineParser behavior used by MagicBranchInput.
- String value = noteDbValues.get(noteDbValues.size() - 1);
+ String value = Iterables.getLast(noteDbValues);
noteDbPushOption = NoteDbPushOption.parse(value);
if (!noteDbPushOption.isPresent()) {
addError("Invalid value in -o " + NoteDbPushOption.OPTION_NAME + "=" + value);
@@ -949,32 +1009,12 @@ class ReceiveCommits {
List<String> traceValues = pushOptions.get("trace");
if (!traceValues.isEmpty()) {
- String value = traceValues.get(traceValues.size() - 1);
- tracePushOption = Optional.of(value);
+ tracePushOption = Optional.of(Iterables.getLast(traceValues));
} else {
tracePushOption = Optional.empty();
}
}
- private static boolean isDirectChangesPush(String refname) {
- Matcher m = NEW_PATCHSET_PATTERN.matcher(refname);
- return m.matches();
- }
-
- private void parseDirectChangesPush(ReceiveCommand cmd) {
- Matcher m = NEW_PATCHSET_PATTERN.matcher(cmd.getRefName());
- checkArgument(m.matches());
-
- if (allowPushToRefsChanges) {
- // The referenced change must exist and must still be open.
- Change.Id changeId = Change.Id.parse(m.group(1));
- parseReplaceCommand(cmd, changeId);
- messages.add(new ValidationMessage("warning: pushes to refs/changes are deprecated", false));
- } else {
- reject(cmd, "upload to refs/changes not allowed");
- }
- }
-
// Wrap ReceiveCommand so the progress counter works automatically.
private ReceiveCommand wrapReceiveCommand(ReceiveCommand cmd, Task progress) {
String refname = cmd.getRefName();
@@ -1007,161 +1047,166 @@ class ReceiveCommits {
*/
private void parseRegularCommand(ReceiveCommand cmd)
throws PermissionBackendException, NoSuchProjectException, IOException {
- if (cmd.getResult() != NOT_ATTEMPTED) {
- // Already rejected by the core receive process.
- logger.atFine().log("Already processed by core: %s %s", cmd.getResult(), cmd);
- return;
- }
-
- if (!Repository.isValidRefName(cmd.getRefName()) || cmd.getRefName().contains("//")) {
- reject(cmd, "not valid ref");
- return;
- }
- if (RefNames.isNoteDbMetaRef(cmd.getRefName())) {
- // Reject pushes to NoteDb refs without a special option and permission. Note that this
- // prohibition doesn't depend on NoteDb being enabled in any way, since all sites will
- // migrate to NoteDb eventually, and we don't want garbage data waiting there when the
- // migration finishes.
- logger.atFine().log(
- "%s NoteDb ref %s with %s=%s",
- cmd.getType(), cmd.getRefName(), NoteDbPushOption.OPTION_NAME, noteDbPushOption);
- if (!Optional.of(NoteDbPushOption.ALLOW).equals(noteDbPushOption)) {
- // Only reject this command, not the whole push. This supports the use case of "git clone
- // --mirror" followed by "git push --mirror", when the user doesn't really intend to clone
- // or mirror the NoteDb data; there is no single refspec that describes all refs *except*
- // NoteDb refs.
- reject(
- cmd,
- "NoteDb update requires -o "
- + NoteDbPushOption.OPTION_NAME
- + "="
- + NoteDbPushOption.ALLOW.value());
+ try (TraceTimer traceTimer = newTimer("parseRegularCommand")) {
+ if (cmd.getResult() != NOT_ATTEMPTED) {
+ // Already rejected by the core receive process.
+ logger.atFine().log("Already processed by core: %s %s", cmd.getResult(), cmd);
return;
}
- try {
- permissionBackend.user(user).check(GlobalPermission.ACCESS_DATABASE);
- } catch (AuthException e) {
- reject(cmd, "NoteDb update requires access database permission");
+
+ if (!Repository.isValidRefName(cmd.getRefName()) || cmd.getRefName().contains("//")) {
+ reject(cmd, "not valid ref");
return;
}
- }
+ if (RefNames.isNoteDbMetaRef(cmd.getRefName())) {
+ // Reject pushes to NoteDb refs without a special option and permission. Note that this
+ // prohibition doesn't depend on NoteDb being enabled in any way, since all sites will
+ // migrate to NoteDb eventually, and we don't want garbage data waiting there when the
+ // migration finishes.
+ logger.atFine().log(
+ "%s NoteDb ref %s with %s=%s",
+ cmd.getType(), cmd.getRefName(), NoteDbPushOption.OPTION_NAME, noteDbPushOption);
+ if (!Optional.of(NoteDbPushOption.ALLOW).equals(noteDbPushOption)) {
+ // Only reject this command, not the whole push. This supports the use case of "git clone
+ // --mirror" followed by "git push --mirror", when the user doesn't really intend to clone
+ // or mirror the NoteDb data; there is no single refspec that describes all refs *except*
+ // NoteDb refs.
+ reject(
+ cmd,
+ "NoteDb update requires -o "
+ + NoteDbPushOption.OPTION_NAME
+ + "="
+ + NoteDbPushOption.ALLOW.value());
+ return;
+ }
+ try {
+ permissionBackend.user(user).check(GlobalPermission.ACCESS_DATABASE);
+ } catch (AuthException e) {
+ reject(cmd, "NoteDb update requires access database permission");
+ return;
+ }
+ }
- switch (cmd.getType()) {
- case CREATE:
- parseCreate(cmd);
- break;
+ switch (cmd.getType()) {
+ case CREATE:
+ parseCreate(cmd);
+ break;
- case UPDATE:
- parseUpdate(cmd);
- break;
+ case UPDATE:
+ parseUpdate(cmd);
+ break;
- case DELETE:
- parseDelete(cmd);
- break;
+ case DELETE:
+ parseDelete(cmd);
+ break;
- case UPDATE_NONFASTFORWARD:
- parseRewind(cmd);
- break;
+ case UPDATE_NONFASTFORWARD:
+ parseRewind(cmd);
+ break;
- default:
- reject(cmd, "prohibited by Gerrit: unknown command type " + cmd.getType());
- return;
- }
+ default:
+ reject(cmd, "prohibited by Gerrit: unknown command type " + cmd.getType());
+ return;
+ }
- if (cmd.getResult() != NOT_ATTEMPTED) {
- return;
- }
+ if (cmd.getResult() != NOT_ATTEMPTED) {
+ return;
+ }
- if (isConfig(cmd)) {
- validateConfigPush(cmd);
+ if (isConfig(cmd)) {
+ validateConfigPush(cmd);
+ }
}
}
/** Validates a push to refs/meta/config, and reject the command if it fails. */
private void validateConfigPush(ReceiveCommand cmd) throws PermissionBackendException {
- logger.atFine().log("Processing %s command", cmd.getRefName());
- try {
- permissions.check(ProjectPermission.WRITE_CONFIG);
- } catch (AuthException e) {
- reject(
- cmd,
- String.format(
- "must be either project owner or have %s permission",
- ProjectPermission.WRITE_CONFIG.describeForException()));
- return;
- }
+ try (TraceTimer traceTimer = newTimer("validateConfigPush")) {
+ logger.atFine().log("Processing %s command", cmd.getRefName());
+ try {
+ permissions.check(ProjectPermission.WRITE_CONFIG);
+ } catch (AuthException e) {
+ reject(
+ cmd,
+ String.format(
+ "must be either project owner or have %s permission",
+ ProjectPermission.WRITE_CONFIG.describeForException()));
+ return;
+ }
- switch (cmd.getType()) {
- case CREATE:
- case UPDATE:
- case UPDATE_NONFASTFORWARD:
- try {
- ProjectConfig cfg = projectConfigFactory.create(project.getNameKey());
- cfg.load(project.getNameKey(), receivePack.getRevWalk(), cmd.getNewId());
- if (!cfg.getValidationErrors().isEmpty()) {
- addError("Invalid project configuration:");
- for (ValidationError err : cfg.getValidationErrors()) {
- addError(" " + err.getMessage());
- }
- reject(cmd, "invalid project configuration");
- logger.atSevere().log(
- "User %s tried to push invalid project configuration %s for %s",
- user.getLoggableName(), cmd.getNewId().name(), project.getName());
- return;
- }
- Project.NameKey newParent = cfg.getProject().getParent(allProjectsName);
- Project.NameKey oldParent = project.getParent(allProjectsName);
- if (oldParent == null) {
- // update of the 'All-Projects' project
- if (newParent != null) {
- reject(cmd, "invalid project configuration: root project cannot have parent");
+ switch (cmd.getType()) {
+ case CREATE:
+ case UPDATE:
+ case UPDATE_NONFASTFORWARD:
+ try {
+ ProjectConfig cfg = projectConfigFactory.create(project.getNameKey());
+ cfg.load(project.getNameKey(), receivePack.getRevWalk(), cmd.getNewId());
+ if (!cfg.getValidationErrors().isEmpty()) {
+ addError("Invalid project configuration:");
+ for (ValidationError err : cfg.getValidationErrors()) {
+ addError(" " + err.getMessage());
+ }
+ reject(cmd, "invalid project configuration");
+ logger.atSevere().log(
+ "User %s tried to push invalid project configuration %s for %s",
+ user.getLoggableName(), cmd.getNewId().name(), project.getName());
return;
}
- } else {
- if (!oldParent.equals(newParent)) {
- if (allowProjectOwnersToChangeParent) {
- try {
- permissionBackend
- .user(user)
- .project(project.getNameKey())
- .check(ProjectPermission.WRITE_CONFIG);
- } catch (AuthException e) {
- reject(cmd, "invalid project configuration: only project owners can set parent");
- return;
- }
- } else {
- try {
- permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
- } catch (AuthException e) {
- reject(cmd, "invalid project configuration: only Gerrit admin can set parent");
- return;
+ Project.NameKey newParent = cfg.getProject().getParent(allProjectsName);
+ Project.NameKey oldParent = project.getParent(allProjectsName);
+ if (oldParent == null) {
+ // update of the 'All-Projects' project
+ if (newParent != null) {
+ reject(cmd, "invalid project configuration: root project cannot have parent");
+ return;
+ }
+ } else {
+ if (!oldParent.equals(newParent)) {
+ if (allowProjectOwnersToChangeParent) {
+ try {
+ permissionBackend
+ .user(user)
+ .project(project.getNameKey())
+ .check(ProjectPermission.WRITE_CONFIG);
+ } catch (AuthException e) {
+ reject(
+ cmd, "invalid project configuration: only project owners can set parent");
+ return;
+ }
+ } else {
+ try {
+ permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
+ } catch (AuthException e) {
+ reject(cmd, "invalid project configuration: only Gerrit admin can set parent");
+ return;
+ }
}
}
- }
- if (projectCache.get(newParent) == null) {
- reject(cmd, "invalid project configuration: parent does not exist");
- return;
+ if (projectCache.get(newParent) == null) {
+ reject(cmd, "invalid project configuration: parent does not exist");
+ return;
+ }
}
+ validatePluginConfig(cmd, cfg);
+ } catch (Exception e) {
+ reject(cmd, "invalid project configuration");
+ logger.atSevere().withCause(e).log(
+ "User %s tried to push invalid project configuration %s for %s",
+ user.getLoggableName(), cmd.getNewId().name(), project.getName());
+ return;
}
- validatePluginConfig(cmd, cfg);
- } catch (Exception e) {
- reject(cmd, "invalid project configuration");
- logger.atSevere().withCause(e).log(
- "User %s tried to push invalid project configuration %s for %s",
- user.getLoggableName(), cmd.getNewId().name(), project.getName());
- return;
- }
- break;
+ break;
- case DELETE:
- break;
+ case DELETE:
+ break;
- default:
- reject(
- cmd,
- "prohibited by Gerrit: don't know how to handle config update of type "
- + cmd.getType());
+ default:
+ reject(
+ cmd,
+ "prohibited by Gerrit: don't know how to handle config update of type "
+ + cmd.getType());
+ }
}
}
@@ -1212,52 +1257,59 @@ class ReceiveCommits {
private void parseCreate(ReceiveCommand cmd)
throws PermissionBackendException, NoSuchProjectException, IOException {
- RevObject obj;
- try {
- obj = receivePack.getRevWalk().parseAny(cmd.getNewId());
- } catch (IOException err) {
- logger.atSevere().withCause(err).log(
- "Invalid object %s for %s creation", cmd.getNewId().name(), cmd.getRefName());
- reject(cmd, "invalid object");
- return;
- }
- logger.atFine().log("Creating %s", cmd);
+ try (TraceTimer traceTimer = newTimer("parseCreate")) {
+ RevObject obj;
+ try {
+ obj = receivePack.getRevWalk().parseAny(cmd.getNewId());
+ } catch (IOException err) {
+ logger.atSevere().withCause(err).log(
+ "Invalid object %s for %s creation", cmd.getNewId().name(), cmd.getRefName());
+ reject(cmd, "invalid object");
+ return;
+ }
+ logger.atFine().log("Creating %s", cmd);
- if (isHead(cmd) && !isCommit(cmd)) {
- return;
- }
+ if (isHead(cmd) && !isCommit(cmd)) {
+ return;
+ }
- Branch.NameKey branch = new Branch.NameKey(project.getName(), cmd.getRefName());
- try {
- // Must pass explicit user instead of injecting a provider into CreateRefControl, since
- // Provider<CurrentUser> within ReceiveCommits will always return anonymous.
- createRefControl.checkCreateRef(Providers.of(user), receivePack.getRepository(), branch, obj);
- } catch (AuthException denied) {
- rejectProhibited(cmd, denied);
- return;
- } catch (ResourceConflictException denied) {
- reject(cmd, "prohibited by Gerrit: " + denied.getMessage());
- return;
- }
+ BranchNameKey branch = BranchNameKey.create(project.getName(), cmd.getRefName());
+ try {
+ // Must pass explicit user instead of injecting a provider into CreateRefControl, since
+ // Provider<CurrentUser> within ReceiveCommits will always return anonymous.
+ createRefControl.checkCreateRef(
+ Providers.of(user), receivePack.getRepository(), branch, obj);
+ } catch (AuthException denied) {
+ rejectProhibited(cmd, denied);
+ return;
+ } catch (ResourceConflictException denied) {
+ reject(cmd, "prohibited by Gerrit: " + denied.getMessage());
+ return;
+ }
- if (validRefOperation(cmd)) {
- validateRegularPushCommits(new Branch.NameKey(project.getNameKey(), cmd.getRefName()), cmd);
+ if (validRefOperation(cmd)) {
+ validateRegularPushCommits(
+ BranchNameKey.create(project.getNameKey(), cmd.getRefName()), cmd);
+ }
}
}
private void parseUpdate(ReceiveCommand cmd) throws PermissionBackendException {
- logger.atFine().log("Updating %s", cmd);
- Optional<AuthException> err = checkRefPermission(cmd, RefPermission.UPDATE);
- if (!err.isPresent()) {
- if (isHead(cmd) && !isCommit(cmd)) {
- reject(cmd, "head must point to commit");
- return;
- }
- if (validRefOperation(cmd)) {
- validateRegularPushCommits(new Branch.NameKey(project.getNameKey(), cmd.getRefName()), cmd);
+ try (TraceTimer traceTimer = TraceContext.newTimer("parseUpdate")) {
+ logger.atFine().log("Updating %s", cmd);
+ Optional<AuthException> err = checkRefPermission(cmd, RefPermission.UPDATE);
+ if (!err.isPresent()) {
+ if (isHead(cmd) && !isCommit(cmd)) {
+ reject(cmd, "head must point to commit");
+ return;
+ }
+ if (validRefOperation(cmd)) {
+ validateRegularPushCommits(
+ BranchNameKey.create(project.getNameKey(), cmd.getRefName()), cmd);
+ }
+ } else {
+ rejectProhibited(cmd, err.get());
}
- } else {
- rejectProhibited(cmd, err.get());
}
}
@@ -1280,45 +1332,50 @@ class ReceiveCommits {
}
private void parseDelete(ReceiveCommand cmd) throws PermissionBackendException {
- logger.atFine().log("Deleting %s", cmd);
- if (cmd.getRefName().startsWith(REFS_CHANGES)) {
- errors.put(CANNOT_DELETE_CHANGES, cmd.getRefName());
- reject(cmd, "cannot delete changes");
- } else if (isConfigRef(cmd.getRefName())) {
- errors.put(CANNOT_DELETE_CONFIG, cmd.getRefName());
- reject(cmd, "cannot delete project configuration");
- }
-
- Optional<AuthException> err = checkRefPermission(cmd, RefPermission.DELETE);
- if (!err.isPresent()) {
- validRefOperation(cmd);
-
- } else {
- rejectProhibited(cmd, err.get());
+ try (TraceTimer traceTimer = newTimer("parseDelete")) {
+ logger.atFine().log("Deleting %s", cmd);
+ if (cmd.getRefName().startsWith(REFS_CHANGES)) {
+ errors.put(CANNOT_DELETE_CHANGES, cmd.getRefName());
+ reject(cmd, "cannot delete changes");
+ } else if (isConfigRef(cmd.getRefName())) {
+ errors.put(CANNOT_DELETE_CONFIG, cmd.getRefName());
+ reject(cmd, "cannot delete project configuration");
+ }
+
+ Optional<AuthException> err = checkRefPermission(cmd, RefPermission.DELETE);
+ if (!err.isPresent()) {
+ validRefOperation(cmd);
+ } else {
+ rejectProhibited(cmd, err.get());
+ }
}
}
private void parseRewind(ReceiveCommand cmd) throws PermissionBackendException {
- try {
- receivePack.getRevWalk().parseCommit(cmd.getNewId());
- } catch (IOException err) {
- logger.atSevere().withCause(err).log(
- "Invalid object %s for %s forced update", cmd.getNewId().name(), cmd.getRefName());
- reject(cmd, "invalid object");
- return;
- }
- logger.atFine().log("Rewinding %s", cmd);
+ try (TraceTimer traceTimer = newTimer("parseRewind")) {
+ try {
+ receivePack.getRevWalk().parseCommit(cmd.getNewId());
+ } catch (IOException err) {
+ logger.atSevere().withCause(err).log(
+ "Invalid object %s for %s forced update", cmd.getNewId().name(), cmd.getRefName());
+ reject(cmd, "invalid object");
+ return;
+ }
+ logger.atFine().log("Rewinding %s", cmd);
- if (!validRefOperation(cmd)) {
- return;
- }
- validateRegularPushCommits(new Branch.NameKey(project.getNameKey(), cmd.getRefName()), cmd);
- if (cmd.getResult() != NOT_ATTEMPTED) {
- return;
- }
+ if (!validRefOperation(cmd)) {
+ return;
+ }
+ validateRegularPushCommits(BranchNameKey.create(project.getNameKey(), cmd.getRefName()), cmd);
+ if (cmd.getResult() != NOT_ATTEMPTED) {
+ return;
+ }
- checkRefPermission(cmd, RefPermission.FORCE_UPDATE)
- .ifPresent((AuthException err) -> rejectProhibited(cmd, err));
+ Optional<AuthException> err = checkRefPermission(cmd, RefPermission.FORCE_UPDATE);
+ if (err.isPresent()) {
+ rejectProhibited(cmd, err.get());
+ }
+ }
}
private Optional<AuthException> checkRefPermission(ReceiveCommand cmd, RefPermission perm)
@@ -1358,11 +1415,21 @@ class ReceiveCommits {
static class MagicBranchInput {
private static final Splitter COMMAS = Splitter.on(',').omitEmptyStrings();
+ private final IdentifiedUser user;
+ private final ProjectState projectState;
+ private final boolean defaultPublishComments;
+
boolean deprecatedTopicSeen;
final ReceiveCommand cmd;
final LabelTypes labelTypes;
- private final boolean defaultPublishComments;
- Branch.NameKey dest;
+ /**
+ * Result of running {@link CommentValidator}-s on drafts that are published with the commit
+ * (which happens iff {@code --publish-comments} is set). Remains {@code true} if none are
+ * installed.
+ */
+ private boolean commentsValid = true;
+
+ BranchNameKey dest;
PermissionBackend.ForRef perm;
Set<String> reviewer = Sets.newLinkedHashSet();
Set<String> cc = Sets.newLinkedHashSet();
@@ -1381,13 +1448,6 @@ class ReceiveCommits {
@Option(name = "--topic", metaVar = "NAME", usage = "attach topic to changes")
String topic;
- @Option(
- name = "--draft",
- usage =
- "Will be removed. Before that, this option will be mapped to '--private'"
- + "for new changes and '--edit' for existing changes")
- boolean draft;
-
@Option(name = "--private", usage = "mark new/updated change as private")
boolean isPrivate;
@@ -1511,18 +1571,23 @@ class ReceiveCommits {
if (!hashtag.isEmpty()) {
hashtags.add(hashtag);
}
- // TODO(dpursehouse): validate hashtags
}
- MagicBranchInput(IdentifiedUser user, ReceiveCommand cmd, LabelTypes labelTypes) {
+ @UsedAt(UsedAt.Project.GOOGLE)
+ @Option(name = "--create-cod-token", usage = "create a token for consistency-on-demand")
+ private boolean createCodToken;
+
+ MagicBranchInput(
+ IdentifiedUser user, ProjectState projectState, ReceiveCommand cmd, LabelTypes labelTypes) {
+ this.user = user;
+ this.projectState = projectState;
this.deprecatedTopicSeen = false;
this.cmd = cmd;
- this.draft = cmd.getRefName().startsWith(MagicBranch.NEW_DRAFT_CHANGE);
this.labelTypes = labelTypes;
- GeneralPreferencesInfo prefs = user.state().getGeneralPreferences();
+ GeneralPreferencesInfo prefs = user.state().generalPreferences();
this.defaultPublishComments =
prefs != null
- ? firstNonNull(user.state().getGeneralPreferences().publishCommentsOnPush, false)
+ ? firstNonNull(user.state().generalPreferences().publishCommentsOnPush, false)
: false;
}
@@ -1560,7 +1625,15 @@ class ReceiveCommits {
.collect(toImmutableSet());
}
+ void setCommentsValid(boolean commentsValid) {
+ this.commentsValid = commentsValid;
+ }
+
boolean shouldPublishComments() {
+ if (!commentsValid) {
+ // Validation messages of type WARNING have already been added, now withhold the comments.
+ return false;
+ }
if (publishComments) {
return true;
} else if (noPublishComments) {
@@ -1619,9 +1692,24 @@ class ReceiveCommits {
return ref.substring(0, split);
}
+ public boolean shouldSetWorkInProgressOnNewChanges() {
+ // When wip or ready explicitly provided, leave it as is.
+ if (workInProgress) {
+ return true;
+ }
+ if (ready) {
+ return false;
+ }
+
+ return projectState.is(BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT)
+ || firstNonNull(user.state().generalPreferences().workInProgressByDefault, false);
+ }
+
NotifyResolver.Result getNotifyForNewChange() {
return NotifyResolver.Result.create(
- firstNonNull(notifyHandling, workInProgress ? NotifyHandling.OWNER : NotifyHandling.ALL),
+ firstNonNull(
+ notifyHandling,
+ shouldSetWorkInProgressOnNewChanges() ? NotifyHandling.OWNER : NotifyHandling.ALL),
ImmutableSetMultimap.<RecipientType, Account.Id>builder()
.putAll(RecipientType.TO, notifyTo)
.putAll(RecipientType.CC, notifyCc)
@@ -1648,202 +1736,198 @@ class ReceiveCommits {
* <p>Assumes we are handling a magic branch here.
*/
private void parseMagicBranch(ReceiveCommand cmd) throws PermissionBackendException {
- logger.atFine().log("Found magic branch %s", cmd.getRefName());
- MagicBranchInput magicBranch = new MagicBranchInput(user, cmd, labelTypes);
+ try (TraceTimer traceTimer = newTimer("parseMagicBranch")) {
+ logger.atFine().log("Found magic branch %s", cmd.getRefName());
+ MagicBranchInput magicBranch = new MagicBranchInput(user, projectState, cmd, labelTypes);
- String ref;
- magicBranch.cmdLineParser = optionParserFactory.create(magicBranch);
+ String ref;
+ magicBranch.cmdLineParser = optionParserFactory.create(magicBranch);
- try {
- ref = magicBranch.parse(repo, receivePack.getAdvertisedRefs().keySet(), pushOptions);
- } catch (CmdLineException e) {
- if (!magicBranch.cmdLineParser.wasHelpRequestedByOption()) {
- logger.atFine().log("Invalid branch syntax");
- reject(cmd, e.getMessage());
- return;
+ try {
+ ref = magicBranch.parse(repo, receivePack.getAdvertisedRefs().keySet(), pushOptions);
+ } catch (CmdLineException e) {
+ if (!magicBranch.cmdLineParser.wasHelpRequestedByOption()) {
+ logger.atFine().log("Invalid branch syntax");
+ reject(cmd, e.getMessage());
+ return;
+ }
+ ref = null; // never happens
}
- ref = null; // never happens
- }
-
- if (magicBranch.topic != null && magicBranch.topic.length() > ChangeUtil.TOPIC_MAX_LENGTH) {
- reject(
- cmd, String.format("topic length exceeds the limit (%d)", ChangeUtil.TOPIC_MAX_LENGTH));
- }
- if (magicBranch.cmdLineParser.wasHelpRequestedByOption()) {
- StringWriter w = new StringWriter();
- w.write("\nHelp for refs/for/branch:\n\n");
- magicBranch.cmdLineParser.printUsage(w, null);
- addMessage(w.toString());
- reject(cmd, "see help");
- return;
- }
- if (projectState.isAllUsers() && RefNames.REFS_USERS_SELF.equals(ref)) {
- logger.atFine().log("Handling %s", RefNames.REFS_USERS_SELF);
- ref = RefNames.refsUsers(user.getAccountId());
- }
- // Pushing changes for review usually requires that the target branch exists, but there is an
- // exception for the branch to which HEAD points to and for refs/meta/config. Pushing for
- // review to these branches is allowed even if the branch does not exist yet. This allows to
- // push initial code for review to an empty repository and to review an initial project
- // configuration.
- if (!receivePack.getAdvertisedRefs().containsKey(ref)
- && !ref.equals(readHEAD(repo))
- && !ref.equals(RefNames.REFS_CONFIG)) {
- logger.atFine().log("Ref %s not found", ref);
- if (ref.startsWith(Constants.R_HEADS)) {
- String n = ref.substring(Constants.R_HEADS.length());
- reject(cmd, "branch " + n + " not found");
- } else {
- reject(cmd, ref + " not found");
+ if (magicBranch.topic != null && magicBranch.topic.length() > ChangeUtil.TOPIC_MAX_LENGTH) {
+ reject(
+ cmd, String.format("topic length exceeds the limit (%d)", ChangeUtil.TOPIC_MAX_LENGTH));
}
- return;
- }
-
- magicBranch.dest = new Branch.NameKey(project.getNameKey(), ref);
- magicBranch.perm = permissions.ref(ref);
-
- Optional<AuthException> err = checkRefPermission(magicBranch.perm, RefPermission.CREATE_CHANGE);
- if (err.isPresent()) {
- rejectProhibited(cmd, err.get());
- return;
- }
- // TODO(davido): Remove legacy support for drafts magic branch option
- // after repo-tool supports private and work-in-progress changes.
- if (magicBranch.draft && !receiveConfig.allowDrafts) {
- errors.put(CODE_REVIEW_ERROR, ref);
- reject(cmd, "draft workflow is disabled");
- return;
- }
+ if (magicBranch.cmdLineParser.wasHelpRequestedByOption()) {
+ StringWriter w = new StringWriter();
+ w.write("\nHelp for refs/for/branch:\n\n");
+ magicBranch.cmdLineParser.printUsage(w, null);
+ addMessage(w.toString());
+ reject(cmd, "see help");
+ return;
+ }
+ if (projectState.isAllUsers() && RefNames.REFS_USERS_SELF.equals(ref)) {
+ logger.atFine().log("Handling %s", RefNames.REFS_USERS_SELF);
+ ref = RefNames.refsUsers(user.getAccountId());
+ }
+ // Pushing changes for review usually requires that the target branch exists, but there is an
+ // exception for the branch to which HEAD points to and for refs/meta/config. Pushing for
+ // review to these branches is allowed even if the branch does not exist yet. This allows to
+ // push initial code for review to an empty repository and to review an initial project
+ // configuration.
+ if (!receivePack.getAdvertisedRefs().containsKey(ref)
+ && !ref.equals(readHEAD(repo))
+ && !ref.equals(RefNames.REFS_CONFIG)) {
+ logger.atFine().log("Ref %s not found", ref);
+ if (ref.startsWith(Constants.R_HEADS)) {
+ String n = ref.substring(Constants.R_HEADS.length());
+ reject(cmd, "branch " + n + " not found");
+ } else {
+ reject(cmd, ref + " not found");
+ }
+ return;
+ }
- if (magicBranch.isPrivate && magicBranch.removePrivate) {
- reject(cmd, "the options 'private' and 'remove-private' are mutually exclusive");
- return;
- }
+ magicBranch.dest = BranchNameKey.create(project.getNameKey(), ref);
+ magicBranch.perm = permissions.ref(ref);
- boolean privateByDefault =
- projectCache.get(project.getNameKey()).is(BooleanProjectConfig.PRIVATE_BY_DEFAULT);
- setChangeAsPrivate =
- magicBranch.draft
- || magicBranch.isPrivate
- || (privateByDefault && !magicBranch.removePrivate);
+ Optional<AuthException> err =
+ checkRefPermission(magicBranch.perm, RefPermission.CREATE_CHANGE);
+ if (err.isPresent()) {
+ rejectProhibited(cmd, err.get());
+ return;
+ }
- if (receiveConfig.disablePrivateChanges && setChangeAsPrivate) {
- reject(cmd, "private changes are disabled");
- return;
- }
+ if (magicBranch.isPrivate && magicBranch.removePrivate) {
+ reject(cmd, "the options 'private' and 'remove-private' are mutually exclusive");
+ return;
+ }
- if (magicBranch.workInProgress && magicBranch.ready) {
- reject(cmd, "the options 'wip' and 'ready' are mutually exclusive");
- return;
- }
- if (magicBranch.publishComments && magicBranch.noPublishComments) {
- reject(
- cmd, "the options 'publish-comments' and 'no-publish-comments' are mutually exclusive");
- return;
- }
+ boolean privateByDefault =
+ projectCache.get(project.getNameKey()).is(BooleanProjectConfig.PRIVATE_BY_DEFAULT);
+ setChangeAsPrivate =
+ magicBranch.isPrivate || (privateByDefault && !magicBranch.removePrivate);
- if (magicBranch.submit) {
- err = checkRefPermission(magicBranch.perm, RefPermission.UPDATE_BY_SUBMIT);
- if (err.isPresent()) {
- rejectProhibited(cmd, err.get());
+ if (receiveConfig.disablePrivateChanges && setChangeAsPrivate) {
+ reject(cmd, "private changes are disabled");
return;
}
- }
- RevWalk walk = receivePack.getRevWalk();
- RevCommit tip;
- try {
- tip = walk.parseCommit(magicBranch.cmd.getNewId());
- logger.atFine().log("Tip of push: %s", tip.name());
- } catch (IOException ex) {
- magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
- logger.atSevere().withCause(ex).log("Invalid pack upload; one or more objects weren't sent");
- return;
- }
+ if (magicBranch.workInProgress && magicBranch.ready) {
+ reject(cmd, "the options 'wip' and 'ready' are mutually exclusive");
+ return;
+ }
+ if (magicBranch.publishComments && magicBranch.noPublishComments) {
+ reject(
+ cmd, "the options 'publish-comments' and 'no-publish-comments' are mutually exclusive");
+ return;
+ }
- String destBranch = magicBranch.dest.get();
- try {
- if (magicBranch.merged) {
- if (magicBranch.base != null) {
- reject(cmd, "cannot use merged with base");
- return;
- }
- RevCommit branchTip = readBranchTip(magicBranch.dest);
- if (branchTip == null) {
- reject(cmd, magicBranch.dest.get() + " not found");
- return;
- }
- if (!walk.isMergedInto(tip, branchTip)) {
- reject(cmd, "not merged into branch");
+ if (magicBranch.submit) {
+ err = checkRefPermission(magicBranch.perm, RefPermission.UPDATE_BY_SUBMIT);
+ if (err.isPresent()) {
+ rejectProhibited(cmd, err.get());
return;
}
}
- // If tip is a merge commit, or the root commit or
- // if %base or %merged was specified, ignore newChangeForAllNotInTarget.
- if (tip.getParentCount() > 1
- || magicBranch.base != null
- || magicBranch.merged
- || tip.getParentCount() == 0) {
- logger.atFine().log("Forcing newChangeForAllNotInTarget = false");
- newChangeForAllNotInTarget = false;
+ RevWalk walk = receivePack.getRevWalk();
+ RevCommit tip;
+ try {
+ tip = walk.parseCommit(magicBranch.cmd.getNewId());
+ logger.atFine().log("Tip of push: %s", tip.name());
+ } catch (IOException ex) {
+ magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
+ logger.atSevere().withCause(ex).log(
+ "Invalid pack upload; one or more objects weren't sent");
+ return;
}
- if (magicBranch.base != null) {
- logger.atFine().log("Handling %%base: %s", magicBranch.base);
- magicBranch.baseCommit = Lists.newArrayListWithCapacity(magicBranch.base.size());
- for (ObjectId id : magicBranch.base) {
- try {
- magicBranch.baseCommit.add(walk.parseCommit(id));
- } catch (IncorrectObjectTypeException notCommit) {
- reject(cmd, "base must be a commit");
+ String destBranch = magicBranch.dest.branch();
+ try {
+ if (magicBranch.merged) {
+ if (magicBranch.base != null) {
+ reject(cmd, "cannot use merged with base");
return;
- } catch (MissingObjectException e) {
- reject(cmd, "base not found");
+ }
+ RevCommit branchTip = readBranchTip(magicBranch.dest);
+ if (branchTip == null) {
+ reject(cmd, magicBranch.dest.branch() + " not found");
return;
- } catch (IOException e) {
- logger.atWarning().withCause(e).log(
- "Project %s cannot read %s", project.getName(), id.name());
- reject(cmd, INTERNAL_SERVER_ERROR);
+ }
+ if (!walk.isMergedInto(tip, branchTip)) {
+ reject(cmd, "not merged into branch");
return;
}
}
- } else if (newChangeForAllNotInTarget) {
- RevCommit branchTip = readBranchTip(magicBranch.dest);
- if (branchTip != null) {
- magicBranch.baseCommit = Collections.singletonList(branchTip);
- logger.atFine().log("Set baseCommit = %s", magicBranch.baseCommit.get(0).name());
- } else {
- // The target branch does not exist. Usually pushing changes for review requires that the
- // target branch exists, but there is an exception for the branch to which HEAD points to
- // and for refs/meta/config. Pushing for review to these branches is allowed even if the
- // branch does not exist yet. This allows to push initial code for review to an empty
- // repository and to review an initial project configuration.
- if (!ref.equals(readHEAD(repo)) && !ref.equals(RefNames.REFS_CONFIG)) {
- reject(cmd, magicBranch.dest.get() + " not found");
- return;
+
+ // If tip is a merge commit, or the root commit or
+ // if %base or %merged was specified, ignore newChangeForAllNotInTarget.
+ if (tip.getParentCount() > 1
+ || magicBranch.base != null
+ || magicBranch.merged
+ || tip.getParentCount() == 0) {
+ logger.atFine().log("Forcing newChangeForAllNotInTarget = false");
+ newChangeForAllNotInTarget = false;
+ }
+
+ if (magicBranch.base != null) {
+ logger.atFine().log("Handling %%base: %s", magicBranch.base);
+ magicBranch.baseCommit = Lists.newArrayListWithCapacity(magicBranch.base.size());
+ for (ObjectId id : magicBranch.base) {
+ try {
+ magicBranch.baseCommit.add(walk.parseCommit(id));
+ } catch (IncorrectObjectTypeException notCommit) {
+ reject(cmd, "base must be a commit");
+ return;
+ } catch (MissingObjectException e) {
+ reject(cmd, "base not found");
+ return;
+ } catch (IOException e) {
+ logger.atWarning().withCause(e).log(
+ "Project %s cannot read %s", project.getName(), id.name());
+ reject(cmd, INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+ } else if (newChangeForAllNotInTarget) {
+ RevCommit branchTip = readBranchTip(magicBranch.dest);
+ if (branchTip != null) {
+ magicBranch.baseCommit = Collections.singletonList(branchTip);
+ logger.atFine().log("Set baseCommit = %s", magicBranch.baseCommit.get(0).name());
+ } else {
+ // The target branch does not exist. Usually pushing changes for review requires that
+ // the
+ // target branch exists, but there is an exception for the branch to which HEAD points
+ // to
+ // and for refs/meta/config. Pushing for review to these branches is allowed even if the
+ // branch does not exist yet. This allows to push initial code for review to an empty
+ // repository and to review an initial project configuration.
+ if (!ref.equals(readHEAD(repo)) && !ref.equals(RefNames.REFS_CONFIG)) {
+ reject(cmd, magicBranch.dest.branch() + " not found");
+ return;
+ }
}
}
+ } catch (IOException ex) {
+ logger.atWarning().withCause(ex).log(
+ "Error walking to %s in project %s", destBranch, project.getName());
+ reject(cmd, INTERNAL_SERVER_ERROR);
+ return;
}
- } catch (IOException ex) {
- logger.atWarning().withCause(ex).log(
- "Error walking to %s in project %s", destBranch, project.getName());
- reject(cmd, INTERNAL_SERVER_ERROR);
- return;
- }
- if (magicBranch.deprecatedTopicSeen) {
- messages.add(
- new ValidationMessage(
- "WARNING: deprecated topic syntax. Use -o topic=TOPIC instead", false));
- logger.atInfo().log("deprecated topic push seen for project %s", project.getName());
- }
+ if (magicBranch.deprecatedTopicSeen) {
+ messages.add(
+ new ValidationMessage(
+ "WARNING: deprecated topic syntax. Use -o topic=TOPIC instead", false));
+ logger.atInfo().log("deprecated topic push seen for project %s", project.getName());
+ }
- if (validateConnected(magicBranch.cmd, magicBranch.dest, tip)) {
- this.magicBranch = magicBranch;
- this.resultChangeIds.setMagicPush(true);
+ if (validateConnected(magicBranch.cmd, magicBranch.dest, tip)) {
+ this.magicBranch = magicBranch;
+ this.resultChangeIds.setMagicPush(true);
+ }
}
}
@@ -1851,42 +1935,45 @@ class ReceiveCommits {
// branch. If they aren't, we want to abort. We do this check by
// looking to see if we can compute a merge base between the new
// commits and the target branch head.
- private boolean validateConnected(ReceiveCommand cmd, Branch.NameKey dest, RevCommit tip) {
- RevWalk walk = receivePack.getRevWalk();
- try {
- Ref targetRef = receivePack.getAdvertisedRefs().get(dest.get());
- if (targetRef == null || targetRef.getObjectId() == null) {
- // The destination branch does not yet exist. Assume the
- // history being sent for review will start it and thus
- // is "connected" to the branch.
- logger.atFine().log("Branch is unborn");
-
- // This is not an error condition.
- return true;
- }
-
- RevCommit h = walk.parseCommit(targetRef.getObjectId());
- logger.atFine().log("Current branch tip: %s", h.name());
- RevFilter oldRevFilter = walk.getRevFilter();
+ private boolean validateConnected(ReceiveCommand cmd, BranchNameKey dest, RevCommit tip) {
+ try (TraceTimer traceTimer =
+ newTimer("validateConnected", Metadata.builder().branchName(dest.branch()))) {
+ RevWalk walk = receivePack.getRevWalk();
try {
- walk.reset();
- walk.setRevFilter(RevFilter.MERGE_BASE);
- walk.markStart(tip);
- walk.markStart(h);
- if (walk.next() == null) {
- reject(cmd, "no common ancestry");
- return false;
+ Ref targetRef = receivePack.getAdvertisedRefs().get(dest.branch());
+ if (targetRef == null || targetRef.getObjectId() == null) {
+ // The destination branch does not yet exist. Assume the
+ // history being sent for review will start it and thus
+ // is "connected" to the branch.
+ logger.atFine().log("Branch is unborn");
+
+ // This is not an error condition.
+ return true;
}
- } finally {
- walk.reset();
- walk.setRevFilter(oldRevFilter);
+
+ RevCommit h = walk.parseCommit(targetRef.getObjectId());
+ logger.atFine().log("Current branch tip: %s", h.name());
+ RevFilter oldRevFilter = walk.getRevFilter();
+ try {
+ walk.reset();
+ walk.setRevFilter(RevFilter.MERGE_BASE);
+ walk.markStart(tip);
+ walk.markStart(h);
+ if (walk.next() == null) {
+ reject(cmd, "no common ancestry");
+ return false;
+ }
+ } finally {
+ walk.reset();
+ walk.setRevFilter(oldRevFilter);
+ }
+ } catch (IOException e) {
+ cmd.setResult(REJECTED_MISSING_OBJECT);
+ logger.atSevere().withCause(e).log("Invalid pack upload; one or more objects weren't sent");
+ return false;
}
- } catch (IOException e) {
- cmd.setResult(REJECTED_MISSING_OBJECT);
- logger.atSevere().withCause(e).log("Invalid pack upload; one or more objects weren't sent");
- return false;
+ return true;
}
- return true;
}
private static String readHEAD(Repository repo) {
@@ -1900,89 +1987,64 @@ class ReceiveCommits {
}
}
- private RevCommit readBranchTip(Branch.NameKey branch) throws IOException {
- Ref r = allRefs().get(branch.get());
+ private RevCommit readBranchTip(BranchNameKey branch) throws IOException {
+ Ref r = allRefs().get(branch.branch());
if (r == null) {
return null;
}
return receivePack.getRevWalk().parseCommit(r.getObjectId());
}
- // Handle an upload to refs/changes/XX/CHANGED-NUMBER.
- private void parseReplaceCommand(ReceiveCommand cmd, Change.Id changeId) {
- logger.atFine().log("Parsing replace command");
- if (cmd.getType() != ReceiveCommand.Type.CREATE) {
- reject(cmd, "invalid usage");
- return;
- }
-
- RevCommit newCommit;
- try {
- newCommit = receivePack.getRevWalk().parseCommit(cmd.getNewId());
- logger.atFine().log("Replacing with %s", newCommit);
- } catch (IOException e) {
- logger.atSevere().withCause(e).log("Cannot parse %s as commit", cmd.getNewId().name());
- reject(cmd, "invalid commit");
- return;
- }
-
- Change changeEnt;
- try {
- changeEnt = notesFactory.createChecked(project.getNameKey(), changeId).getChange();
- } catch (NoSuchChangeException e) {
- logger.atSevere().withCause(e).log("Change not found %s", changeId);
- reject(cmd, "change " + changeId + " not found");
- return;
- } catch (StorageException e) {
- logger.atSevere().withCause(e).log("Cannot lookup existing change %s", changeId);
- reject(cmd, "database error");
- return;
- }
- if (!project.getNameKey().equals(changeEnt.getProject())) {
- reject(cmd, "change " + changeId + " does not belong to project " + project.getName());
- return;
- }
-
- BranchCommitValidator validator =
- commitValidatorFactory.create(projectState, changeEnt.getDest(), user);
- try {
- if (validator.validCommit(
- receivePack.getRevWalk().getObjectReader(),
- cmd,
- newCommit,
- false,
- messages,
- rejectCommits,
- changeEnt)) {
- logger.atFine().log("Replacing change %s", changeEnt.getId());
- requestReplace(cmd, true, changeEnt, newCommit);
- }
- } catch (IOException e) {
- reject(cmd, "I/O exception validating commit");
- }
- }
-
/**
- * Add an update for an existing change. Returns true if it succeeded; rejects the command if it
- * failed.
+ * Update an existing change. If draft comments are to be published, these are validated and may
+ * be withheld.
+ *
+ * @return True if the command succeeded, false if it was rejected.
*/
- private boolean requestReplace(
+ private boolean requestReplaceAndValidateComments(
ReceiveCommand cmd, boolean checkMergedInto, Change change, RevCommit newCommit) {
- if (change.isClosed()) {
- reject(
- cmd,
- changeFormatter.changeClosed(
- ChangeReportFormatter.Input.builder().setChange(change).build()));
- return false;
- }
+ try (TraceTimer traceTimer = newTimer("requestReplaceAndValidateComments")) {
+ if (change.isClosed()) {
+ reject(
+ cmd,
+ changeFormatter.changeClosed(
+ ChangeReportFormatter.Input.builder().setChange(change).build()));
+ return false;
+ }
- ReplaceRequest req = new ReplaceRequest(change.getId(), newCommit, cmd, checkMergedInto);
- if (replaceByChange.containsKey(req.ontoChange)) {
- reject(cmd, "duplicate request");
- return false;
+ ReplaceRequest req = new ReplaceRequest(change.getId(), newCommit, cmd, checkMergedInto);
+ if (replaceByChange.containsKey(req.ontoChange)) {
+ reject(cmd, "duplicate request");
+ return false;
+ }
+
+ if (magicBranch != null && magicBranch.shouldPublishComments()) {
+ List<Comment> drafts =
+ commentsUtil.draftByChangeAuthor(
+ notesFactory.createChecked(change), user.getAccountId());
+ ImmutableList<CommentForValidation> draftsForValidation =
+ drafts.stream()
+ .map(
+ comment ->
+ CommentForValidation.create(
+ comment.lineNbr > 0
+ ? CommentType.INLINE_COMMENT
+ : CommentType.FILE_COMMENT,
+ comment.message))
+ .collect(toImmutableList());
+ ImmutableList<CommentValidationFailure> commentValidationFailures =
+ PublishCommentUtil.findInvalidComments(commentValidators, draftsForValidation);
+ magicBranch.setCommentsValid(commentValidationFailures.isEmpty());
+ commentValidationFailures.forEach(
+ failure ->
+ addMessage(
+ "Comment validation failure: " + failure.getMessage(),
+ ValidationMessage.Type.WARNING));
+ }
+
+ replaceByChange.put(req.ontoChange, req);
+ return true;
}
- replaceByChange.put(req.ontoChange, req);
- return true;
}
private void warnAboutMissingChangeId(List<CreateRequest> newChanges) {
@@ -2003,351 +2065,367 @@ class ReceiveCommits {
}
private List<CreateRequest> selectNewAndReplacedChangesFromMagicBranch(Task newProgress) {
- logger.atFine().log("Finding new and replaced changes");
- List<CreateRequest> newChanges = new ArrayList<>();
-
- ListMultimap<ObjectId, Ref> existing = changeRefsById();
- GroupCollector groupCollector =
- GroupCollector.create(changeRefsById(), psUtil, notesFactory, project.getNameKey());
+ try (TraceTimer traceTimer = newTimer("selectNewAndReplacedChangesFromMagicBranch")) {
+ logger.atFine().log("Finding new and replaced changes");
+ List<CreateRequest> newChanges = new ArrayList<>();
- BranchCommitValidator validator =
- commitValidatorFactory.create(projectState, magicBranch.dest, user);
-
- try {
- RevCommit start = setUpWalkForSelectingChanges();
- if (start == null) {
- return Collections.emptyList();
- }
+ ListMultimap<ObjectId, Ref> existing = changeRefsById();
+ GroupCollector groupCollector =
+ GroupCollector.create(changeRefsById(), psUtil, notesFactory, project.getNameKey());
- LinkedHashMap<RevCommit, ChangeLookup> pending = new LinkedHashMap<>();
- Set<Change.Key> newChangeIds = new HashSet<>();
- int maxBatchChanges = receiveConfig.getEffectiveMaxBatchChangesLimit(user);
- int total = 0;
- int alreadyTracked = 0;
- boolean rejectImplicitMerges =
- start.getParentCount() == 1
- && projectCache
- .get(project.getNameKey())
- .is(BooleanProjectConfig.REJECT_IMPLICIT_MERGES)
- // Don't worry about implicit merges when creating changes for
- // already-merged commits; they're already in history, so it's too
- // late.
- && !magicBranch.merged;
- Set<RevCommit> mergedParents;
- if (rejectImplicitMerges) {
- mergedParents = new HashSet<>();
- } else {
- mergedParents = null;
- }
+ BranchCommitValidator validator =
+ commitValidatorFactory.create(projectState, magicBranch.dest, user);
- for (; ; ) {
- RevCommit c = receivePack.getRevWalk().next();
- if (c == null) {
- break;
+ try {
+ RevCommit start = setUpWalkForSelectingChanges();
+ if (start == null) {
+ return Collections.emptyList();
}
- total++;
- receivePack.getRevWalk().parseBody(c);
- String name = c.name();
- groupCollector.visit(c);
- Collection<Ref> existingRefs = existing.get(c);
+ LinkedHashMap<RevCommit, ChangeLookup> pending = new LinkedHashMap<>();
+ Set<Change.Key> newChangeIds = new HashSet<>();
+ int maxBatchChanges = receiveConfig.getEffectiveMaxBatchChangesLimit(user);
+ int total = 0;
+ int alreadyTracked = 0;
+ boolean rejectImplicitMerges =
+ start.getParentCount() == 1
+ && projectCache
+ .get(project.getNameKey())
+ .is(BooleanProjectConfig.REJECT_IMPLICIT_MERGES)
+ // Don't worry about implicit merges when creating changes for
+ // already-merged commits; they're already in history, so it's too
+ // late.
+ && !magicBranch.merged;
+ Set<RevCommit> mergedParents;
if (rejectImplicitMerges) {
- Collections.addAll(mergedParents, c.getParents());
- mergedParents.remove(c);
- }
-
- boolean commitAlreadyTracked = !existingRefs.isEmpty();
- if (commitAlreadyTracked) {
- alreadyTracked++;
- // Corner cases where an existing commit might need a new group:
- // A) Existing commit has a null group; wasn't assigned during schema
- // upgrade, or schema upgrade is performed on a running server.
- // B) Let A<-B<-C, then:
- // 1. Push A to refs/heads/master
- // 2. Push B to refs/for/master
- // 3. Force push A~ to refs/heads/master
- // 4. Push C to refs/for/master.
- // B will be in existing so we aren't replacing the patch set. It
- // used to have its own group, but now needs to to be changed to
- // A's group.
- // C) Commit is a PatchSet of a pre-existing change uploaded with a
- // different target branch.
- for (Ref ref : existingRefs) {
- updateGroups.add(new UpdateGroupsRequest(ref, c));
- }
- if (!(newChangeForAllNotInTarget || magicBranch.base != null)) {
- continue;
- }
- }
-
- List<String> idList = c.getFooterLines(CHANGE_ID);
- if (!idList.isEmpty()) {
- pending.put(
- c, lookupByChangeKey(c, new Change.Key(idList.get(idList.size() - 1).trim())));
+ mergedParents = new HashSet<>();
} else {
- pending.put(c, lookupByCommit(c));
+ mergedParents = null;
}
- int n = pending.size() + newChanges.size();
- if (maxBatchChanges != 0 && n > maxBatchChanges) {
- logger.atFine().log("%d changes exceeds limit of %d", n, maxBatchChanges);
- reject(
- magicBranch.cmd,
- "the number of pushed changes in a batch exceeds the max limit " + maxBatchChanges);
- return Collections.emptyList();
- }
+ for (; ; ) {
+ RevCommit c = receivePack.getRevWalk().next();
+ if (c == null) {
+ break;
+ }
+ total++;
+ receivePack.getRevWalk().parseBody(c);
+ String name = c.name();
+ groupCollector.visit(c);
+ Collection<Ref> existingRefs = existing.get(c);
+
+ if (rejectImplicitMerges) {
+ Collections.addAll(mergedParents, c.getParents());
+ mergedParents.remove(c);
+ }
- if (commitAlreadyTracked) {
- boolean changeExistsOnDestBranch = false;
- for (ChangeData cd : pending.get(c).destChanges) {
- if (cd.change().getDest().equals(magicBranch.dest)) {
- changeExistsOnDestBranch = true;
- break;
+ boolean commitAlreadyTracked = !existingRefs.isEmpty();
+ if (commitAlreadyTracked) {
+ alreadyTracked++;
+ // Corner cases where an existing commit might need a new group:
+ // A) Existing commit has a null group; wasn't assigned during schema
+ // upgrade, or schema upgrade is performed on a running server.
+ // B) Let A<-B<-C, then:
+ // 1. Push A to refs/heads/master
+ // 2. Push B to refs/for/master
+ // 3. Force push A~ to refs/heads/master
+ // 4. Push C to refs/for/master.
+ // B will be in existing so we aren't replacing the patch set. It
+ // used to have its own group, but now needs to to be changed to
+ // A's group.
+ // C) Commit is a PatchSet of a pre-existing change uploaded with a
+ // different target branch.
+ for (Ref ref : existingRefs) {
+ updateGroups.add(new UpdateGroupsRequest(ref, c));
+ }
+ if (!(newChangeForAllNotInTarget || magicBranch.base != null)) {
+ continue;
}
- }
- if (changeExistsOnDestBranch) {
- continue;
}
- logger.atFine().log("Creating new change for %s even though it is already tracked", name);
- }
+ List<String> idList = c.getFooterLines(FooterConstants.CHANGE_ID);
+ if (!idList.isEmpty()) {
+ pending.put(c, lookupByChangeKey(c, Change.key(idList.get(idList.size() - 1).trim())));
+ } else {
+ pending.put(c, lookupByCommit(c));
+ }
- if (!validator.validCommit(
- receivePack.getRevWalk().getObjectReader(),
- magicBranch.cmd,
- c,
- magicBranch.merged,
- messages,
- rejectCommits,
- null)) {
- // Not a change the user can propose? Abort as early as possible.
- logger.atFine().log("Aborting early due to invalid commit");
- return Collections.emptyList();
- }
+ int n = pending.size() + newChanges.size();
+ if (maxBatchChanges != 0 && n > maxBatchChanges) {
+ logger.atFine().log("%d changes exceeds limit of %d", n, maxBatchChanges);
+ reject(
+ magicBranch.cmd,
+ "the number of pushed changes in a batch exceeds the max limit " + maxBatchChanges);
+ return Collections.emptyList();
+ }
- // Don't allow merges to be uploaded in commit chain via all-not-in-target
- if (newChangeForAllNotInTarget && c.getParentCount() > 1) {
- reject(
- magicBranch.cmd,
- "Pushing merges in commit chains with 'all not in target' is not allowed,\n"
- + "to override please set the base manually");
- logger.atFine().log("Rejecting merge commit %s with newChangeForAllNotInTarget", name);
- // TODO(dborowitz): Should we early return here?
- }
+ if (commitAlreadyTracked) {
+ boolean changeExistsOnDestBranch = false;
+ for (ChangeData cd : pending.get(c).destChanges) {
+ if (cd.change().getDest().equals(magicBranch.dest)) {
+ changeExistsOnDestBranch = true;
+ break;
+ }
+ }
+ if (changeExistsOnDestBranch) {
+ continue;
+ }
- if (idList.isEmpty()) {
- newChanges.add(new CreateRequest(c, magicBranch.dest.get(), newProgress));
- continue;
- }
- }
- logger.atFine().log(
- "Finished initial RevWalk with %d commits total: %d already"
- + " tracked, %d new changes with no Change-Id, and %d deferred"
- + " lookups",
- total, alreadyTracked, newChanges.size(), pending.size());
+ logger.atFine().log(
+ "Creating new change for %s even though it is already tracked", name);
+ }
- if (rejectImplicitMerges) {
- rejectImplicitMerges(mergedParents);
- }
+ BranchCommitValidator.Result validationResult =
+ validator.validateCommit(
+ receivePack.getRevWalk().getObjectReader(),
+ magicBranch.cmd,
+ c,
+ magicBranch.merged,
+ rejectCommits,
+ null);
+ messages.addAll(validationResult.messages());
+ if (!validationResult.isValid()) {
+ // Not a change the user can propose? Abort as early as possible.
+ logger.atFine().log("Aborting early due to invalid commit");
+ return Collections.emptyList();
+ }
- for (Iterator<ChangeLookup> itr = pending.values().iterator(); itr.hasNext(); ) {
- ChangeLookup p = itr.next();
- if (p.changeKey == null) {
- continue;
- }
+ // Don't allow merges to be uploaded in commit chain via all-not-in-target
+ if (newChangeForAllNotInTarget && c.getParentCount() > 1) {
+ reject(
+ magicBranch.cmd,
+ "Pushing merges in commit chains with 'all not in target' is not allowed,\n"
+ + "to override please set the base manually");
+ logger.atFine().log("Rejecting merge commit %s with newChangeForAllNotInTarget", name);
+ // TODO(dborowitz): Should we early return here?
+ }
- if (newChangeIds.contains(p.changeKey)) {
- logger.atFine().log("Multiple commits with Change-Id %s", p.changeKey);
- reject(magicBranch.cmd, SAME_CHANGE_ID_IN_MULTIPLE_CHANGES);
- return Collections.emptyList();
+ if (idList.isEmpty()) {
+ newChanges.add(new CreateRequest(c, magicBranch.dest.branch(), newProgress));
+ continue;
+ }
}
+ logger.atFine().log(
+ "Finished initial RevWalk with %d commits total: %d already"
+ + " tracked, %d new changes with no Change-Id, and %d deferred"
+ + " lookups",
+ total, alreadyTracked, newChanges.size(), pending.size());
- List<ChangeData> changes = p.destChanges;
- if (changes.size() > 1) {
- logger.atFine().log(
- "Multiple changes in branch %s with Change-Id %s: %s",
- magicBranch.dest,
- p.changeKey,
- changes.stream().map(cd -> cd.getId().toString()).collect(joining()));
- // WTF, multiple changes in this branch have the same key?
- // Since the commit is new, the user should recreate it with
- // a different Change-Id. In practice, we should never see
- // this error message as Change-Id should be unique per branch.
- //
- reject(magicBranch.cmd, p.changeKey.get() + " has duplicates");
- return Collections.emptyList();
+ if (rejectImplicitMerges) {
+ rejectImplicitMerges(mergedParents);
}
- if (changes.size() == 1) {
- // Schedule as a replacement to this one matching change.
- //
+ for (Iterator<ChangeLookup> itr = pending.values().iterator(); itr.hasNext(); ) {
+ ChangeLookup p = itr.next();
+ if (p.changeKey == null) {
+ continue;
+ }
- RevId currentPs = changes.get(0).currentPatchSet().getRevision();
- // If Commit is already current PatchSet of target Change.
- if (p.commit.name().equals(currentPs.get())) {
- if (pending.size() == 1) {
- // There are no commits left to check, all commits in pending were already
- // current PatchSet of the corresponding target changes.
- reject(magicBranch.cmd, "commit(s) already exists (as current patchset)");
- } else {
- // Commit is already current PatchSet.
- // Remove from pending and try next commit.
- itr.remove();
- continue;
- }
+ if (newChangeIds.contains(p.changeKey)) {
+ logger.atFine().log("Multiple commits with Change-Id %s", p.changeKey);
+ reject(magicBranch.cmd, SAME_CHANGE_ID_IN_MULTIPLE_CHANGES);
+ return Collections.emptyList();
}
- if (requestReplace(magicBranch.cmd, false, changes.get(0).change(), p.commit)) {
- continue;
+
+ List<ChangeData> changes = p.destChanges;
+ if (changes.size() > 1) {
+ logger.atFine().log(
+ "Multiple changes in branch %s with Change-Id %s: %s",
+ magicBranch.dest,
+ p.changeKey,
+ changes.stream().map(cd -> cd.getId().toString()).collect(joining()));
+ // WTF, multiple changes in this branch have the same key?
+ // Since the commit is new, the user should recreate it with
+ // a different Change-Id. In practice, we should never see
+ // this error message as Change-Id should be unique per branch.
+ //
+ reject(magicBranch.cmd, p.changeKey.get() + " has duplicates");
+ return Collections.emptyList();
}
- return Collections.emptyList();
- }
- if (changes.isEmpty()) {
- if (!isValidChangeId(p.changeKey.get())) {
- reject(magicBranch.cmd, "invalid Change-Id");
+ if (changes.size() == 1) {
+ // Schedule as a replacement to this one matching change.
+ //
+
+ ObjectId currentPs = changes.get(0).currentPatchSet().commitId();
+ // If Commit is already current PatchSet of target Change.
+ if (p.commit.equals(currentPs)) {
+ if (pending.size() == 1) {
+ // There are no commits left to check, all commits in pending were already
+ // current PatchSet of the corresponding target changes.
+ reject(magicBranch.cmd, "commit(s) already exists (as current patchset)");
+ } else {
+ // Commit is already current PatchSet.
+ // Remove from pending and try next commit.
+ itr.remove();
+ continue;
+ }
+ }
+ if (requestReplaceAndValidateComments(
+ magicBranch.cmd, false, changes.get(0).change(), p.commit)) {
+ continue;
+ }
return Collections.emptyList();
}
- // In case the change look up from the index failed,
- // double check against the existing refs
- if (foundInExistingRef(existing.get(p.commit))) {
- if (pending.size() == 1) {
- reject(magicBranch.cmd, "commit(s) already exists (as current patchset)");
+ if (changes.isEmpty()) {
+ if (!isValidChangeId(p.changeKey.get())) {
+ reject(magicBranch.cmd, "invalid Change-Id");
return Collections.emptyList();
}
- itr.remove();
- continue;
+
+ // In case the change look up from the index failed,
+ // double check against the existing refs
+ if (foundInExistingRef(existing.get(p.commit))) {
+ if (pending.size() == 1) {
+ reject(magicBranch.cmd, "commit(s) already exists (as current patchset)");
+ return Collections.emptyList();
+ }
+ itr.remove();
+ continue;
+ }
+ newChangeIds.add(p.changeKey);
}
- newChangeIds.add(p.changeKey);
+ newChanges.add(new CreateRequest(p.commit, magicBranch.dest.branch(), newProgress));
}
- newChanges.add(new CreateRequest(p.commit, magicBranch.dest.get(), newProgress));
+ logger.atFine().log(
+ "Finished deferred lookups with %d updates and %d new changes",
+ replaceByChange.size(), newChanges.size());
+ } catch (IOException e) {
+ // Should never happen, the core receive process would have
+ // identified the missing object earlier before we got control.
+ //
+ magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
+ logger.atSevere().withCause(e).log("Invalid pack upload; one or more objects weren't sent");
+ return Collections.emptyList();
+ } catch (StorageException e) {
+ logger.atSevere().withCause(e).log("Cannot query database to locate prior changes");
+ reject(magicBranch.cmd, "database error");
+ return Collections.emptyList();
}
- logger.atFine().log(
- "Finished deferred lookups with %d updates and %d new changes",
- replaceByChange.size(), newChanges.size());
- } catch (IOException e) {
- // Should never happen, the core receive process would have
- // identified the missing object earlier before we got control.
- //
- magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
- logger.atSevere().withCause(e).log("Invalid pack upload; one or more objects weren't sent");
- return Collections.emptyList();
- } catch (StorageException e) {
- logger.atSevere().withCause(e).log("Cannot query database to locate prior changes");
- reject(magicBranch.cmd, "database error");
- return Collections.emptyList();
- }
- if (newChanges.isEmpty() && replaceByChange.isEmpty()) {
- reject(magicBranch.cmd, "no new changes");
- return Collections.emptyList();
- }
- if (!newChanges.isEmpty() && magicBranch.edit) {
- reject(magicBranch.cmd, "edit is not supported for new changes");
- return newChanges;
- }
-
- try {
- SortedSetMultimap<ObjectId, String> groups = groupCollector.getGroups();
- List<Integer> newIds = seq.nextChangeIds(newChanges.size());
- for (int i = 0; i < newChanges.size(); i++) {
- CreateRequest create = newChanges.get(i);
- create.setChangeId(newIds.get(i));
- create.groups = ImmutableList.copyOf(groups.get(create.commit));
+ if (newChanges.isEmpty() && replaceByChange.isEmpty()) {
+ reject(magicBranch.cmd, "no new changes");
+ return Collections.emptyList();
}
- for (ReplaceRequest replace : replaceByChange.values()) {
- replace.groups = ImmutableList.copyOf(groups.get(replace.newCommitId));
+ if (!newChanges.isEmpty() && magicBranch.edit) {
+ reject(magicBranch.cmd, "edit is not supported for new changes");
+ return newChanges;
}
- for (UpdateGroupsRequest update : updateGroups) {
- update.groups = ImmutableList.copyOf((groups.get(update.commit)));
+
+ try {
+ SortedSetMultimap<ObjectId, String> groups = groupCollector.getGroups();
+ List<Integer> newIds = seq.nextChangeIds(newChanges.size());
+ for (int i = 0; i < newChanges.size(); i++) {
+ CreateRequest create = newChanges.get(i);
+ create.setChangeId(newIds.get(i));
+ create.groups = ImmutableList.copyOf(groups.get(create.commit));
+ }
+ for (ReplaceRequest replace : replaceByChange.values()) {
+ replace.groups = ImmutableList.copyOf(groups.get(replace.newCommitId));
+ }
+ for (UpdateGroupsRequest update : updateGroups) {
+ update.groups = ImmutableList.copyOf((groups.get(update.commit)));
+ }
+ logger.atFine().log("Finished updating groups from GroupCollector");
+ } catch (StorageException e) {
+ logger.atSevere().withCause(e).log("Error collecting groups for changes");
+ reject(magicBranch.cmd, INTERNAL_SERVER_ERROR);
}
- logger.atFine().log("Finished updating groups from GroupCollector");
- } catch (StorageException e) {
- logger.atSevere().withCause(e).log("Error collecting groups for changes");
- reject(magicBranch.cmd, INTERNAL_SERVER_ERROR);
+ return newChanges;
}
- return newChanges;
}
private boolean foundInExistingRef(Collection<Ref> existingRefs) {
- for (Ref ref : existingRefs) {
- ChangeNotes notes =
- notesFactory.create(project.getNameKey(), Change.Id.fromRef(ref.getName()));
- Change change = notes.getChange();
- if (change.getDest().equals(magicBranch.dest)) {
- logger.atFine().log("Found change %s from existing refs.", change.getKey());
- // Reindex the change asynchronously, ignoring errors.
- @SuppressWarnings("unused")
- Future<?> possiblyIgnoredError = indexer.indexAsync(project.getNameKey(), change.getId());
- return true;
+ try (TraceTimer traceTimer = newTimer("foundInExistingRef")) {
+ for (Ref ref : existingRefs) {
+ ChangeNotes notes =
+ notesFactory.create(project.getNameKey(), Change.Id.fromRef(ref.getName()));
+ Change change = notes.getChange();
+ if (change.getDest().equals(magicBranch.dest)) {
+ logger.atFine().log("Found change %s from existing refs.", change.getKey());
+ // Reindex the change asynchronously, ignoring errors.
+ @SuppressWarnings("unused")
+ Future<?> possiblyIgnoredError = indexer.indexAsync(project.getNameKey(), change.getId());
+ return true;
+ }
}
+ return false;
}
- return false;
}
private RevCommit setUpWalkForSelectingChanges() throws IOException {
- RevWalk rw = receivePack.getRevWalk();
- RevCommit start = rw.parseCommit(magicBranch.cmd.getNewId());
-
- rw.reset();
- rw.sort(RevSort.TOPO);
- rw.sort(RevSort.REVERSE, true);
- receivePack.getRevWalk().markStart(start);
- if (magicBranch.baseCommit != null) {
- markExplicitBasesUninteresting();
- } else if (magicBranch.merged) {
- logger.atFine().log("Marking parents of merged commit %s uninteresting", start.name());
- for (RevCommit c : start.getParents()) {
- rw.markUninteresting(c);
+ try (TraceTimer traceTimer = newTimer("setUpWalkForSelectingChanges")) {
+ RevWalk rw = receivePack.getRevWalk();
+ RevCommit start = rw.parseCommit(magicBranch.cmd.getNewId());
+
+ rw.reset();
+ rw.sort(RevSort.TOPO);
+ rw.sort(RevSort.REVERSE, true);
+ receivePack.getRevWalk().markStart(start);
+ if (magicBranch.baseCommit != null) {
+ markExplicitBasesUninteresting();
+ } else if (magicBranch.merged) {
+ logger.atFine().log("Marking parents of merged commit %s uninteresting", start.name());
+ for (RevCommit c : start.getParents()) {
+ rw.markUninteresting(c);
+ }
+ } else {
+ markHeadsAsUninteresting(rw, magicBranch.dest != null ? magicBranch.dest.branch() : null);
}
- } else {
- markHeadsAsUninteresting(rw, magicBranch.dest != null ? magicBranch.dest.get() : null);
+ return start;
}
- return start;
}
private void markExplicitBasesUninteresting() throws IOException {
- logger.atFine().log("Marking %d base commits uninteresting", magicBranch.baseCommit.size());
- for (RevCommit c : magicBranch.baseCommit) {
- receivePack.getRevWalk().markUninteresting(c);
- }
- Ref targetRef = allRefs().get(magicBranch.dest.get());
- if (targetRef != null) {
- logger.atFine().log(
- "Marking target ref %s (%s) uninteresting",
- magicBranch.dest.get(), targetRef.getObjectId().name());
- receivePack
- .getRevWalk()
- .markUninteresting(receivePack.getRevWalk().parseCommit(targetRef.getObjectId()));
+ try (TraceTimer traceTimer = newTimer("markExplicitBasesUninteresting")) {
+ logger.atFine().log("Marking %d base commits uninteresting", magicBranch.baseCommit.size());
+ for (RevCommit c : magicBranch.baseCommit) {
+ receivePack.getRevWalk().markUninteresting(c);
+ }
+ Ref targetRef = allRefs().get(magicBranch.dest.branch());
+ if (targetRef != null) {
+ logger.atFine().log(
+ "Marking target ref %s (%s) uninteresting",
+ magicBranch.dest.branch(), targetRef.getObjectId().name());
+ receivePack
+ .getRevWalk()
+ .markUninteresting(receivePack.getRevWalk().parseCommit(targetRef.getObjectId()));
+ }
}
}
private void rejectImplicitMerges(Set<RevCommit> mergedParents) throws IOException {
- if (!mergedParents.isEmpty()) {
- Ref targetRef = allRefs().get(magicBranch.dest.get());
- if (targetRef != null) {
- RevWalk rw = receivePack.getRevWalk();
- RevCommit tip = rw.parseCommit(targetRef.getObjectId());
- boolean containsImplicitMerges = true;
- for (RevCommit p : mergedParents) {
- containsImplicitMerges &= !rw.isMergedInto(p, tip);
- }
-
- if (containsImplicitMerges) {
- rw.reset();
+ try (TraceTimer traceTimer = newTimer("rejectImplicitMerges")) {
+ if (!mergedParents.isEmpty()) {
+ Ref targetRef = allRefs().get(magicBranch.dest.branch());
+ if (targetRef != null) {
+ RevWalk rw = receivePack.getRevWalk();
+ RevCommit tip = rw.parseCommit(targetRef.getObjectId());
+ boolean containsImplicitMerges = true;
for (RevCommit p : mergedParents) {
- rw.markStart(p);
+ containsImplicitMerges &= !rw.isMergedInto(p, tip);
}
- rw.markUninteresting(tip);
- RevCommit c;
- while ((c = rw.next()) != null) {
- rw.parseBody(c);
- messages.add(
- new CommitValidationMessage(
- "Implicit Merge of " + c.abbreviate(7).name() + " " + c.getShortMessage(),
- ValidationMessage.Type.ERROR));
+
+ if (containsImplicitMerges) {
+ rw.reset();
+ for (RevCommit p : mergedParents) {
+ rw.markStart(p);
+ }
+ rw.markUninteresting(tip);
+ RevCommit c;
+ while ((c = rw.next()) != null) {
+ rw.parseBody(c);
+ messages.add(
+ new CommitValidationMessage(
+ "Implicit Merge of "
+ + abbreviateName(c, rw.getObjectReader())
+ + " "
+ + c.getShortMessage(),
+ ValidationMessage.Type.ERROR));
+ }
+ reject(magicBranch.cmd, "implicit merges detected");
}
- reject(magicBranch.cmd, "implicit merges detected");
}
}
}
@@ -2356,20 +2434,23 @@ class ReceiveCommits {
// Mark all branch tips as uninteresting in the given revwalk,
// so we get only the new commits when walking rw.
private void markHeadsAsUninteresting(RevWalk rw, @Nullable String forRef) {
- int i = 0;
- for (Ref ref : allRefs().values()) {
- if ((ref.getName().startsWith(R_HEADS) || ref.getName().equals(forRef))
- && ref.getObjectId() != null) {
- try {
- rw.markUninteresting(rw.parseCommit(ref.getObjectId()));
- i++;
- } catch (IOException e) {
- logger.atWarning().withCause(e).log(
- "Invalid ref %s in %s", ref.getName(), project.getName());
+ try (TraceTimer traceTimer =
+ newTimer("markHeadsAsUninteresting", Metadata.builder().branchName(forRef))) {
+ int i = 0;
+ for (Ref ref : allRefs().values()) {
+ if ((ref.getName().startsWith(R_HEADS) || ref.getName().equals(forRef))
+ && ref.getObjectId() != null) {
+ try {
+ rw.markUninteresting(rw.parseCommit(ref.getObjectId()));
+ i++;
+ } catch (IOException e) {
+ logger.atWarning().withCause(e).log(
+ "Invalid ref %s in %s", ref.getName(), project.getName());
+ }
}
}
+ logger.atFine().log("Marked %d heads as uninteresting", i);
}
- logger.atFine().log("Marked %d heads as uninteresting", i);
}
private static boolean isValidChangeId(String idStr) {
@@ -2390,12 +2471,16 @@ class ReceiveCommits {
}
private ChangeLookup lookupByChangeKey(RevCommit c, Change.Key key) {
- return new ChangeLookup(c, key, queryProvider.get().byBranchKey(magicBranch.dest, key));
+ try (TraceTimer traceTimer = newTimer("lookupByChangeKey")) {
+ return new ChangeLookup(c, key, queryProvider.get().byBranchKey(magicBranch.dest, key));
+ }
}
private ChangeLookup lookupByCommit(RevCommit c) {
- return new ChangeLookup(
- c, null, queryProvider.get().byBranchCommit(magicBranch.dest, c.getName()));
+ try (TraceTimer traceTimer = newTimer("lookupByCommit")) {
+ return new ChangeLookup(
+ c, null, queryProvider.get().byBranchCommit(magicBranch.dest, c.getName()));
+ }
}
/** Represents a commit for which a Change should be created. */
@@ -2418,103 +2503,97 @@ class ReceiveCommits {
}
private void setChangeId(int id) {
- possiblyOverrideWorkInProgress();
-
- changeId = new Change.Id(id);
- ins =
- changeInserterFactory
- .create(changeId, commit, refName)
- .setTopic(magicBranch.topic)
- .setPrivate(setChangeAsPrivate)
- .setWorkInProgress(magicBranch.workInProgress)
- // Changes already validated in validateNewCommits.
- .setValidate(false);
-
- if (magicBranch.merged) {
- ins.setStatus(Change.Status.MERGED);
- }
- cmd = new ReceiveCommand(ObjectId.zeroId(), commit, ins.getPatchSetId().toRefName());
- if (receivePack.getPushCertificate() != null) {
- ins.setPushCertificate(receivePack.getPushCertificate().toTextWithSignature());
- }
- }
-
- private void possiblyOverrideWorkInProgress() {
- // When wip or ready explicitly provided, leave it as is.
- if (magicBranch.workInProgress || magicBranch.ready) {
- return;
+ try (TraceTimer traceTimer = newTimer(CreateRequest.class, "setChangeId")) {
+ changeId = Change.id(id);
+ ins =
+ changeInserterFactory
+ .create(changeId, commit, refName)
+ .setTopic(magicBranch.topic)
+ .setPrivate(setChangeAsPrivate)
+ .setWorkInProgress(magicBranch.shouldSetWorkInProgressOnNewChanges())
+ // Changes already validated in validateNewCommits.
+ .setValidate(false);
+
+ if (magicBranch.merged) {
+ ins.setStatus(Change.Status.MERGED);
+ }
+ cmd = new ReceiveCommand(ObjectId.zeroId(), commit, ins.getPatchSetId().toRefName());
+ if (receivePack.getPushCertificate() != null) {
+ ins.setPushCertificate(receivePack.getPushCertificate().toTextWithSignature());
+ }
}
- magicBranch.workInProgress =
- projectState.is(BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT)
- || firstNonNull(user.state().getGeneralPreferences().workInProgressByDefault, false);
}
private void addOps(BatchUpdate bu) throws RestApiException {
- checkState(changeId != null, "must call setChangeId before addOps");
- try {
- RevWalk rw = receivePack.getRevWalk();
- rw.parseBody(commit);
- final PatchSet.Id psId = ins.setGroups(groups).getPatchSetId();
- Account.Id me = user.getAccountId();
- List<FooterLine> footerLines = commit.getFooterLines();
- requireNonNull(magicBranch);
-
- // TODO(dborowitz): Support reviewers by email from footers? Maybe not: kernel developers
- // with AOSP accounts already complain about these notifications, and that would make it
- // worse. Might be better to get rid of the feature entirely:
- // https://groups.google.com/d/topic/repo-discuss/tIFxY7L4DXk/discussion
- MailRecipients fromFooters = getRecipientsFromFooters(accountResolver, footerLines);
- fromFooters.remove(me);
-
- Map<String, Short> approvals = magicBranch.labels;
- StringBuilder msg =
- new StringBuilder(
- ApprovalsUtil.renderMessageWithApprovals(
- psId.get(), approvals, Collections.emptyMap()));
- msg.append('.');
- if (!Strings.isNullOrEmpty(magicBranch.message)) {
- msg.append("\n").append(magicBranch.message);
- }
-
- bu.setNotify(magicBranch.getNotifyForNewChange());
- bu.insertChange(
- ins.setReviewersAndCcsAsStrings(
- magicBranch.getCombinedReviewers(fromFooters),
- magicBranch.getCombinedCcs(fromFooters))
- .setApprovals(approvals)
- .setMessage(msg.toString())
- .setRequestScopePropagator(requestScopePropagator)
- .setSendMail(true)
- .setPatchSetDescription(magicBranch.message));
- if (!magicBranch.hashtags.isEmpty()) {
- // Any change owner is allowed to add hashtags when creating a change.
- bu.addOp(
- changeId,
- hashtagsFactory.create(new HashtagsInput(magicBranch.hashtags)).setFireEvent(false));
- }
- if (!Strings.isNullOrEmpty(magicBranch.topic)) {
+ try (TraceTimer traceTimer = newTimer(CreateRequest.class, "addOps")) {
+ checkState(changeId != null, "must call setChangeId before addOps");
+ try {
+ RevWalk rw = receivePack.getRevWalk();
+ rw.parseBody(commit);
+ final PatchSet.Id psId = ins.setGroups(groups).getPatchSetId();
+ Account.Id me = user.getAccountId();
+ List<FooterLine> footerLines = commit.getFooterLines();
+ requireNonNull(magicBranch);
+
+ // TODO(dborowitz): Support reviewers by email from footers? Maybe not: kernel developers
+ // with AOSP accounts already complain about these notifications, and that would make it
+ // worse. Might be better to get rid of the feature entirely:
+ // https://groups.google.com/d/topic/repo-discuss/tIFxY7L4DXk/discussion
+ MailRecipients fromFooters = getRecipientsFromFooters(accountResolver, footerLines);
+ fromFooters.remove(me);
+
+ Map<String, Short> approvals = magicBranch.labels;
+ StringBuilder msg =
+ new StringBuilder(
+ ApprovalsUtil.renderMessageWithApprovals(
+ psId.get(), approvals, Collections.emptyMap()));
+ msg.append('.');
+ if (!Strings.isNullOrEmpty(magicBranch.message)) {
+ msg.append("\n").append(magicBranch.message);
+ }
+
+ bu.setNotify(magicBranch.getNotifyForNewChange());
+ bu.insertChange(
+ ins.setReviewersAndCcsAsStrings(
+ magicBranch.getCombinedReviewers(fromFooters),
+ magicBranch.getCombinedCcs(fromFooters))
+ .setApprovals(approvals)
+ .setMessage(msg.toString())
+ .setRequestScopePropagator(requestScopePropagator)
+ .setSendMail(true)
+ .setPatchSetDescription(magicBranch.message));
+ if (!magicBranch.hashtags.isEmpty()) {
+ // Any change owner is allowed to add hashtags when creating a change.
+ bu.addOp(
+ changeId,
+ hashtagsFactory
+ .create(new HashtagsInput(magicBranch.hashtags))
+ .setFireEvent(false));
+ }
+ if (!Strings.isNullOrEmpty(magicBranch.topic)) {
+ bu.addOp(
+ changeId,
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ctx.getUpdate(psId).setTopic(magicBranch.topic);
+ return true;
+ }
+ });
+ }
bu.addOp(
changeId,
new BatchUpdateOp() {
@Override
public boolean updateChange(ChangeContext ctx) {
- ctx.getUpdate(psId).setTopic(magicBranch.topic);
- return true;
+ CreateRequest.this.change = ctx.getChange();
+ return false;
}
});
+ bu.addOp(changeId, new ChangeProgressOp(progress));
+ } catch (Exception e) {
+ throw asRestApiException(e);
}
- bu.addOp(
- changeId,
- new BatchUpdateOp() {
- @Override
- public boolean updateChange(ChangeContext ctx) {
- CreateRequest.this.change = ctx.getChange();
- return false;
- }
- });
- bu.addOp(changeId, new ChangeProgressOp(progress));
- } catch (Exception e) {
- throw INSERT_EXCEPTION.apply(e);
}
}
}
@@ -2522,67 +2601,88 @@ class ReceiveCommits {
private void submit(Collection<CreateRequest> create, Collection<ReplaceRequest> replace)
throws RestApiException, UpdateException, IOException, ConfigInvalidException,
PermissionBackendException {
- Map<ObjectId, Change> bySha = Maps.newHashMapWithExpectedSize(create.size() + replace.size());
- for (CreateRequest r : create) {
+ try (TraceTimer traceTimer = newTimer("submit")) {
+ Map<ObjectId, Change> bySha = Maps.newHashMapWithExpectedSize(create.size() + replace.size());
+ for (CreateRequest r : create) {
+ requireNonNull(
+ r.change,
+ () -> String.format("cannot submit new change %s; op may not have run", r.changeId));
+ bySha.put(r.commit, r.change);
+ }
+ for (ReplaceRequest r : replace) {
+ bySha.put(r.newCommitId, r.notes.getChange());
+ }
+ Change tipChange = bySha.get(magicBranch.cmd.getNewId());
requireNonNull(
- r.change,
- () -> String.format("cannot submit new change %s; op may not have run", r.changeId));
- bySha.put(r.commit, r.change);
- }
- for (ReplaceRequest r : replace) {
- bySha.put(r.newCommitId, r.notes.getChange());
- }
- Change tipChange = bySha.get(magicBranch.cmd.getNewId());
- requireNonNull(
- tipChange,
- () ->
- String.format(
- "tip of push does not correspond to a change; found these changes: %s", bySha));
- logger.atFine().log(
- "Processing submit with tip change %s (%s)", tipChange.getId(), magicBranch.cmd.getNewId());
- try (MergeOp op = mergeOpProvider.get()) {
- op.merge(tipChange, user, false, new SubmitInput(), false);
+ tipChange,
+ () ->
+ String.format(
+ "tip of push does not correspond to a change; found these changes: %s", bySha));
+ logger.atFine().log(
+ "Processing submit with tip change %s (%s)",
+ tipChange.getId(), magicBranch.cmd.getNewId());
+ try (MergeOp op = mergeOpProvider.get()) {
+ SubmitInput submitInput = new SubmitInput();
+ submitInput.notify = magicBranch.notifyHandling;
+ submitInput.notifyDetails = new HashMap<>();
+ submitInput.notifyDetails.put(
+ RecipientType.TO,
+ new NotifyInfo(magicBranch.notifyTo.stream().map(Object::toString).collect(toList())));
+ submitInput.notifyDetails.put(
+ RecipientType.CC,
+ new NotifyInfo(magicBranch.notifyCc.stream().map(Object::toString).collect(toList())));
+ submitInput.notifyDetails.put(
+ RecipientType.BCC,
+ new NotifyInfo(magicBranch.notifyBcc.stream().map(Object::toString).collect(toList())));
+ op.merge(tipChange, user, false, submitInput, false);
+ }
}
}
private void preparePatchSetsForReplace(List<CreateRequest> newChanges) {
- try {
- readChangesForReplace();
- for (ReplaceRequest req : replaceByChange.values()) {
- if (req.inputCommand.getResult() == NOT_ATTEMPTED) {
- req.validateNewPatchSet();
+ try (TraceTimer traceTimer =
+ newTimer(
+ "preparePatchSetsForReplace", Metadata.builder().resourceCount(newChanges.size()))) {
+ try {
+ readChangesForReplace();
+ for (ReplaceRequest req : replaceByChange.values()) {
+ if (req.inputCommand.getResult() == NOT_ATTEMPTED) {
+ req.validateNewPatchSet();
+ }
}
- }
- } catch (StorageException err) {
- logger.atSevere().withCause(err).log(
- "Cannot read database before replacement for project %s", project.getName());
- rejectRemainingRequests(replaceByChange.values(), INTERNAL_SERVER_ERROR);
- } catch (IOException | PermissionBackendException err) {
- logger.atSevere().withCause(err).log(
- "Cannot read repository before replacement for project %s", project.getName());
- rejectRemainingRequests(replaceByChange.values(), INTERNAL_SERVER_ERROR);
- }
- logger.atFine().log("Read %d changes to replace", replaceByChange.size());
-
- if (magicBranch != null && magicBranch.cmd.getResult() != NOT_ATTEMPTED) {
- // Cancel creations tied to refs/for/ or refs/drafts/ command.
- for (ReplaceRequest req : replaceByChange.values()) {
- if (req.inputCommand == magicBranch.cmd && req.cmd != null) {
+ } catch (StorageException err) {
+ logger.atSevere().withCause(err).log(
+ "Cannot read database before replacement for project %s", project.getName());
+ rejectRemainingRequests(replaceByChange.values(), INTERNAL_SERVER_ERROR);
+ } catch (IOException | PermissionBackendException err) {
+ logger.atSevere().withCause(err).log(
+ "Cannot read repository before replacement for project %s", project.getName());
+ rejectRemainingRequests(replaceByChange.values(), INTERNAL_SERVER_ERROR);
+ }
+ logger.atFine().log("Read %d changes to replace", replaceByChange.size());
+
+ if (magicBranch != null && magicBranch.cmd.getResult() != NOT_ATTEMPTED) {
+ // Cancel creations tied to refs/for/ command.
+ for (ReplaceRequest req : replaceByChange.values()) {
+ if (req.inputCommand == magicBranch.cmd && req.cmd != null) {
+ req.cmd.setResult(Result.REJECTED_OTHER_REASON, "aborted");
+ }
+ }
+ for (CreateRequest req : newChanges) {
req.cmd.setResult(Result.REJECTED_OTHER_REASON, "aborted");
}
}
- for (CreateRequest req : newChanges) {
- req.cmd.setResult(Result.REJECTED_OTHER_REASON, "aborted");
- }
}
}
private void readChangesForReplace() {
- Collection<ChangeNotes> allNotes =
- notesFactory.create(
- replaceByChange.values().stream().map(r -> r.ontoChange).collect(toList()));
- for (ChangeNotes notes : allNotes) {
- replaceByChange.get(notes.getChangeId()).notes = notes;
+ try (TraceTimer traceTimer = newTimer("readChangesForReplace")) {
+ Collection<ChangeNotes> allNotes =
+ notesFactory.create(
+ replaceByChange.values().stream().map(r -> r.ontoChange).collect(toList()));
+ for (ChangeNotes notes : allNotes) {
+ replaceByChange.get(notes.getChangeId()).notes = notes;
+ }
}
}
@@ -2644,24 +2744,26 @@ class ReceiveCommits {
* @throws PermissionBackendException
*/
boolean validateNewPatchSet() throws IOException, PermissionBackendException {
- if (!validateNewPatchSetNoteDb()) {
- return false;
- }
- sameTreeWarning();
-
- if (magicBranch != null) {
- validateMagicBranchWipStatusChange();
- if (inputCommand.getResult() != NOT_ATTEMPTED) {
+ try (TraceTimer traceTimer = newTimer("validateNewPatchSet")) {
+ if (!validateNewPatchSetNoteDb()) {
return false;
}
+ sameTreeWarning();
- if (magicBranch.edit || magicBranch.draft) {
- return newEdit();
+ if (magicBranch != null) {
+ validateMagicBranchWipStatusChange();
+ if (inputCommand.getResult() != NOT_ATTEMPTED) {
+ return false;
+ }
+
+ if (magicBranch.edit) {
+ return newEdit();
+ }
}
- }
- newPatchSet();
- return true;
+ newPatchSet();
+ return true;
+ }
}
boolean validateNewPatchSetForAutoClose() throws IOException, PermissionBackendException {
@@ -2675,59 +2777,63 @@ class ReceiveCommits {
/** Validates the new PS against permissions and notedb status. */
private boolean validateNewPatchSetNoteDb() throws IOException, PermissionBackendException {
- if (notes == null) {
- reject(inputCommand, "change " + ontoChange + " not found");
- return false;
- }
-
- Change change = notes.getChange();
- priorPatchSet = change.currentPatchSetId();
- if (!revisions.containsValue(priorPatchSet)) {
- reject(inputCommand, "change " + ontoChange + " missing revisions");
- return false;
- }
-
- RevCommit newCommit = receivePack.getRevWalk().parseCommit(newCommitId);
+ try (TraceTimer traceTimer = newTimer("validateNewPatchSetNoteDb")) {
+ if (notes == null) {
+ reject(inputCommand, "change " + ontoChange + " not found");
+ return false;
+ }
- // Not allowed to create a new patch set if the current patch set is locked.
- if (psUtil.isPatchSetLocked(notes)) {
- reject(inputCommand, "cannot add patch set to " + ontoChange + ".");
- return false;
- }
+ Change change = notes.getChange();
+ priorPatchSet = change.currentPatchSetId();
+ if (!revisions.containsValue(priorPatchSet)) {
+ reject(inputCommand, "change " + ontoChange + " missing revisions");
+ return false;
+ }
- try {
- permissions.change(notes).check(ChangePermission.ADD_PATCH_SET);
- } catch (AuthException no) {
- reject(inputCommand, "cannot add patch set to " + ontoChange + ".");
- return false;
- }
+ RevCommit newCommit = receivePack.getRevWalk().parseCommit(newCommitId);
- if (change.isClosed()) {
- reject(inputCommand, "change " + ontoChange + " closed");
- return false;
- } else if (revisions.containsKey(newCommit)) {
- reject(inputCommand, "commit already exists (in the change)");
- return false;
- }
+ // Not allowed to create a new patch set if the current patch set is locked.
+ if (psUtil.isPatchSetLocked(notes)) {
+ reject(inputCommand, "cannot add patch set to " + ontoChange + ".");
+ return false;
+ }
- for (Ref r : receivePack.getRepository().getRefDatabase().getRefsByPrefix("refs/changes")) {
- if (r.getObjectId().equals(newCommit)) {
- reject(inputCommand, "commit already exists (in the project)");
+ try {
+ permissions.change(notes).check(ChangePermission.ADD_PATCH_SET);
+ } catch (AuthException no) {
+ reject(inputCommand, "cannot add patch set to " + ontoChange + ".");
return false;
}
- }
- for (RevCommit prior : revisions.keySet()) {
- // Don't allow a change to directly depend upon itself. This is a
- // very common error due to users making a new commit rather than
- // amending when trying to address review comments.
- if (receivePack.getRevWalk().isMergedInto(prior, newCommit)) {
- reject(inputCommand, SAME_CHANGE_ID_IN_MULTIPLE_CHANGES);
+ if (change.isClosed()) {
+ reject(inputCommand, "change " + ontoChange + " closed");
+ return false;
+ } else if (revisions.containsKey(newCommit)) {
+ reject(inputCommand, "commit already exists (in the change)");
return false;
}
- }
- return true;
+ for (Ref r : receivePack.getRepository().getRefDatabase().getRefsByPrefix("refs/changes")) {
+ if (r.getObjectId().equals(newCommit)) {
+ reject(inputCommand, "commit already exists (in the project)");
+ return false;
+ }
+ }
+
+ try (TraceTimer traceTimer2 = newTimer("validateNewPatchSetNoteDb#isMergedInto")) {
+ for (RevCommit prior : revisions.keySet()) {
+ // Don't allow a change to directly depend upon itself. This is a
+ // very common error due to users making a new commit rather than
+ // amending when trying to address review comments.
+ if (receivePack.getRevWalk().isMergedInto(prior, newCommit)) {
+ reject(inputCommand, SAME_CHANGE_ID_IN_MULTIPLE_CHANGES);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
}
/** Validates whether the WIP change is allowed. Rejects inputCommand if not. */
@@ -2756,39 +2862,41 @@ class ReceiveCommits {
/** prints a warning if the new PS has the same tree as the previous commit. */
private void sameTreeWarning() throws IOException {
- RevWalk rw = receivePack.getRevWalk();
- RevCommit newCommit = rw.parseCommit(newCommitId);
- RevCommit priorCommit = revisions.inverse().get(priorPatchSet);
-
- if (newCommit.getTree().equals(priorCommit.getTree())) {
- rw.parseBody(newCommit);
- rw.parseBody(priorCommit);
- boolean messageEq =
- Objects.equals(newCommit.getFullMessage(), priorCommit.getFullMessage());
- boolean parentsEq = parentsEqual(newCommit, priorCommit);
- boolean authorEq = authorEqual(newCommit, priorCommit);
- ObjectReader reader = receivePack.getRevWalk().getObjectReader();
-
- if (messageEq && parentsEq && authorEq) {
- addMessage(
- String.format(
- "warning: no changes between prior commit %s and new commit %s",
- reader.abbreviate(priorCommit).name(), reader.abbreviate(newCommit).name()));
- } else {
- StringBuilder msg = new StringBuilder();
- msg.append("warning: ").append(reader.abbreviate(newCommit).name());
- msg.append(":");
- msg.append(" no files changed");
- if (!authorEq) {
- msg.append(", author changed");
- }
- if (!messageEq) {
- msg.append(", message updated");
- }
- if (!parentsEq) {
- msg.append(", was rebased");
+ try (TraceTimer traceTimer = newTimer("sameTreeWarning")) {
+ RevWalk rw = receivePack.getRevWalk();
+ RevCommit newCommit = rw.parseCommit(newCommitId);
+ RevCommit priorCommit = revisions.inverse().get(priorPatchSet);
+
+ if (newCommit.getTree().equals(priorCommit.getTree())) {
+ rw.parseBody(newCommit);
+ rw.parseBody(priorCommit);
+ boolean messageEq =
+ Objects.equals(newCommit.getFullMessage(), priorCommit.getFullMessage());
+ boolean parentsEq = parentsEqual(newCommit, priorCommit);
+ boolean authorEq = authorEqual(newCommit, priorCommit);
+ ObjectReader reader = receivePack.getRevWalk().getObjectReader();
+
+ if (messageEq && parentsEq && authorEq) {
+ addMessage(
+ String.format(
+ "warning: no changes between prior commit %s and new commit %s",
+ abbreviateName(priorCommit, reader), abbreviateName(newCommit, reader)));
+ } else {
+ StringBuilder msg = new StringBuilder();
+ msg.append("warning: ").append(abbreviateName(newCommit, reader));
+ msg.append(":");
+ msg.append(" no files changed");
+ if (!authorEq) {
+ msg.append(", author changed");
+ }
+ if (!messageEq) {
+ msg.append(", message updated");
+ }
+ if (!parentsEq) {
+ msg.append(", was rebased");
+ }
+ addMessage(msg.toString());
}
- addMessage(msg.toString());
}
}
}
@@ -2798,33 +2906,36 @@ class ReceiveCommits {
* failure.
*/
private boolean newEdit() {
- psId = notes.getChange().currentPatchSetId();
- Optional<ChangeEdit> edit;
+ try (TraceTimer traceTimer = newTimer("newEdit")) {
+ psId = notes.getChange().currentPatchSetId();
+ Optional<ChangeEdit> edit;
- try {
- edit = editUtil.byChange(notes, user);
- } catch (AuthException | IOException e) {
- logger.atSevere().withCause(e).log("Cannot retrieve edit");
- return false;
- }
+ try {
+ edit = editUtil.byChange(notes, user);
+ } catch (AuthException | IOException e) {
+ logger.atSevere().withCause(e).log("Cannot retrieve edit");
+ return false;
+ }
- if (edit.isPresent()) {
- if (edit.get().getBasePatchSet().getId().equals(psId)) {
- // replace edit
- cmd =
- new ReceiveCommand(edit.get().getEditCommit(), newCommitId, edit.get().getRefName());
+ if (edit.isPresent()) {
+ if (edit.get().getBasePatchSet().id().equals(psId)) {
+ // replace edit
+ cmd =
+ new ReceiveCommand(
+ edit.get().getEditCommit(), newCommitId, edit.get().getRefName());
+ } else {
+ // delete old edit ref on rebase
+ prev =
+ new ReceiveCommand(
+ edit.get().getEditCommit(), ObjectId.zeroId(), edit.get().getRefName());
+ createEditCommand();
+ }
} else {
- // delete old edit ref on rebase
- prev =
- new ReceiveCommand(
- edit.get().getEditCommit(), ObjectId.zeroId(), edit.get().getRefName());
createEditCommand();
}
- } else {
- createEditCommand();
- }
- return true;
+ return true;
+ }
}
/** Creates a ReceiveCommand for a new edit. */
@@ -2838,47 +2949,52 @@ class ReceiveCommits {
/** Updates 'this' to add a new patchset. */
private void newPatchSet() throws IOException {
- RevCommit newCommit = receivePack.getRevWalk().parseCommit(newCommitId);
- psId =
- ChangeUtil.nextPatchSetIdFromAllRefsMap(allRefs(), notes.getChange().currentPatchSetId());
- info = patchSetInfoFactory.get(receivePack.getRevWalk(), newCommit, psId);
- cmd = new ReceiveCommand(ObjectId.zeroId(), newCommitId, psId.toRefName());
+ try (TraceTimer traceTimer = newTimer("newPatchSet")) {
+ RevCommit newCommit = receivePack.getRevWalk().parseCommit(newCommitId);
+ psId =
+ ChangeUtil.nextPatchSetIdFromAllRefsMap(
+ allRefs(), notes.getChange().currentPatchSetId());
+ info = patchSetInfoFactory.get(receivePack.getRevWalk(), newCommit, psId);
+ cmd = new ReceiveCommand(ObjectId.zeroId(), newCommitId, psId.toRefName());
+ }
}
void addOps(BatchUpdate bu, @Nullable Task progress) throws IOException {
- if (magicBranch != null && (magicBranch.edit || magicBranch.draft)) {
- bu.addOp(notes.getChangeId(), new ReindexOnlyOp());
- if (prev != null) {
- bu.addRepoOnlyOp(new UpdateOneRefOp(prev));
+ try (TraceTimer traceTimer = newTimer("addOps")) {
+ if (magicBranch != null && magicBranch.edit) {
+ bu.addOp(notes.getChangeId(), new ReindexOnlyOp());
+ if (prev != null) {
+ bu.addRepoOnlyOp(new UpdateOneRefOp(prev));
+ }
+ bu.addRepoOnlyOp(new UpdateOneRefOp(cmd));
+ return;
+ }
+ RevWalk rw = receivePack.getRevWalk();
+ // TODO(dborowitz): Move to ReplaceOp#updateRepo.
+ RevCommit newCommit = rw.parseCommit(newCommitId);
+ rw.parseBody(newCommit);
+
+ RevCommit priorCommit = revisions.inverse().get(priorPatchSet);
+ replaceOp =
+ replaceOpFactory
+ .create(
+ projectState,
+ notes.getChange().getDest(),
+ checkMergedInto,
+ checkMergedInto ? inputCommand.getNewId().name() : null,
+ priorPatchSet,
+ priorCommit,
+ psId,
+ newCommit,
+ info,
+ groups,
+ magicBranch,
+ receivePack.getPushCertificate())
+ .setRequestScopePropagator(requestScopePropagator);
+ bu.addOp(notes.getChangeId(), replaceOp);
+ if (progress != null) {
+ bu.addOp(notes.getChangeId(), new ChangeProgressOp(progress));
}
- bu.addRepoOnlyOp(new UpdateOneRefOp(cmd));
- return;
- }
- RevWalk rw = receivePack.getRevWalk();
- // TODO(dborowitz): Move to ReplaceOp#updateRepo.
- RevCommit newCommit = rw.parseCommit(newCommitId);
- rw.parseBody(newCommit);
-
- RevCommit priorCommit = revisions.inverse().get(priorPatchSet);
- replaceOp =
- replaceOpFactory
- .create(
- projectState,
- notes.getChange().getDest(),
- checkMergedInto,
- checkMergedInto ? inputCommand.getNewId().name() : null,
- priorPatchSet,
- priorCommit,
- psId,
- newCommit,
- info,
- groups,
- magicBranch,
- receivePack.getPushCertificate())
- .setRequestScopePropagator(requestScopePropagator);
- bu.addOp(notes.getChangeId(), replaceOp);
- if (progress != null) {
- bu.addOp(notes.getChangeId(), new ChangeProgressOp(progress));
}
}
@@ -2899,12 +3015,12 @@ class ReceiveCommits {
private void addOps(BatchUpdate bu) {
bu.addOp(
- psId.getParentKey(),
+ psId.changeId(),
new BatchUpdateOp() {
@Override
public boolean updateChange(ChangeContext ctx) {
PatchSet ps = psUtil.get(ctx.getNotes(), psId);
- List<String> oldGroups = ps.getGroups();
+ List<String> oldGroups = ps.groups();
if (oldGroups == null) {
if (groups == null) {
return false;
@@ -2912,7 +3028,7 @@ class ReceiveCommits {
} else if (sameGroups(oldGroups, groups)) {
return false;
}
- psUtil.setGroups(ctx.getUpdate(psId), ps, groups);
+ ctx.getUpdate(psId).setGroups(groups);
return true;
}
});
@@ -2976,7 +3092,11 @@ class ReceiveCommits {
}
private void initChangeRefMaps() {
- if (refsByChange == null) {
+ if (refsByChange != null) {
+ return;
+ }
+
+ try (TraceTimer traceTimer = newTimer("initChangeRefMaps")) {
int estRefsPerChange = 4;
refsById = MultimapBuilder.hashKeys().arrayListValues().build();
refsByChange =
@@ -2989,7 +3109,7 @@ class ReceiveCommits {
PatchSet.Id psId = PatchSet.Id.fromRef(ref.getName());
if (psId != null) {
refsById.put(obj, ref);
- refsByChange.put(psId.getParentKey(), ref);
+ refsByChange.put(psId.changeId(), ref);
}
}
}
@@ -3035,17 +3155,19 @@ class ReceiveCommits {
// Run RefValidators on the command. If any validator fails, the command status is set to
// REJECTED, and the return value is 'false'
private boolean validRefOperation(ReceiveCommand cmd) {
- RefOperationValidators refValidators = refValidatorsFactory.create(getProject(), user, cmd);
+ try (TraceTimer traceTimer = newTimer("validRefOperation")) {
+ RefOperationValidators refValidators = refValidatorsFactory.create(getProject(), user, cmd);
- try {
- messages.addAll(refValidators.validateForRefOperation());
- } catch (RefOperationValidationException e) {
- messages.addAll(Lists.newArrayList(e.getMessages()));
- reject(cmd, e.getMessage());
- return false;
- }
+ try {
+ messages.addAll(refValidators.validateForRefOperation());
+ } catch (RefOperationValidationException e) {
+ messages.addAll(e.getMessages());
+ reject(cmd, e.getMessage());
+ return false;
+ }
- return true;
+ return true;
+ }
}
/**
@@ -3053,184 +3175,194 @@ class ReceiveCommits {
*
* <p>On validation failure, the command is rejected.
*/
- private void validateRegularPushCommits(Branch.NameKey branch, ReceiveCommand cmd)
+ private void validateRegularPushCommits(BranchNameKey branch, ReceiveCommand cmd)
throws PermissionBackendException {
- boolean skipValidation =
- !RefNames.REFS_CONFIG.equals(cmd.getRefName())
- && !(MagicBranch.isMagicBranch(cmd.getRefName())
- || NEW_PATCHSET_PATTERN.matcher(cmd.getRefName()).matches())
- && pushOptions.containsKey(PUSH_OPTION_SKIP_VALIDATION);
- if (skipValidation) {
- if (projectState.is(BooleanProjectConfig.USE_SIGNED_OFF_BY)) {
- reject(cmd, "requireSignedOffBy prevents option " + PUSH_OPTION_SKIP_VALIDATION);
- return;
- }
+ try (TraceTimer traceTimer =
+ newTimer("validateRegularPushCommits", Metadata.builder().branchName(branch.branch()))) {
+ boolean skipValidation =
+ !RefNames.REFS_CONFIG.equals(cmd.getRefName())
+ && !(MagicBranch.isMagicBranch(cmd.getRefName())
+ || NEW_PATCHSET_PATTERN.matcher(cmd.getRefName()).matches())
+ && pushOptions.containsKey(PUSH_OPTION_SKIP_VALIDATION);
+ if (skipValidation) {
+ if (projectState.is(BooleanProjectConfig.USE_SIGNED_OFF_BY)) {
+ reject(cmd, "requireSignedOffBy prevents option " + PUSH_OPTION_SKIP_VALIDATION);
+ return;
+ }
- Optional<AuthException> err =
- checkRefPermission(permissions.ref(branch.get()), RefPermission.SKIP_VALIDATION);
- if (err.isPresent()) {
- rejectProhibited(cmd, err.get());
- return;
- }
- if (!Iterables.isEmpty(rejectCommits)) {
- reject(cmd, "reject-commits prevents " + PUSH_OPTION_SKIP_VALIDATION);
+ Optional<AuthException> err =
+ checkRefPermission(permissions.ref(branch.branch()), RefPermission.SKIP_VALIDATION);
+ if (err.isPresent()) {
+ rejectProhibited(cmd, err.get());
+ return;
+ }
+ if (!Iterables.isEmpty(rejectCommits)) {
+ reject(cmd, "reject-commits prevents " + PUSH_OPTION_SKIP_VALIDATION);
+ }
}
- }
- BranchCommitValidator validator = commitValidatorFactory.create(projectState, branch, user);
- RevWalk walk = receivePack.getRevWalk();
- walk.reset();
- walk.sort(RevSort.NONE);
- try {
- RevObject parsedObject = walk.parseAny(cmd.getNewId());
- if (!(parsedObject instanceof RevCommit)) {
- return;
- }
- ListMultimap<ObjectId, Ref> existing = changeRefsById();
- walk.markStart((RevCommit) parsedObject);
- markHeadsAsUninteresting(walk, cmd.getRefName());
- int limit = receiveConfig.maxBatchCommits;
- int n = 0;
- for (RevCommit c; (c = walk.next()) != null; ) {
- // Even if skipValidation is set, we still get here when at least one plugin
- // commit validator requires to validate all commits. In this case, however,
- // we don't need to check the commit limit.
- if (++n > limit && !skipValidation) {
- logger.atFine().log("Number of new commits exceeds limit of %d", limit);
- reject(
- cmd,
- String.format(
- "more than %d commits, and %s not set", limit, PUSH_OPTION_SKIP_VALIDATION));
+ BranchCommitValidator validator = commitValidatorFactory.create(projectState, branch, user);
+ RevWalk walk = receivePack.getRevWalk();
+ walk.reset();
+ walk.sort(RevSort.NONE);
+ try {
+ RevObject parsedObject = walk.parseAny(cmd.getNewId());
+ if (!(parsedObject instanceof RevCommit)) {
return;
}
- if (existing.keySet().contains(c)) {
- continue;
- }
+ ListMultimap<ObjectId, Ref> existing = changeRefsById();
+ walk.markStart((RevCommit) parsedObject);
+ markHeadsAsUninteresting(walk, cmd.getRefName());
+ int limit = receiveConfig.maxBatchCommits;
+ int n = 0;
+ for (RevCommit c; (c = walk.next()) != null; ) {
+ // Even if skipValidation is set, we still get here when at least one plugin
+ // commit validator requires to validate all commits. In this case, however,
+ // we don't need to check the commit limit.
+ if (++n > limit && !skipValidation) {
+ logger.atFine().log("Number of new commits exceeds limit of %d", limit);
+ reject(
+ cmd,
+ String.format(
+ "more than %d commits, and %s not set", limit, PUSH_OPTION_SKIP_VALIDATION));
+ return;
+ }
+ if (existing.keySet().contains(c)) {
+ continue;
+ }
- if (!validator.validCommit(
- walk.getObjectReader(), cmd, c, false, messages, rejectCommits, null, skipValidation)) {
- break;
+ BranchCommitValidator.Result validationResult =
+ validator.validateCommit(
+ walk.getObjectReader(), cmd, c, false, rejectCommits, null, skipValidation);
+ messages.addAll(validationResult.messages());
+ if (!validationResult.isValid()) {
+ break;
+ }
}
+ logger.atFine().log("Validated %d new commits", n);
+ } catch (IOException err) {
+ cmd.setResult(REJECTED_MISSING_OBJECT);
+ logger.atSevere().withCause(err).log(
+ "Invalid pack upload; one or more objects weren't sent");
}
- logger.atFine().log("Validated %d new commits", n);
- } catch (IOException err) {
- cmd.setResult(REJECTED_MISSING_OBJECT);
- logger.atSevere().withCause(err).log("Invalid pack upload; one or more objects weren't sent");
}
}
private void autoCloseChanges(ReceiveCommand cmd, Task progress) {
- logger.atFine().log("Starting auto-closing of changes");
- String refName = cmd.getRefName();
- Set<Change.Id> ids = new HashSet<>();
-
- // TODO(dborowitz): Combine this BatchUpdate with the main one in
- // handleRegularCommands
- try {
- retryHelper.execute(
- updateFactory -> {
- try (BatchUpdate bu =
- updateFactory.create(projectState.getNameKey(), user, TimeUtil.nowTs());
- ObjectInserter ins = repo.newObjectInserter();
- ObjectReader reader = ins.newReader();
- RevWalk rw = new RevWalk(reader)) {
- bu.setRepository(repo, rw, ins);
- // TODO(dborowitz): Teach BatchUpdate to ignore missing changes.
-
- RevCommit newTip = rw.parseCommit(cmd.getNewId());
- Branch.NameKey branch = new Branch.NameKey(project.getNameKey(), refName);
-
- rw.reset();
- rw.sort(RevSort.REVERSE);
- rw.markStart(newTip);
- if (!ObjectId.zeroId().equals(cmd.getOldId())) {
- rw.markUninteresting(rw.parseCommit(cmd.getOldId()));
- }
+ try (TraceTimer traceTimer = newTimer("autoCloseChanges")) {
+ logger.atFine().log("Starting auto-closing of changes");
+ String refName = cmd.getRefName();
+ Set<Change.Id> ids = new HashSet<>();
- ListMultimap<ObjectId, Ref> byCommit = changeRefsById();
- Map<Change.Key, ChangeNotes> byKey = null;
- List<ReplaceRequest> replaceAndClose = new ArrayList<>();
-
- int existingPatchSets = 0;
- int newPatchSets = 0;
- COMMIT:
- for (RevCommit c; (c = rw.next()) != null; ) {
- rw.parseBody(c);
-
- for (Ref ref : byCommit.get(c.copy())) {
- PatchSet.Id psId = PatchSet.Id.fromRef(ref.getName());
- Optional<ChangeNotes> notes = getChangeNotes(psId.getParentKey());
- if (notes.isPresent() && notes.get().getChange().getDest().equals(branch)) {
- existingPatchSets++;
- bu.addOp(notes.get().getChangeId(), setPrivateOpFactory.create(false, null));
- bu.addOp(
- psId.getParentKey(),
- mergedByPushOpFactory.create(
- requestScopePropagator, psId, refName, newTip.getId().getName()));
- continue COMMIT;
- }
+ // TODO(dborowitz): Combine this BatchUpdate with the main one in
+ // handleRegularCommands
+ try {
+ retryHelper.execute(
+ updateFactory -> {
+ try (BatchUpdate bu =
+ updateFactory.create(projectState.getNameKey(), user, TimeUtil.nowTs());
+ ObjectInserter ins = repo.newObjectInserter();
+ ObjectReader reader = ins.newReader();
+ RevWalk rw = new RevWalk(reader)) {
+ bu.setRepository(repo, rw, ins);
+ // TODO(dborowitz): Teach BatchUpdate to ignore missing changes.
+
+ RevCommit newTip = rw.parseCommit(cmd.getNewId());
+ BranchNameKey branch = BranchNameKey.create(project.getNameKey(), refName);
+
+ rw.reset();
+ rw.sort(RevSort.REVERSE);
+ rw.markStart(newTip);
+ if (!ObjectId.zeroId().equals(cmd.getOldId())) {
+ rw.markUninteresting(rw.parseCommit(cmd.getOldId()));
}
- for (String changeId : c.getFooterLines(CHANGE_ID)) {
- if (byKey == null) {
- byKey = executeIndexQuery(() -> openChangesByKeyByBranch(branch));
+ ListMultimap<ObjectId, Ref> byCommit = changeRefsById();
+ Map<Change.Key, ChangeNotes> byKey = null;
+ List<ReplaceRequest> replaceAndClose = new ArrayList<>();
+
+ int existingPatchSets = 0;
+ int newPatchSets = 0;
+ COMMIT:
+ for (RevCommit c; (c = rw.next()) != null; ) {
+ rw.parseBody(c);
+
+ for (Ref ref : byCommit.get(c.copy())) {
+ PatchSet.Id psId = PatchSet.Id.fromRef(ref.getName());
+ Optional<ChangeNotes> notes = getChangeNotes(psId.changeId());
+ if (notes.isPresent() && notes.get().getChange().getDest().equals(branch)) {
+ existingPatchSets++;
+ bu.addOp(notes.get().getChangeId(), setPrivateOpFactory.create(false, null));
+ bu.addOp(
+ psId.changeId(),
+ mergedByPushOpFactory.create(
+ requestScopePropagator, psId, refName, newTip.getId().getName()));
+ continue COMMIT;
+ }
}
- ChangeNotes onto = byKey.get(new Change.Key(changeId.trim()));
- if (onto != null) {
- newPatchSets++;
- // Hold onto this until we're done with the walk, as the call to
- // req.validate below calls isMergedInto which resets the walk.
- ReplaceRequest req = new ReplaceRequest(onto.getChangeId(), c, cmd, false);
- req.notes = onto;
- replaceAndClose.add(req);
- continue COMMIT;
+ for (String changeId : c.getFooterLines(FooterConstants.CHANGE_ID)) {
+ if (byKey == null) {
+ byKey = executeIndexQuery(() -> openChangesByKeyByBranch(branch));
+ }
+
+ ChangeNotes onto = byKey.get(Change.key(changeId.trim()));
+ if (onto != null) {
+ newPatchSets++;
+ // Hold onto this until we're done with the walk, as the call to
+ // req.validate below calls isMergedInto which resets the walk.
+ ReplaceRequest req = new ReplaceRequest(onto.getChangeId(), c, cmd, false);
+ req.notes = onto;
+ replaceAndClose.add(req);
+ continue COMMIT;
+ }
}
}
- }
- for (ReplaceRequest req : replaceAndClose) {
- Change.Id id = req.notes.getChangeId();
- if (!req.validateNewPatchSetForAutoClose()) {
- logger.atFine().log("Not closing %s because validation failed", id);
- continue;
+ for (ReplaceRequest req : replaceAndClose) {
+ Change.Id id = req.notes.getChangeId();
+ if (!req.validateNewPatchSetForAutoClose()) {
+ logger.atFine().log("Not closing %s because validation failed", id);
+ continue;
+ }
+ req.addOps(bu, null);
+ bu.addOp(id, setPrivateOpFactory.create(false, null));
+ bu.addOp(
+ id,
+ mergedByPushOpFactory
+ .create(
+ requestScopePropagator, req.psId, refName, newTip.getId().getName())
+ .setPatchSetProvider(req.replaceOp::getPatchSet));
+ bu.addOp(id, new ChangeProgressOp(progress));
+ ids.add(id);
}
- req.addOps(bu, null);
- bu.addOp(id, setPrivateOpFactory.create(false, null));
- bu.addOp(
- id,
- mergedByPushOpFactory
- .create(requestScopePropagator, req.psId, refName, newTip.getId().getName())
- .setPatchSetProvider(req.replaceOp::getPatchSet));
- bu.addOp(id, new ChangeProgressOp(progress));
- ids.add(id);
+
+ logger.atFine().log(
+ "Auto-closing %d changes with existing patch sets and %d with new patch sets",
+ existingPatchSets, newPatchSets);
+ bu.execute();
+ } catch (IOException | StorageException | PermissionBackendException e) {
+ logger.atSevere().withCause(e).log("Failed to auto-close changes");
+ return null;
}
- logger.atFine().log(
- "Auto-closing %s changes with existing patch sets and %s with new patch sets",
- existingPatchSets, newPatchSets);
- bu.execute();
- } catch (IOException | StorageException | PermissionBackendException e) {
- logger.atSevere().withCause(e).log("Failed to auto-close changes");
- return null;
- }
+ // If we are here, we didn't throw UpdateException. Record the result.
+ // The ordering is indeterminate due to the HashSet; unfortunately, Change.Id doesn't
+ // fit into TreeSet.
+ ids.stream().forEach(id -> resultChangeIds.add(ResultChangeIds.Key.AUTOCLOSED, id));
- // If we are here, we didn't throw UpdateException. Record the result.
- // The ordering is indeterminate due to the HashSet; unfortunately, Change.Id doesn't
- // fit into TreeSet.
- ids.stream().forEach(id -> resultChangeIds.add(ResultChangeIds.Key.AUTOCLOSED, id));
-
- return null;
- },
- // Use a multiple of the default timeout to account for inner retries that may otherwise
- // eat up the whole timeout so that no time is left to retry this outer action.
- RetryHelper.options()
- .timeout(retryHelper.getDefaultTimeout(ActionType.CHANGE_UPDATE).multipliedBy(5))
- .build());
- } catch (RestApiException e) {
- logger.atSevere().withCause(e).log("Can't insert patchset");
- } catch (UpdateException e) {
- logger.atSevere().withCause(e).log("Failed to auto-close changes");
+ return null;
+ },
+ // Use a multiple of the default timeout to account for inner retries that may otherwise
+ // eat up the whole timeout so that no time is left to retry this outer action.
+ RetryHelper.options()
+ .timeout(retryHelper.getDefaultTimeout(ActionType.CHANGE_UPDATE).multipliedBy(5))
+ .build());
+ } catch (RestApiException e) {
+ logger.atSevere().withCause(e).log("Can't insert patchset");
+ } catch (UpdateException e) {
+ logger.atSevere().withCause(e).log("Failed to auto-close changes");
+ }
}
}
@@ -3243,7 +3375,7 @@ class ReceiveCommits {
}
private <T> T executeIndexQuery(Action<T> action) {
- try {
+ try (TraceTimer traceTimer = newTimer("executeIndexQuery")) {
return retryHelper.execute(
ActionType.INDEX_QUERY, action, StorageException.class::isInstance);
} catch (Exception e) {
@@ -3252,16 +3384,19 @@ class ReceiveCommits {
}
}
- private Map<Change.Key, ChangeNotes> openChangesByKeyByBranch(Branch.NameKey branch) {
- Map<Change.Key, ChangeNotes> r = new HashMap<>();
- for (ChangeData cd : queryProvider.get().byBranchOpen(branch)) {
- try {
- r.put(cd.change().getKey(), cd.notes());
- } catch (NoSuchChangeException e) {
- // Ignore deleted change
+ private Map<Change.Key, ChangeNotes> openChangesByKeyByBranch(BranchNameKey branch) {
+ try (TraceTimer traceTimer =
+ newTimer("openChangesByKeyByBranch", Metadata.builder().branchName(branch.branch()))) {
+ Map<Change.Key, ChangeNotes> r = new HashMap<>();
+ for (ChangeData cd : queryProvider.get().byBranchOpen(branch)) {
+ try {
+ r.put(cd.change().getKey(), cd.notes());
+ } catch (NoSuchChangeException e) {
+ // Ignore deleted change
+ }
}
+ return r;
}
- return r;
}
// allRefsWatcher hooks into the protocol negotation to get a list of all known refs.
@@ -3271,7 +3406,25 @@ class ReceiveCommits {
return allRefsWatcher.getAllRefs();
}
+ private TraceTimer newTimer(String name) {
+ return newTimer(getClass(), name);
+ }
+
+ private TraceTimer newTimer(Class<?> clazz, String name) {
+ return newTimer(clazz, name, Metadata.builder());
+ }
+
+ private TraceTimer newTimer(String name, Metadata.Builder metadataBuilder) {
+ return newTimer(getClass(), name, metadataBuilder);
+ }
+
+ private TraceTimer newTimer(Class<?> clazz, String name, Metadata.Builder metadataBuilder) {
+ metadataBuilder.projectName(project.getName());
+ return TraceContext.newTimer(clazz.getSimpleName() + "#" + name, metadataBuilder.build());
+ }
+
private static void reject(ReceiveCommand cmd, String why) {
+ logger.atFine().log("Rejecting command '%s': %s", cmd, why);
cmd.setResult(REJECTED_OTHER_REASON, why);
}
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
index cfe06ea7bd..83bf55441d 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
@@ -14,43 +14,55 @@
package com.google.gerrit.server.git.receive;
-import com.google.auto.value.AutoValue;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Maps;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.HookUtil;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.util.MagicBranch;
import com.google.inject.Provider;
+import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.AdvertiseRefsHook;
-import org.eclipse.jgit.transport.BaseReceivePack;
+import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.UploadPack;
-/** Exposes only the non refs/changes/ reference names. */
+/**
+ * Exposes only the non refs/changes/ reference names and provide additional haves.
+ *
+ * <p>Negotiation on Git push is suboptimal in that it tends to send more objects to the server than
+ * it should. This results in increased latencies for {@code git push}.
+ *
+ * <p>Ref advertisement for Git pushes still works in a "the server speaks first fashion" as Git
+ * Protocol V2 only addressed fetches Therefore the server needs to send all available references.
+ * For large repositories, this can be in the tens of megabytes to send to the client. We therefore
+ * remove all refs in refs/changes/* to scale down that footprint. Trivially, this would increase
+ * the unnecessary objects that the client has to send to the server because the common ancestor it
+ * finds in negotiation might be further back in history.
+ *
+ * <p>To work around this, we advertise the last 32 changes in that repository as additional {@code
+ * .haves}. This is a heuristical approach that aims at scaling down the number of unnecessary
+ * objects that client sends to the server. Unnecessary here refers to objects that the server
+ * already has.
+ *
+ * <p>TODO(hiesel): Instrument this heuristic and proof its value.
+ */
public class ReceiveCommitsAdvertiseRefsHook implements AdvertiseRefsHook {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- @VisibleForTesting
- @AutoValue
- public abstract static class Result {
- public abstract Map<String, Ref> allRefs();
-
- public abstract Set<ObjectId> additionalHaves();
- }
-
private final Provider<InternalChangeQuery> queryProvider;
private final Project.NameKey projectName;
@@ -66,31 +78,18 @@ public class ReceiveCommitsAdvertiseRefsHook implements AdvertiseRefsHook {
"ReceiveCommitsAdvertiseRefsHook cannot be used for UploadPack");
}
- @SuppressWarnings("deprecation")
@Override
- public void advertiseRefs(BaseReceivePack rp) throws ServiceMayNotContinueException {
- Result r = advertiseRefs(HookUtil.ensureAllRefsAdvertised(rp));
- rp.setAdvertisedRefs(r.allRefs(), r.additionalHaves());
- }
-
- @VisibleForTesting
- public Result advertiseRefs(Map<String, Ref> oldRefs) {
- Map<String, Ref> r = Maps.newHashMapWithExpectedSize(oldRefs.size());
- Set<ObjectId> allPatchSets = Sets.newHashSetWithExpectedSize(oldRefs.size());
- for (Map.Entry<String, Ref> e : oldRefs.entrySet()) {
- String name = e.getKey();
- if (!skip(name)) {
- r.put(name, e.getValue());
- }
- if (name.startsWith(RefNames.REFS_CHANGES)) {
- allPatchSets.add(e.getValue().getObjectId());
- }
- }
- return new AutoValue_ReceiveCommitsAdvertiseRefsHook_Result(
- r, advertiseOpenChanges(allPatchSets));
+ public void advertiseRefs(ReceivePack rp) throws ServiceMayNotContinueException {
+ Map<String, Ref> advertisedRefs = HookUtil.ensureAllRefsAdvertised(rp);
+ advertisedRefs.keySet().stream()
+ .filter(ReceiveCommitsAdvertiseRefsHook::skip)
+ .collect(toImmutableList())
+ .forEach(r -> advertisedRefs.remove(r));
+ rp.setAdvertisedRefs(advertisedRefs, advertiseOpenChanges(rp.getRepository()));
}
- private Set<ObjectId> advertiseOpenChanges(Set<ObjectId> allPatchSets) {
+ private Set<ObjectId> advertiseOpenChanges(Repository repo)
+ throws ServiceMayNotContinueException {
// Advertise some recent open changes, in case a commit is based on one.
int limit = 32;
try {
@@ -109,15 +108,20 @@ public class ReceiveCommitsAdvertiseRefsHook implements AdvertiseRefsHook {
.byProjectOpen(projectName)) {
PatchSet ps = cd.currentPatchSet();
if (ps != null) {
- ObjectId id = ObjectId.fromString(ps.getRevision().get());
// Ensure we actually observed a patch set ref pointing to this
// object, in case the database is out of sync with the repo and the
// object doesn't actually exist.
- if (allPatchSets.contains(id)) {
- r.add(id);
+ try {
+ Ref psRef = repo.getRefDatabase().exactRef(RefNames.patchSetRef(ps.id()));
+ if (psRef != null) {
+ r.add(ps.commitId());
+ }
+ } catch (IOException e) {
+ throw new ServiceMayNotContinueException(e);
}
}
}
+
return r;
} catch (StorageException err) {
logger.atSevere().withCause(err).log("Cannot list open changes of %s", projectName);
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHookChain.java b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHookChain.java
new file mode 100644
index 0000000000..d5744663eb
--- /dev/null
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHookChain.java
@@ -0,0 +1,91 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git.receive;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.AllUsersNameProvider;
+import com.google.gerrit.server.git.UsersSelfAdvertiseRefsHook;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.inject.Provider;
+import com.google.inject.util.Providers;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jgit.transport.AdvertiseRefsHook;
+import org.eclipse.jgit.transport.AdvertiseRefsHookChain;
+
+/**
+ * Helper to ensure that the chain for advertising refs is the same in tests and production code.
+ */
+public class ReceiveCommitsAdvertiseRefsHookChain {
+
+ /**
+ * Returns a single {@link AdvertiseRefsHook} that encompasses a chain of {@link
+ * AdvertiseRefsHook} to be used for advertising when processing a Git push.
+ */
+ public static AdvertiseRefsHook create(
+ AllRefsWatcher allRefsWatcher,
+ UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook,
+ AllUsersName allUsersName,
+ Provider<InternalChangeQuery> queryProvider,
+ Project.NameKey projectName) {
+ return create(
+ allRefsWatcher,
+ usersSelfAdvertiseRefsHook,
+ allUsersName,
+ queryProvider,
+ projectName,
+ false);
+ }
+
+ /**
+ * Returns a single {@link AdvertiseRefsHook} that encompasses a chain of {@link
+ * AdvertiseRefsHook} to be used for advertising when processing a Git push. Omits {@link
+ * HackPushNegotiateHook} as that does not advertise refs on it's own but adds {@code .have} based
+ * on history which is not relevant for the tests we have.
+ */
+ @VisibleForTesting
+ public static AdvertiseRefsHook createForTest(
+ Provider<InternalChangeQuery> queryProvider, Project.NameKey projectName, CurrentUser user) {
+ return create(
+ new AllRefsWatcher(),
+ new UsersSelfAdvertiseRefsHook(Providers.of(user)),
+ new AllUsersName(AllUsersNameProvider.DEFAULT),
+ queryProvider,
+ projectName,
+ true);
+ }
+
+ private static AdvertiseRefsHook create(
+ AllRefsWatcher allRefsWatcher,
+ UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook,
+ AllUsersName allUsersName,
+ Provider<InternalChangeQuery> queryProvider,
+ Project.NameKey projectName,
+ boolean skipHackPushNegotiateHook) {
+ List<AdvertiseRefsHook> advHooks = new ArrayList<>();
+ advHooks.add(allRefsWatcher);
+ advHooks.add(new ReceiveCommitsAdvertiseRefsHook(queryProvider, projectName));
+ if (!skipHackPushNegotiateHook) {
+ advHooks.add(new HackPushNegotiateHook());
+ }
+ if (projectName.equals(allUsersName)) {
+ advHooks.add(usersSelfAdvertiseRefsHook);
+ }
+ return AdvertiseRefsHookChain.newChain(advHooks);
+ }
+}
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveConfig.java b/java/com/google/gerrit/server/git/receive/ReceiveConfig.java
index b1c9f133a3..cdbf3108c8 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveConfig.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveConfig.java
@@ -27,7 +27,6 @@ import org.eclipse.jgit.lib.Config;
class ReceiveConfig {
final boolean checkMagicRefs;
final boolean checkReferencedObjectsAreReachable;
- final boolean allowDrafts;
final int maxBatchCommits;
final boolean disablePrivateChanges;
private final int systemMaxBatchChanges;
@@ -38,7 +37,6 @@ class ReceiveConfig {
checkMagicRefs = config.getBoolean("receive", null, "checkMagicRefs", true);
checkReferencedObjectsAreReachable =
config.getBoolean("receive", null, "checkReferencedObjectsAreReachable", true);
- allowDrafts = config.getBoolean("change", null, "allowDrafts", false);
maxBatchCommits = config.getInt("receive", null, "maxBatchCommits", 10000);
systemMaxBatchChanges = config.getInt("receive", "maxBatchChanges", 0);
disablePrivateChanges = config.getBoolean("change", null, "disablePrivateChanges", false);
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveRefFilter.java b/java/com/google/gerrit/server/git/receive/ReceiveRefFilter.java
index 16cba53c82..24a89f7d87 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveRefFilter.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveRefFilter.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.git.receive;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CACHE_AUTOMERGE;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
+import static com.google.gerrit.entities.RefNames.REFS_CACHE_AUTOMERGE;
+import static com.google.gerrit.entities.RefNames.REFS_CHANGES;
import com.google.common.collect.Maps;
import java.util.Map;
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index 4d0d2c3559..e95cf3b198 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -29,19 +29,19 @@ import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.PatchSetInfo;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
@@ -99,7 +99,7 @@ public class ReplaceOp implements BatchUpdateOp {
public interface Factory {
ReplaceOp create(
ProjectState projectState,
- Branch.NameKey dest,
+ BranchNameKey dest,
boolean checkMergedInto,
@Nullable String mergeResultRevId,
@Assisted("priorPatchSetId") PatchSet.Id priorPatchSetId,
@@ -132,7 +132,7 @@ public class ReplaceOp implements BatchUpdateOp {
private final ReviewerAdder reviewerAdder;
private final ProjectState projectState;
- private final Branch.NameKey dest;
+ private final BranchNameKey dest;
private final boolean checkMergedInto;
private final String mergeResultRevId;
private final PatchSet.Id priorPatchSetId;
@@ -177,7 +177,7 @@ public class ReplaceOp implements BatchUpdateOp {
@SendEmailExecutor ExecutorService sendEmailExecutor,
ReviewerAdder reviewerAdder,
@Assisted ProjectState projectState,
- @Assisted Branch.NameKey dest,
+ @Assisted BranchNameKey dest,
@Assisted boolean checkMergedInto,
@Assisted @Nullable String mergeResultRevId,
@Assisted("priorPatchSetId") PatchSet.Id priorPatchSetId,
@@ -232,7 +232,7 @@ public class ReplaceOp implements BatchUpdateOp {
commitId);
if (checkMergedInto) {
- String mergedInto = findMergedInto(ctx, dest.get(), commit);
+ String mergedInto = findMergedInto(ctx, dest.branch(), commit);
if (mergedInto != null) {
mergedByPushOp =
mergedByPushOpFactory.create(
@@ -255,7 +255,7 @@ public class ReplaceOp implements BatchUpdateOp {
}
if (groups.isEmpty()) {
PatchSet prevPs = psUtil.current(notes);
- groups = prevPs != null ? prevPs.getGroups() : ImmutableList.of();
+ groups = prevPs != null ? prevPs.groups() : ImmutableList.of();
}
ChangeData cd = changeDataFactory.create(ctx.getNotes());
@@ -295,7 +295,7 @@ public class ReplaceOp implements BatchUpdateOp {
}
if (shouldPublishComments()) {
boolean workInProgress = change.isWorkInProgress();
- if (magicBranch != null && magicBranch.workInProgress) {
+ if (magicBranch.workInProgress) {
workInProgress = true;
}
comments = publishComments(ctx, workInProgress);
@@ -459,7 +459,7 @@ public class ReplaceOp implements BatchUpdateOp {
continue;
}
- LabelType lt = projectState.getLabelTypes().byLabel(a.getLabelId());
+ LabelType lt = projectState.getLabelTypes().byLabel(a.labelId());
if (lt != null) {
current.put(lt.getName(), a);
}
@@ -481,11 +481,7 @@ public class ReplaceOp implements BatchUpdateOp {
change.setCurrentPatchSet(info);
List<String> idList = commit.getFooterLines(CHANGE_ID);
- if (idList.isEmpty()) {
- change.setKey(new Change.Key("I" + commitId.name()));
- } else {
- change.setKey(new Change.Key(idList.get(idList.size() - 1).trim()));
- }
+ change.setKey(Change.key(idList.get(idList.size() - 1).trim()));
}
private List<Comment> publishComments(ChangeContext ctx, boolean workInProgress) {
@@ -548,7 +544,7 @@ public class ReplaceOp implements BatchUpdateOp {
try {
ReplacePatchSetSender cm =
replacePatchSetFactory.create(projectState.getNameKey(), notes.getChangeId());
- cm.setFrom(ctx.getAccount().getAccount().getId());
+ cm.setFrom(ctx.getAccount().account().id());
cm.setPatchSet(newPatchSet, info);
cm.setChangeMessage(msg.getMessage(), ctx.getWhen());
cm.setNotify(ctx.getNotify(notes.getChangeId()));
@@ -556,7 +552,7 @@ public class ReplaceOp implements BatchUpdateOp {
Streams.concat(
oldRecipients.getReviewers().stream(),
reviewerAdditions.flattenResults(AddReviewersOp.Result::addedReviewers).stream()
- .map(PatchSetApproval::getAccountId))
+ .map(PatchSetApproval::accountId))
.collect(toImmutableSet()));
cm.addExtraCC(
Streams.concat(
@@ -567,7 +563,7 @@ public class ReplaceOp implements BatchUpdateOp {
cm.send();
} catch (Exception e) {
logger.atSevere().withCause(e).log(
- "Cannot send email for new patch set %s", newPatchSet.getId());
+ "Cannot send email for new patch set %s", newPatchSet.id());
}
}
diff --git a/java/com/google/gerrit/server/git/receive/ResultChangeIds.java b/java/com/google/gerrit/server/git/receive/ResultChangeIds.java
index e326141a1f..805822c5e3 100644
--- a/java/com/google/gerrit/server/git/receive/ResultChangeIds.java
+++ b/java/com/google/gerrit/server/git/receive/ResultChangeIds.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.git.receive;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
diff --git a/java/com/google/gerrit/server/git/receive/testing/BUILD b/java/com/google/gerrit/server/git/receive/testing/BUILD
new file mode 100644
index 0000000000..06407aefea
--- /dev/null
+++ b/java/com/google/gerrit/server/git/receive/testing/BUILD
@@ -0,0 +1,14 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "testing",
+ testonly = 1,
+ srcs = glob(["**/*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//lib:guava",
+ "//lib:jgit",
+ "//lib/auto:auto-value",
+ "//lib/auto:auto-value-annotations",
+ ],
+)
diff --git a/java/com/google/gerrit/server/git/receive/testing/TestRefAdvertiser.java b/java/com/google/gerrit/server/git/receive/testing/TestRefAdvertiser.java
new file mode 100644
index 0000000000..c54ab25f94
--- /dev/null
+++ b/java/com/google/gerrit/server/git/receive/testing/TestRefAdvertiser.java
@@ -0,0 +1,87 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git.receive.testing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.StreamSupport;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RefAdvertiser;
+
+/** Helper to collect advertised refs and additonal haves and verify them in tests. */
+public class TestRefAdvertiser extends RefAdvertiser {
+
+ @VisibleForTesting
+ @AutoValue
+ public abstract static class Result {
+ public abstract Map<String, Ref> allRefs();
+
+ public abstract Set<ObjectId> additionalHaves();
+
+ public static Result create(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves) {
+ return new AutoValue_TestRefAdvertiser_Result(allRefs, additionalHaves);
+ }
+ }
+
+ private final Map<String, Ref> advertisedRefs;
+ private final Set<ObjectId> additionalHaves;
+ private final Repository repo;
+
+ public TestRefAdvertiser(Repository repo) {
+ advertisedRefs = new HashMap<>();
+ additionalHaves = new HashSet<>();
+ this.repo = repo;
+ }
+
+ @Override
+ protected void writeOne(CharSequence line) throws IOException {
+ List<String> lineParts =
+ StreamSupport.stream(Splitter.on(' ').split(line).spliterator(), false)
+ .map(String::trim)
+ .collect(toImmutableList());
+ if (".have".equals(lineParts.get(1))) {
+ additionalHaves.add(ObjectId.fromString(lineParts.get(0)));
+ } else {
+ ObjectId id = ObjectId.fromString(lineParts.get(0));
+ Ref ref =
+ repo.getRefDatabase().getRefs().stream()
+ .filter(r -> r.getObjectId().equals(id))
+ .findAny()
+ .orElseThrow(
+ () ->
+ new RuntimeException(
+ line.toString() + " does not conform to expected pattern"));
+ advertisedRefs.put(lineParts.get(1), ref);
+ }
+ }
+
+ @Override
+ protected void end() {}
+
+ public Result result() {
+ return Result.create(advertisedRefs, additionalHaves);
+ }
+}
diff --git a/java/com/google/gerrit/server/git/validators/AccountValidator.java b/java/com/google/gerrit/server/git/validators/AccountValidator.java
index 08870d36dc..c6af49cab6 100644
--- a/java/com/google/gerrit/server/git/validators/AccountValidator.java
+++ b/java/com/google/gerrit/server/git/validators/AccountValidator.java
@@ -18,7 +18,7 @@ import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountConfig;
import com.google.gerrit.server.account.AccountProperties;
@@ -87,10 +87,10 @@ public class AccountValidator {
messages.add("cannot deactivate own account");
}
- String newPreferredEmail = newAccount.get().getPreferredEmail();
+ String newPreferredEmail = newAccount.get().preferredEmail();
if (newPreferredEmail != null
&& (!oldAccount.isPresent()
- || !newPreferredEmail.equals(oldAccount.get().getPreferredEmail()))) {
+ || !newPreferredEmail.equals(oldAccount.get().preferredEmail()))) {
if (!emailValidator.isValid(newPreferredEmail)) {
messages.add(
String.format(
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index ff21b2dddc..6773d29fc7 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -15,9 +15,9 @@
package com.google.gerrit.server.git.validators;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.client.Change.CHANGE_ID_PATTERN;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CONFIG;
+import static com.google.gerrit.entities.Change.CHANGE_ID_PATTERN;
+import static com.google.gerrit.entities.RefNames.REFS_CHANGES;
+import static com.google.gerrit.entities.RefNames.REFS_CONFIG;
import static java.util.stream.Collectors.toList;
import com.google.common.annotations.VisibleForTesting;
@@ -25,14 +25,14 @@ import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.externalids.ExternalIdsConsistencyChecker;
@@ -127,7 +127,7 @@ public class CommitValidators {
public CommitValidators forReceiveCommits(
PermissionBackend.ForProject forProject,
- Branch.NameKey branch,
+ BranchNameKey branch,
IdentifiedUser user,
SshInfo sshInfo,
NoteMap rejectCommits,
@@ -135,8 +135,8 @@ public class CommitValidators {
@Nullable Change change,
boolean skipValidation)
throws IOException {
- PermissionBackend.ForRef perm = forProject.ref(branch.get());
- ProjectState projectState = projectCache.checkedGet(branch.getParentKey());
+ PermissionBackend.ForRef perm = forProject.ref(branch.branch());
+ ProjectState projectState = projectCache.checkedGet(branch.project());
ImmutableList.Builder<CommitValidationListener> validators = ImmutableList.builder();
validators
.add(new UploadMergesPermissionValidator(perm))
@@ -164,21 +164,21 @@ public class CommitValidators {
public CommitValidators forGerritCommits(
PermissionBackend.ForProject forProject,
- Branch.NameKey branch,
+ BranchNameKey branch,
IdentifiedUser user,
SshInfo sshInfo,
RevWalk rw,
@Nullable Change change)
throws IOException {
- PermissionBackend.ForRef perm = forProject.ref(branch.get());
- ProjectState projectState = projectCache.checkedGet(branch.getParentKey());
+ PermissionBackend.ForRef perm = forProject.ref(branch.branch());
+ ProjectState projectState = projectCache.checkedGet(branch.project());
ImmutableList.Builder<CommitValidationListener> validators = ImmutableList.builder();
validators
.add(new UploadMergesPermissionValidator(perm))
.add(new ProjectStateValidationListener(projectState))
.add(new AmendedGerritMergeCommitValidationListener(perm, gerritIdent))
.add(new AuthorUploaderValidator(user, perm, urlFormatter.get()))
- .add(new SignedOffByValidator(user, perm, projectCache.checkedGet(branch.getParentKey())))
+ .add(new SignedOffByValidator(user, perm, projectCache.checkedGet(branch.project())))
.add(
new ChangeIdValidator(
projectState,
@@ -196,7 +196,7 @@ public class CommitValidators {
}
public CommitValidators forMergedCommits(
- PermissionBackend.ForProject forProject, Branch.NameKey branch, IdentifiedUser user)
+ PermissionBackend.ForProject forProject, BranchNameKey branch, IdentifiedUser user)
throws IOException {
// Generally only include validators that are based on permissions of the
// user creating a change for a merged commit; generally exclude
@@ -211,11 +211,11 @@ public class CommitValidators {
// discuss what to do about it.
// - Plugin validators may do things like require certain commit message
// formats, so we play it safe and exclude them.
- PermissionBackend.ForRef perm = forProject.ref(branch.get());
+ PermissionBackend.ForRef perm = forProject.ref(branch.branch());
ImmutableList.Builder<CommitValidationListener> validators = ImmutableList.builder();
validators
.add(new UploadMergesPermissionValidator(perm))
- .add(new ProjectStateValidationListener(projectCache.checkedGet(branch.getParentKey())))
+ .add(new ProjectStateValidationListener(projectCache.checkedGet(branch.project())))
.add(new AuthorUploaderValidator(user, perm, urlFormatter.get()))
.add(new CommitterUploaderValidator(user, perm, urlFormatter.get()));
return new CommitValidators(validators.build());
@@ -395,7 +395,7 @@ public class CommitValidators {
/** If this is the special project configuration branch, validate the config. */
public static class ConfigValidator implements CommitValidationListener {
private final ProjectConfig.Factory projectConfigFactory;
- private final Branch.NameKey branch;
+ private final BranchNameKey branch;
private final IdentifiedUser user;
private final RevWalk rw;
private final AllUsersName allUsers;
@@ -403,7 +403,7 @@ public class CommitValidators {
public ConfigValidator(
ProjectConfig.Factory projectConfigFactory,
- Branch.NameKey branch,
+ BranchNameKey branch,
IdentifiedUser user,
RevWalk rw,
AllUsersName allUsers,
@@ -419,7 +419,7 @@ public class CommitValidators {
@Override
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
throws CommitValidationException {
- if (REFS_CONFIG.equals(branch.get())) {
+ if (REFS_CONFIG.equals(branch.branch())) {
List<CommitValidationMessage> messages = new ArrayList<>();
try {
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidationListener.java b/java/com/google/gerrit/server/git/validators/MergeValidationListener.java
index 6edd04eeff..b47d7d656b 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidationListener.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidationListener.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.git.validators;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.project.ProjectState;
@@ -44,7 +44,7 @@ public interface MergeValidationListener {
Repository repo,
CodeReviewCommit commit,
ProjectState destProject,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
PatchSet.Id patchSetId,
IdentifiedUser caller)
throws MergeValidationException;
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidators.java b/java/com/google/gerrit/server/git/validators/MergeValidators.java
index 75be8f35bf..e17e1290a9 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidators.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidators.java
@@ -17,16 +17,16 @@ package com.google.gerrit.server.git.validators;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountProperties;
import com.google.gerrit.server.config.AllProjectsName;
@@ -80,7 +80,7 @@ public class MergeValidators {
Repository repo,
CodeReviewCommit commit,
ProjectState destProject,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
PatchSet.Id patchSetId,
IdentifiedUser caller)
throws MergeValidationException {
@@ -156,11 +156,11 @@ public class MergeValidators {
final Repository repo,
final CodeReviewCommit commit,
final ProjectState destProject,
- final Branch.NameKey destBranch,
+ final BranchNameKey destBranch,
final PatchSet.Id patchSetId,
IdentifiedUser caller)
throws MergeValidationException {
- if (RefNames.REFS_CONFIG.equals(destBranch.get())) {
+ if (RefNames.REFS_CONFIG.equals(destBranch.branch())) {
final Project.NameKey newParent;
try {
ProjectConfig cfg = projectConfigFactory.create(destProject.getNameKey());
@@ -251,7 +251,7 @@ public class MergeValidators {
Repository repo,
CodeReviewCommit commit,
ProjectState destProject,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
PatchSet.Id patchSetId,
IdentifiedUser caller)
throws MergeValidationException {
@@ -285,18 +285,17 @@ public class MergeValidators {
Repository repo,
CodeReviewCommit commit,
ProjectState destProject,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
PatchSet.Id patchSetId,
IdentifiedUser caller)
throws MergeValidationException {
- Account.Id accountId = Account.Id.fromRef(destBranch.get());
+ Account.Id accountId = Account.Id.fromRef(destBranch.branch());
if (!allUsersName.equals(destProject.getNameKey()) || accountId == null) {
return;
}
ChangeData cd =
- changeDataFactory.create(
- destProject.getProject().getNameKey(), patchSetId.getParentKey());
+ changeDataFactory.create(destProject.getProject().getNameKey(), patchSetId.changeId());
try {
if (!cd.currentFilePaths().contains(AccountProperties.ACCOUNT_CONFIG)) {
return;
@@ -336,13 +335,13 @@ public class MergeValidators {
Repository repo,
CodeReviewCommit commit,
ProjectState destProject,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
PatchSet.Id patchSetId,
IdentifiedUser caller)
throws MergeValidationException {
// Groups are stored inside the 'All-Users' repository.
if (!allUsersName.equals(destProject.getNameKey())
- || !RefNames.isGroupRef(destBranch.get())) {
+ || !RefNames.isGroupRef(destBranch.branch())) {
return;
}
diff --git a/java/com/google/gerrit/server/git/validators/OnSubmitValidationListener.java b/java/com/google/gerrit/server/git/validators/OnSubmitValidationListener.java
index 308fdc01af..432dda32ca 100644
--- a/java/com/google/gerrit/server/git/validators/OnSubmitValidationListener.java
+++ b/java/com/google/gerrit/server/git/validators/OnSubmitValidationListener.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.git.validators;
import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.RefCache;
import com.google.gerrit.server.update.ChainedReceiveCommands;
import com.google.gerrit.server.validators.ValidationException;
diff --git a/java/com/google/gerrit/server/git/validators/OnSubmitValidators.java b/java/com/google/gerrit/server/git/validators/OnSubmitValidators.java
index 409240e257..6faa7afb36 100644
--- a/java/com/google/gerrit/server/git/validators/OnSubmitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/OnSubmitValidators.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.git.validators;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.validators.OnSubmitValidationListener.Arguments;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.submit.IntegrationException;
diff --git a/java/com/google/gerrit/server/git/validators/RefOperationValidationException.java b/java/com/google/gerrit/server/git/validators/RefOperationValidationException.java
index 9eaf2d2629..d27cc38f15 100644
--- a/java/com/google/gerrit/server/git/validators/RefOperationValidationException.java
+++ b/java/com/google/gerrit/server/git/validators/RefOperationValidationException.java
@@ -14,27 +14,28 @@
package com.google.gerrit.server.git.validators;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.server.validators.ValidationException;
public class RefOperationValidationException extends ValidationException {
private static final long serialVersionUID = 1L;
- private final Iterable<ValidationMessage> messages;
+ private final ImmutableList<ValidationMessage> messages;
- public RefOperationValidationException(String reason, Iterable<ValidationMessage> messages) {
+ public RefOperationValidationException(String reason, ImmutableList<ValidationMessage> messages) {
super(reason);
this.messages = messages;
}
- public Iterable<ValidationMessage> getMessages() {
+ public ImmutableList<ValidationMessage> getMessages() {
return messages;
}
@Override
public String getMessage() {
- StringBuilder msg = new StringBuilder(super.getMessage());
- for (ValidationMessage error : messages) {
- msg.append("\n").append(error.getMessage());
- }
- return msg.toString();
+ return messages.stream()
+ .map(ValidationMessage::getMessage)
+ .collect(joining("\n", super.getMessage() + "\n", ""));
}
}
diff --git a/java/com/google/gerrit/server/git/validators/RefOperationValidators.java b/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
index 3c0208c787..e9734a3444 100644
--- a/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
+++ b/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
@@ -13,14 +13,14 @@
// limitations under the License.
package com.google.gerrit.server.git.validators;
-import com.google.common.base.Predicate;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.events.RefReceivedEvent;
@@ -39,8 +39,6 @@ import org.eclipse.jgit.transport.ReceiveCommand;
public class RefOperationValidators {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private static final GetErrorMessages GET_ERRORS = new GetErrorMessages();
-
public interface Factory {
RefOperationValidators create(Project project, IdentifiedUser user, ReceiveCommand cmd);
}
@@ -93,22 +91,15 @@ public class RefOperationValidators {
return messages;
}
- private void throwException(Iterable<ValidationMessage> messages, RefReceivedEvent event)
+ private void throwException(List<ValidationMessage> messages, RefReceivedEvent event)
throws RefOperationValidationException {
- Iterable<ValidationMessage> errors = Iterables.filter(messages, GET_ERRORS);
String header =
String.format(
"Ref \"%s\" %S in project %s validation failed",
event.command.getRefName(), event.command.getType(), event.project.getName());
logger.atSevere().log(header);
- throw new RefOperationValidationException(header, errors);
- }
-
- private static class GetErrorMessages implements Predicate<ValidationMessage> {
- @Override
- public boolean apply(ValidationMessage input) {
- return input.isError();
- }
+ throw new RefOperationValidationException(
+ header, messages.stream().filter(ValidationMessage::isError).collect(toImmutableList()));
}
private static class DisallowCreationAndDeletionOfGerritMaintainedBranches
diff --git a/java/com/google/gerrit/server/git/validators/UploadValidationListener.java b/java/com/google/gerrit/server/git/validators/UploadValidationListener.java
index 13065e4be6..8f1d5e4a25 100644
--- a/java/com/google/gerrit/server/git/validators/UploadValidationListener.java
+++ b/java/com/google/gerrit/server/git/validators/UploadValidationListener.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.git.validators;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.validators.ValidationException;
import java.util.Collection;
import org.eclipse.jgit.lib.ObjectId;
diff --git a/java/com/google/gerrit/server/git/validators/UploadValidators.java b/java/com/google/gerrit/server/git/validators/UploadValidators.java
index 25952837d7..6847a28f26 100644
--- a/java/com/google/gerrit/server/git/validators/UploadValidators.java
+++ b/java/com/google/gerrit/server/git/validators/UploadValidators.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.git.validators;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/group/GroupAuditService.java b/java/com/google/gerrit/server/group/GroupAuditService.java
index 4c02adaf5f..30e5d3c6b0 100644
--- a/java/com/google/gerrit/server/group/GroupAuditService.java
+++ b/java/com/google/gerrit/server/group/GroupAuditService.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.group;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.AuditEvent;
import java.sql.Timestamp;
diff --git a/java/com/google/gerrit/server/group/GroupResolver.java b/java/com/google/gerrit/server/group/GroupResolver.java
index 5fe3e8ea36..a54f465e87 100644
--- a/java/com/google/gerrit/server/group/GroupResolver.java
+++ b/java/com/google/gerrit/server/group/GroupResolver.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.group;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.account.GroupCache;
@@ -81,7 +81,7 @@ public class GroupResolver {
* @return the group, null if no group is found for the given group ID
*/
public GroupDescription.Basic parseId(String id) {
- AccountGroup.UUID uuid = new AccountGroup.UUID(id);
+ AccountGroup.UUID uuid = AccountGroup.uuid(id);
if (groupBackend.handles(uuid)) {
GroupDescription.Basic d = groupBackend.get(uuid);
if (d != null) {
diff --git a/java/com/google/gerrit/server/group/InternalGroup.java b/java/com/google/gerrit/server/group/InternalGroup.java
index 7828586271..639cd7ab6f 100644
--- a/java/com/google/gerrit/server/group/InternalGroup.java
+++ b/java/com/google/gerrit/server/group/InternalGroup.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.group;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.io.Serializable;
import java.sql.Timestamp;
import org.eclipse.jgit.lib.ObjectId;
diff --git a/java/com/google/gerrit/server/group/InternalGroupDescription.java b/java/com/google/gerrit/server/group/InternalGroupDescription.java
index 1d2252d05d..c70c8bf9a4 100644
--- a/java/com/google/gerrit/server/group/InternalGroupDescription.java
+++ b/java/com/google/gerrit/server/group/InternalGroupDescription.java
@@ -20,8 +20,8 @@ import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.sql.Timestamp;
public class InternalGroupDescription implements GroupDescription.Internal {
diff --git a/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java b/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
index 2a9538d07a..b2d9632122 100644
--- a/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
+++ b/java/com/google/gerrit/server/group/PeriodicGroupIndexer.java
@@ -20,9 +20,9 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.ScheduleConfig;
diff --git a/java/com/google/gerrit/server/group/SubgroupResource.java b/java/com/google/gerrit/server/group/SubgroupResource.java
index a33e96b2f7..ceea2dc81d 100644
--- a/java/com/google/gerrit/server/group/SubgroupResource.java
+++ b/java/com/google/gerrit/server/group/SubgroupResource.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.group;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.inject.TypeLiteral;
public class SubgroupResource extends GroupResource {
diff --git a/java/com/google/gerrit/server/group/SystemGroupBackend.java b/java/com/google/gerrit/server/group/SystemGroupBackend.java
index 85c1e7316b..7821a01d67 100644
--- a/java/com/google/gerrit/server/group/SystemGroupBackend.java
+++ b/java/com/google/gerrit/server/group/SystemGroupBackend.java
@@ -23,7 +23,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.StartupCheck;
import com.google.gerrit.server.StartupException;
@@ -56,19 +56,19 @@ public class SystemGroupBackend extends AbstractGroupBackend {
/** Common UUID assigned to the "Anonymous Users" group. */
public static final AccountGroup.UUID ANONYMOUS_USERS =
- new AccountGroup.UUID(SYSTEM_GROUP_SCHEME + "Anonymous-Users");
+ AccountGroup.uuid(SYSTEM_GROUP_SCHEME + "Anonymous-Users");
/** Common UUID assigned to the "Registered Users" group. */
public static final AccountGroup.UUID REGISTERED_USERS =
- new AccountGroup.UUID(SYSTEM_GROUP_SCHEME + "Registered-Users");
+ AccountGroup.uuid(SYSTEM_GROUP_SCHEME + "Registered-Users");
/** Common UUID assigned to the "Project Owners" placeholder group. */
public static final AccountGroup.UUID PROJECT_OWNERS =
- new AccountGroup.UUID(SYSTEM_GROUP_SCHEME + "Project-Owners");
+ AccountGroup.uuid(SYSTEM_GROUP_SCHEME + "Project-Owners");
/** Common UUID assigned to the "Change Owner" placeholder group. */
public static final AccountGroup.UUID CHANGE_OWNER =
- new AccountGroup.UUID(SYSTEM_GROUP_SCHEME + "Change-Owner");
+ AccountGroup.uuid(SYSTEM_GROUP_SCHEME + "Change-Owner");
private static final AccountGroup.UUID[] all = {
ANONYMOUS_USERS, REGISTERED_USERS, PROJECT_OWNERS, CHANGE_OWNER,
diff --git a/java/com/google/gerrit/server/group/db/AuditLogFormatter.java b/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
index 508f5ef333..ec4c0fcbe3 100644
--- a/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
+++ b/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
@@ -20,8 +20,8 @@ import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.GroupBackend;
@@ -51,7 +51,7 @@ public class AuditLogFormatter {
}
private static Optional<Account> getAccount(AccountCache accountCache, Account.Id accountId) {
- return accountCache.get(accountId).map(AccountState::getAccount);
+ return accountCache.get(accountId).map(AccountState::account);
}
private static Optional<GroupDescription.Basic> getGroup(
@@ -72,7 +72,7 @@ public class AuditLogFormatter {
}
private static Optional<Account> getAccount(ImmutableSet<Account> accounts, Account.Id id) {
- return accounts.stream().filter(account -> account.getId().equals(id)).findAny();
+ return accounts.stream().filter(account -> account.id().equals(id)).findAny();
}
public static AuditLogFormatter createPartiallyWorkingFallBack() {
@@ -119,7 +119,7 @@ public class AuditLogFormatter {
* @return a {@code PersonIdent} which can be used for the author of a commit
*/
public PersonIdent getParsableAuthorIdent(Account account, PersonIdent personIdent) {
- return getParsableAuthorIdent(account.getName(), account.getId(), personIdent);
+ return getParsableAuthorIdent(account.getName(), account.id(), personIdent);
}
/**
diff --git a/java/com/google/gerrit/server/group/db/AuditLogReader.java b/java/com/google/gerrit/server/group/db/AuditLogReader.java
index 106ee6b472..d8f0a0fcc0 100644
--- a/java/com/google/gerrit/server/group/db/AuditLogReader.java
+++ b/java/com/google/gerrit/server/group/db/AuditLogReader.java
@@ -14,18 +14,19 @@
package com.google.gerrit.server.group.db;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.AccountGroupByIdAudit;
+import com.google.gerrit.entities.AccountGroupMemberAudit;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.notedb.NoteDbUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -49,12 +50,10 @@ import org.eclipse.jgit.util.RawParseUtils;
public class AuditLogReader {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final String serverId;
private final AllUsersName allUsersName;
@Inject
- public AuditLogReader(@GerritServerId String serverId, AllUsersName allUsersName) {
- this.serverId = serverId;
+ public AuditLogReader(AllUsersName allUsersName) {
this.allUsersName = allUsersName;
}
@@ -69,70 +68,79 @@ public class AuditLogReader {
private ImmutableList<AccountGroupMemberAudit> getMembersAudit(
AccountGroup.Id groupId, List<ParsedCommit> commits) {
- ListMultimap<MemberKey, AccountGroupMemberAudit> audits =
+ ListMultimap<MemberKey, AccountGroupMemberAudit.Builder> audits =
MultimapBuilder.hashKeys().linkedListValues().build();
- ImmutableList.Builder<AccountGroupMemberAudit> result = ImmutableList.builder();
+ List<AccountGroupMemberAudit.Builder> result = new ArrayList<>();
for (ParsedCommit pc : commits) {
for (Account.Id id : pc.addedMembers()) {
MemberKey key = MemberKey.create(groupId, id);
- AccountGroupMemberAudit audit =
- new AccountGroupMemberAudit(
- new AccountGroupMemberAudit.Key(id, groupId, pc.when()), pc.authorId());
+ AccountGroupMemberAudit.Builder audit =
+ AccountGroupMemberAudit.builder()
+ .memberId(id)
+ .groupId(groupId)
+ .addedOn(pc.when())
+ .addedBy(pc.authorId());
audits.put(key, audit);
result.add(audit);
}
for (Account.Id id : pc.removedMembers()) {
- List<AccountGroupMemberAudit> adds = audits.get(MemberKey.create(groupId, id));
+ List<AccountGroupMemberAudit.Builder> adds = audits.get(MemberKey.create(groupId, id));
if (!adds.isEmpty()) {
- AccountGroupMemberAudit audit = adds.remove(0);
+ AccountGroupMemberAudit.Builder audit = adds.remove(0);
audit.removed(pc.authorId(), pc.when());
} else {
// Match old behavior of DbGroupAuditListener and add a "legacy" add/remove pair.
- AccountGroupMemberAudit audit =
- new AccountGroupMemberAudit(
- new AccountGroupMemberAudit.Key(id, groupId, pc.when()), pc.authorId());
- audit.removedLegacy();
+ AccountGroupMemberAudit.Builder audit =
+ AccountGroupMemberAudit.builder()
+ .groupId(groupId)
+ .memberId(id)
+ .addedOn(pc.when())
+ .addedBy(pc.authorId())
+ .removedLegacy();
result.add(audit);
}
}
}
- return result.build();
+ return result.stream().map(AccountGroupMemberAudit.Builder::build).collect(toImmutableList());
}
- public ImmutableList<AccountGroupByIdAud> getSubgroupsAudit(
+ public ImmutableList<AccountGroupByIdAudit> getSubgroupsAudit(
Repository repo, AccountGroup.UUID uuid) throws IOException, ConfigInvalidException {
return getSubgroupsAudit(getGroupId(repo, uuid), parseCommits(repo, uuid));
}
- private ImmutableList<AccountGroupByIdAud> getSubgroupsAudit(
+ private ImmutableList<AccountGroupByIdAudit> getSubgroupsAudit(
AccountGroup.Id groupId, List<ParsedCommit> commits) {
- ListMultimap<SubgroupKey, AccountGroupByIdAud> audits =
+ ListMultimap<SubgroupKey, AccountGroupByIdAudit.Builder> audits =
MultimapBuilder.hashKeys().linkedListValues().build();
- ImmutableList.Builder<AccountGroupByIdAud> result = ImmutableList.builder();
+ List<AccountGroupByIdAudit.Builder> result = new ArrayList<>();
for (ParsedCommit pc : commits) {
for (AccountGroup.UUID uuid : pc.addedSubgroups()) {
SubgroupKey key = SubgroupKey.create(groupId, uuid);
- AccountGroupByIdAud audit =
- new AccountGroupByIdAud(
- new AccountGroupByIdAud.Key(groupId, uuid, pc.when()), pc.authorId());
+ AccountGroupByIdAudit.Builder audit =
+ AccountGroupByIdAudit.builder()
+ .groupId(groupId)
+ .includeUuid(uuid)
+ .addedOn(pc.when())
+ .addedBy(pc.authorId());
audits.put(key, audit);
result.add(audit);
}
for (AccountGroup.UUID uuid : pc.removedSubgroups()) {
- List<AccountGroupByIdAud> adds = audits.get(SubgroupKey.create(groupId, uuid));
+ List<AccountGroupByIdAudit.Builder> adds = audits.get(SubgroupKey.create(groupId, uuid));
if (!adds.isEmpty()) {
- AccountGroupByIdAud audit = adds.remove(0);
+ AccountGroupByIdAudit.Builder audit = adds.remove(0);
audit.removed(pc.authorId(), pc.when());
} else {
// Unlike members, DbGroupAuditListener didn't insert an add/remove pair here.
}
}
}
- return result.build();
+ return result.stream().map(AccountGroupByIdAudit.Builder::build).collect(toImmutableList());
}
private Optional<ParsedCommit> parse(AccountGroup.UUID uuid, RevCommit c) {
- Optional<Account.Id> authorId = NoteDbUtil.parseIdent(c.getAuthorIdent(), serverId);
+ Optional<Account.Id> authorId = NoteDbUtil.parseIdent(c.getAuthorIdent());
if (!authorId.isPresent()) {
// Only report audit events from identified users, since this was a non-nullable field in
// ReviewDb. May be revisited.
@@ -168,7 +176,7 @@ public class AuditLogReader {
private Optional<Account.Id> parseAccount(AccountGroup.UUID uuid, RevCommit c, FooterLine line) {
Optional<Account.Id> result =
Optional.ofNullable(RawParseUtils.parsePersonIdent(line.getValue()))
- .flatMap(ident -> NoteDbUtil.parseIdent(ident, serverId));
+ .flatMap(ident -> NoteDbUtil.parseIdent(ident));
if (!result.isPresent()) {
logInvalid(uuid, c, line);
}
@@ -182,7 +190,7 @@ public class AuditLogReader {
logInvalid(uuid, c, line);
return Optional.empty();
}
- return Optional.of(new AccountGroup.UUID(ident.getEmailAddress()));
+ return Optional.of(AccountGroup.uuid(ident.getEmailAddress()));
}
private static void logInvalid(AccountGroup.UUID uuid, RevCommit c, FooterLine line) {
diff --git a/java/com/google/gerrit/server/group/db/GroupConfig.java b/java/com/google/gerrit/server/group/db/GroupConfig.java
index 903b9c08b0..ab5c9b8563 100644
--- a/java/com/google/gerrit/server/group/db/GroupConfig.java
+++ b/java/com/google/gerrit/server/group/db/GroupConfig.java
@@ -24,11 +24,11 @@ import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.DuplicateKeyException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import com.google.gerrit.server.group.InternalGroup;
@@ -411,12 +411,12 @@ public class GroupConfig extends VersionedMetaData {
}
private ImmutableSet<Account.Id> readMembers() throws IOException, ConfigInvalidException {
- return readFromFile(MEMBERS_FILE, entry -> new Account.Id(Integer.parseInt(entry)));
+ return readFromFile(MEMBERS_FILE, entry -> Account.id(Integer.parseInt(entry)));
}
private ImmutableSet<AccountGroup.UUID> readSubgroups()
throws IOException, ConfigInvalidException {
- return readFromFile(SUBGROUPS_FILE, AccountGroup.UUID::new);
+ return readFromFile(SUBGROUPS_FILE, AccountGroup::uuid);
}
private <E> ImmutableSet<E> readFromFile(String filePath, Function<String, E> fromStringFunction)
diff --git a/java/com/google/gerrit/server/group/db/GroupConfigEntry.java b/java/com/google/gerrit/server/group/db/GroupConfigEntry.java
index f7a104d7d9..81f5c7e4d0 100644
--- a/java/com/google/gerrit/server/group/db/GroupConfigEntry.java
+++ b/java/com/google/gerrit/server/group/db/GroupConfigEntry.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.group.db;
import com.google.common.base.Strings;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
@@ -45,7 +45,7 @@ enum GroupConfigEntry {
String.format(
"ID of the group %s must not be negative, found %d", groupUuid.get(), id));
}
- group.setId(new AccountGroup.Id(id));
+ group.setId(AccountGroup.id(id));
}
@Override
@@ -77,7 +77,7 @@ enum GroupConfigEntry {
// the NoteDb migration converted such groups faithfully, so we need to be able to read them
// back here.
name = Strings.nullToEmpty(name);
- group.setNameKey(new AccountGroup.NameKey(name));
+ group.setNameKey(AccountGroup.nameKey(name));
}
@Override
@@ -135,7 +135,7 @@ enum GroupConfigEntry {
throw new ConfigInvalidException(
String.format("Owner UUID of the group %s must be defined", groupUuid.get()));
}
- group.setOwnerGroupUUID(new AccountGroup.UUID(ownerGroupUuid));
+ group.setOwnerGroupUUID(AccountGroup.uuid(ownerGroupUuid));
}
@Override
diff --git a/java/com/google/gerrit/server/group/db/GroupNameNotes.java b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
index eda7153cf2..70d7a1aad3 100644
--- a/java/com/google/gerrit/server/group/db/GroupNameNotes.java
+++ b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
@@ -29,10 +29,11 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.DuplicateKeyException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import java.io.IOException;
import java.util.Collection;
@@ -266,7 +267,7 @@ public class GroupNameNotes extends VersionedMetaData {
RevCommit oldCommit = ref != null ? rw.parseCommit(ref.getObjectId()) : null;
for (Map.Entry<AccountGroup.UUID, String> e : biMap.entrySet()) {
- AccountGroup.NameKey nameKey = new AccountGroup.NameKey(e.getValue());
+ AccountGroup.NameKey nameKey = AccountGroup.nameKey(e.getValue());
ObjectId noteKey = getNoteKey(nameKey);
noteMap.set(noteKey, getAsNoteData(e.getKey(), nameKey), inserter);
}
@@ -286,7 +287,7 @@ public class GroupNameNotes extends VersionedMetaData {
cb.setMessage("Store " + n + " group name" + (n != 1 ? "s" : ""));
ObjectId newId = inserter.insert(cb).copy();
- ObjectId oldId = oldCommit != null ? oldCommit.copy() : ObjectId.zeroId();
+ ObjectId oldId = ObjectIds.copyOrZero(oldCommit);
bru.addCommand(new ReceiveCommand(oldId, newId, RefNames.REFS_GROUPNAMES));
}
}
@@ -442,7 +443,7 @@ public class GroupNameNotes extends VersionedMetaData {
throw new ConfigInvalidException(String.format("UUID for group '%s' must be defined", name));
}
- return new GroupReference(new AccountGroup.UUID(uuid), name);
+ return new GroupReference(AccountGroup.uuid(uuid), name);
}
private String getCommitMessage() {
diff --git a/java/com/google/gerrit/server/group/db/Groups.java b/java/com/google/gerrit/server/group/db/Groups.java
index 37de011fb7..2925cb3ee2 100644
--- a/java/com/google/gerrit/server/group/db/Groups.java
+++ b/java/com/google/gerrit/server/group/db/Groups.java
@@ -17,9 +17,9 @@ package com.google.gerrit.server.group.db;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.AccountGroupByIdAudit;
+import com.google.gerrit.entities.AccountGroupMemberAudit;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.group.InternalGroup;
@@ -152,7 +152,7 @@ public class Groups {
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the group couldn't be retrieved from NoteDb
*/
- public List<AccountGroupByIdAud> getSubgroupsAudit(Repository repo, AccountGroup.UUID groupUuid)
+ public List<AccountGroupByIdAudit> getSubgroupsAudit(Repository repo, AccountGroup.UUID groupUuid)
throws IOException, ConfigInvalidException {
return auditLogReader.getSubgroupsAudit(repo, groupUuid);
}
diff --git a/java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java b/java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java
index 3afb793566..8a6cd9465e 100644
--- a/java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java
@@ -17,9 +17,9 @@ package com.google.gerrit.server.group.db;
import static com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo.error;
import static com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo.warning;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.Accounts;
import com.google.gerrit.server.account.GroupBackend;
diff --git a/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java b/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
index c3ca60bf11..04143044cf 100644
--- a/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
+++ b/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
@@ -24,10 +24,10 @@ import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.group.InternalGroup;
import com.google.inject.Inject;
@@ -163,7 +163,7 @@ public class GroupsNoteDbConsistencyChecker {
continue;
}
- ObjectId nameKey = GroupNameNotes.getNoteKey(new AccountGroup.NameKey(gRef.getName()));
+ ObjectId nameKey = GroupNameNotes.getNoteKey(AccountGroup.nameKey(gRef.getName()));
if (!Objects.equals(nameKey, note)) {
result.problems.add(
error("notename entry %s does not match name %s", note, gRef.getName()));
diff --git a/java/com/google/gerrit/server/group/db/GroupsUpdate.java b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
index b450ab856e..7f1ba6a178 100644
--- a/java/com/google/gerrit/server/group/db/GroupsUpdate.java
+++ b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
@@ -21,13 +21,13 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
@@ -42,6 +42,7 @@ import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.GroupAuditService;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.index.group.GroupIndexer;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.update.RetryHelper;
@@ -265,7 +266,10 @@ public class GroupsUpdate {
throws DuplicateKeyException, IOException, ConfigInvalidException {
try (TraceTimer timer =
TraceContext.newTimer(
- "Creating group '%s'", groupUpdate.getName().orElseGet(groupCreation::getNameKey))) {
+ "Creating group",
+ Metadata.builder()
+ .groupName(groupUpdate.getName().orElseGet(groupCreation::getNameKey).get())
+ .build())) {
InternalGroup createdGroup = createGroupInNoteDbWithRetry(groupCreation, groupUpdate);
evictCachesOnGroupCreation(createdGroup);
dispatchAuditEventsOnGroupCreation(createdGroup);
@@ -285,7 +289,9 @@ public class GroupsUpdate {
*/
public void updateGroup(AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
throws DuplicateKeyException, IOException, NoSuchGroupException, ConfigInvalidException {
- try (TraceTimer timer = TraceContext.newTimer("Updating group %s", groupUuid)) {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Updating group", Metadata.builder().groupUuid(groupUuid.get()).build())) {
Optional<Timestamp> updatedOn = groupUpdate.getUpdatedOn();
if (!updatedOn.isPresent()) {
updatedOn = Optional.of(TimeUtil.nowTs());
diff --git a/java/com/google/gerrit/server/group/db/InternalGroupCreation.java b/java/com/google/gerrit/server/group/db/InternalGroupCreation.java
index bb21d62ece..89885471ea 100644
--- a/java/com/google/gerrit/server/group/db/InternalGroupCreation.java
+++ b/java/com/google/gerrit/server/group/db/InternalGroupCreation.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.group.db;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
/**
* Definition of all properties necessary for a group creation.
diff --git a/java/com/google/gerrit/server/group/db/InternalGroupUpdate.java b/java/com/google/gerrit/server/group/db/InternalGroupUpdate.java
index bff295217a..9e6539ae37 100644
--- a/java/com/google/gerrit/server/group/db/InternalGroupUpdate.java
+++ b/java/com/google/gerrit/server/group/db/InternalGroupUpdate.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.group.db;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import java.sql.Timestamp;
import java.util.Optional;
import java.util.Set;
diff --git a/java/com/google/gerrit/server/group/db/RenameGroupOp.java b/java/com/google/gerrit/server/group/db/RenameGroupOp.java
index e002192b50..35ff5134de 100644
--- a/java/com/google/gerrit/server/group/db/RenameGroupOp.java
+++ b/java/com/google/gerrit/server/group/db/RenameGroupOp.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.group.db;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.DefaultQueueOp;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
diff --git a/java/com/google/gerrit/server/group/db/testing/BUILD b/java/com/google/gerrit/server/group/db/testing/BUILD
index 0cc45fd020..8f33f98a48 100644
--- a/java/com/google/gerrit/server/group/db/testing/BUILD
+++ b/java/com/google/gerrit/server/group/db/testing/BUILD
@@ -9,7 +9,7 @@ java_library(
deps = [
"//java/com/google/gerrit/server",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
+ "//lib:jgit",
+ "//lib:jgit-junit",
],
)
diff --git a/java/com/google/gerrit/server/group/testing/BUILD b/java/com/google/gerrit/server/group/testing/BUILD
index 9b6d8de925..fd61dffab0 100644
--- a/java/com/google/gerrit/server/group/testing/BUILD
+++ b/java/com/google/gerrit/server/group/testing/BUILD
@@ -8,10 +8,10 @@ java_library(
srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
"//lib/truth",
],
)
diff --git a/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java b/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java
index 2f91394dee..8f20e927b8 100644
--- a/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java
+++ b/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java
@@ -18,17 +18,16 @@ import static com.google.common.truth.Truth.assertAbout;
import com.google.common.truth.BooleanSubject;
import com.google.common.truth.ComparableSubject;
-import com.google.common.truth.DefaultSubject;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IterableSubject;
import com.google.common.truth.StringSubject;
import com.google.common.truth.Subject;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import java.sql.Timestamp;
import org.eclipse.jgit.lib.ObjectId;
-public class InternalGroupSubject extends Subject<InternalGroupSubject, InternalGroup> {
+public class InternalGroupSubject extends Subject {
public static InternalGroupSubject assertThat(InternalGroup group) {
return assertAbout(internalGroups()).that(group);
@@ -38,73 +37,65 @@ public class InternalGroupSubject extends Subject<InternalGroupSubject, Internal
return InternalGroupSubject::new;
}
- private InternalGroupSubject(FailureMetadata metadata, InternalGroup actual) {
- super(metadata, actual);
+ private final InternalGroup group;
+
+ private InternalGroupSubject(FailureMetadata metadata, InternalGroup group) {
+ super(metadata, group);
+ this.group = group;
}
- public ComparableSubject<?, AccountGroup.UUID> groupUuid() {
+ public ComparableSubject<AccountGroup.UUID> groupUuid() {
isNotNull();
- InternalGroup group = actual();
- return check("groupUuid()").that(group.getGroupUUID());
+ return check("getGroupUUID()").that(group.getGroupUUID());
}
- public ComparableSubject<?, AccountGroup.NameKey> nameKey() {
+ public ComparableSubject<AccountGroup.NameKey> nameKey() {
isNotNull();
- InternalGroup group = actual();
- return check("nameKey()").that(group.getNameKey());
+ return check("getNameKey()").that(group.getNameKey());
}
public StringSubject name() {
isNotNull();
- InternalGroup group = actual();
- return check("name()").that(group.getName());
+ return check("getName()").that(group.getName());
}
- public Subject<DefaultSubject, Object> id() {
+ public Subject id() {
isNotNull();
- InternalGroup group = actual();
- return check("id()").that(group.getId());
+ return check("getId()").that(group.getId());
}
public StringSubject description() {
isNotNull();
- InternalGroup group = actual();
- return check("description()").that(group.getDescription());
+ return check("getDescription()").that(group.getDescription());
}
- public ComparableSubject<?, AccountGroup.UUID> ownerGroupUuid() {
+ public ComparableSubject<AccountGroup.UUID> ownerGroupUuid() {
isNotNull();
- InternalGroup group = actual();
- return check("ownerGroupUuid()").that(group.getOwnerGroupUUID());
+ return check("getOwnerGroupUUID()").that(group.getOwnerGroupUUID());
}
public BooleanSubject visibleToAll() {
isNotNull();
- InternalGroup group = actual();
- return check("visibleToAll()").that(group.isVisibleToAll());
+ return check("isVisibleToAll()").that(group.isVisibleToAll());
}
- public ComparableSubject<?, Timestamp> createdOn() {
+ public ComparableSubject<Timestamp> createdOn() {
isNotNull();
- InternalGroup group = actual();
- return check("createdOn()").that(group.getCreatedOn());
+ return check("getCreatedOn()").that(group.getCreatedOn());
}
public IterableSubject members() {
isNotNull();
- InternalGroup group = actual();
- return check("members()").that(group.getMembers());
+ return check("getMembers()").that(group.getMembers());
}
public IterableSubject subgroups() {
isNotNull();
- InternalGroup group = actual();
- return check("subgroups()").that(group.getSubgroups());
+ return check("getSubgroups()").that(group.getSubgroups());
}
- public ComparableSubject<?, ObjectId> refState() {
+ public ComparableSubject<ObjectId> refState() {
isNotNull();
- InternalGroup group = actual();
- return check("refState()").that(group.getRefState());
+ return check("getRefState()").that(group.getRefState());
}
}
diff --git a/java/com/google/gerrit/server/group/testing/TestGroupBackend.java b/java/com/google/gerrit/server/group/testing/TestGroupBackend.java
index 550b15c421..51c7ca3a0a 100644
--- a/java/com/google/gerrit/server/group/testing/TestGroupBackend.java
+++ b/java/com/google/gerrit/server/group/testing/TestGroupBackend.java
@@ -20,7 +20,7 @@ import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupMembership;
@@ -43,7 +43,7 @@ public class TestGroupBackend implements GroupBackend {
*/
public GroupDescription.Basic create(String name) {
requireNonNull(name);
- return create(new AccountGroup.UUID(name.startsWith(PREFIX) ? name : PREFIX + name));
+ return create(AccountGroup.uuid(name.startsWith(PREFIX) ? name : PREFIX + name));
}
/**
diff --git a/java/com/google/gerrit/server/index/AbstractIndexModule.java b/java/com/google/gerrit/server/index/AbstractIndexModule.java
index 352ea4b3f2..995b4b6bd4 100644
--- a/java/com/google/gerrit/server/index/AbstractIndexModule.java
+++ b/java/com/google/gerrit/server/index/AbstractIndexModule.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.index;
import com.google.gerrit.index.IndexConfig;
-import com.google.gerrit.index.Schema;
import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -44,9 +43,21 @@ public abstract class AbstractIndexModule extends AbstractModule {
@Override
protected void configure() {
if (slave) {
- bind(AccountIndex.Factory.class).toInstance(AbstractIndexModule::createDummyIndexFactory);
- bind(ChangeIndex.Factory.class).toInstance(AbstractIndexModule::createDummyIndexFactory);
- bind(ProjectIndex.Factory.class).toInstance(AbstractIndexModule::createDummyIndexFactory);
+ bind(AccountIndex.Factory.class)
+ .toInstance(
+ s -> {
+ throw new UnsupportedOperationException();
+ });
+ bind(ChangeIndex.Factory.class)
+ .toInstance(
+ s -> {
+ throw new UnsupportedOperationException();
+ });
+ bind(ProjectIndex.Factory.class)
+ .toInstance(
+ s -> {
+ throw new UnsupportedOperationException();
+ });
} else {
install(
new FactoryModuleBuilder()
@@ -74,11 +85,6 @@ public abstract class AbstractIndexModule extends AbstractModule {
}
}
- @SuppressWarnings("unused")
- private static <T> T createDummyIndexFactory(Schema<?> schema) {
- throw new UnsupportedOperationException();
- }
-
protected abstract Class<? extends AccountIndex> getAccountIndex();
protected abstract Class<? extends ChangeIndex> getChangeIndex();
diff --git a/java/com/google/gerrit/server/index/IndexModule.java b/java/com/google/gerrit/server/index/IndexModule.java
index e7b892d611..6db00f5a4b 100644
--- a/java/com/google/gerrit/server/index/IndexModule.java
+++ b/java/com/google/gerrit/server/index/IndexModule.java
@@ -22,10 +22,10 @@ import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
-import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.IndexDefinition;
+import com.google.gerrit.index.IndexType;
import com.google.gerrit.index.SchemaDefinitions;
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.index.project.ProjectIndexRewriter;
@@ -71,11 +71,6 @@ import org.eclipse.jgit.lib.Config;
* (e.g. Lucene).
*/
public class IndexModule extends LifecycleModule {
- public enum IndexType {
- LUCENE,
- ELASTICSEARCH
- }
-
public static final ImmutableCollection<SchemaDefinitions<?>> ALL_SCHEMA_DEFS =
ImmutableList.of(
AccountSchemaDefinitions.INSTANCE,
@@ -86,19 +81,8 @@ public class IndexModule extends LifecycleModule {
/** Type of secondary index. */
public static IndexType getIndexType(Injector injector) {
Config cfg = injector.getInstance(Key.get(Config.class, GerritServerConfig.class));
- if (cfg != null) {
- return cfg.getEnum("index", null, "type", IndexType.LUCENE);
- }
- return IndexType.LUCENE;
- }
-
- /** Type of secondary index. */
- // TODO: stop relying on this method fostering error-prone string comparisons.
- public static String getIndexType(@Nullable Config cfg) {
- if (cfg != null) {
- return cfg.getString("index", null, "type");
- }
- return IndexType.LUCENE.name();
+ String configValue = cfg != null ? cfg.getString("index", null, "type") : null;
+ return new IndexType(configValue);
}
private final int threads;
diff --git a/java/com/google/gerrit/server/index/IndexUtils.java b/java/com/google/gerrit/server/index/IndexUtils.java
index 4b5cd4946c..a45777edeb 100644
--- a/java/com/google/gerrit/server/index/IndexUtils.java
+++ b/java/com/google/gerrit/server/index/IndexUtils.java
@@ -16,18 +16,21 @@ package com.google.gerrit.server.index;
import static com.google.gerrit.server.index.change.ChangeField.CHANGE;
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
+import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID_STR;
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.project.ProjectField;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.account.AccountField;
import com.google.gerrit.server.index.group.GroupField;
+import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.SingleGroupUser;
import java.io.IOException;
import java.util.Set;
@@ -56,17 +59,18 @@ public final class IndexUtils {
}
}
- public static Set<String> accountFields(QueryOptions opts) {
- return accountFields(opts.fields());
+ public static Set<String> accountFields(QueryOptions opts, boolean useLegacyNumericFields) {
+ return accountFields(opts.fields(), useLegacyNumericFields);
}
- public static Set<String> accountFields(Set<String> fields) {
- return fields.contains(AccountField.ID.getName())
- ? fields
- : Sets.union(fields, ImmutableSet.of(AccountField.ID.getName()));
+ public static Set<String> accountFields(Set<String> fields, boolean useLegacyNumericFields) {
+ String idFieldName =
+ useLegacyNumericFields ? AccountField.ID.getName() : AccountField.ID_STR.getName();
+ return fields.contains(idFieldName) ? fields : Sets.union(fields, ImmutableSet.of(idFieldName));
}
- public static Set<String> changeFields(QueryOptions opts) {
+ public static Set<String> changeFields(QueryOptions opts, boolean useLegacyNumericFields) {
+ FieldDef<ChangeData, ?> idField = useLegacyNumericFields ? LEGACY_ID : LEGACY_ID_STR;
// Ensure we request enough fields to construct a ChangeData. We need both
// change ID and project, which can either come via the Change field or
// separate fields.
@@ -75,10 +79,10 @@ public final class IndexUtils {
// A Change is always sufficient.
return fs;
}
- if (fs.contains(PROJECT.getName()) && fs.contains(LEGACY_ID.getName())) {
+ if (fs.contains(PROJECT.getName()) && fs.contains(idField.getName())) {
return fs;
}
- return Sets.union(fs, ImmutableSet.of(LEGACY_ID.getName(), PROJECT.getName()));
+ return Sets.union(fs, ImmutableSet.of(idField.getName(), PROJECT.getName()));
}
public static Set<String> groupFields(QueryOptions opts) {
diff --git a/java/com/google/gerrit/server/index/account/AccountField.java b/java/com/google/gerrit/server/index/account/AccountField.java
index f67a41d045..0dd22cea91 100644
--- a/java/com/google/gerrit/server/index/account/AccountField.java
+++ b/java/com/google/gerrit/server/index/account/AccountField.java
@@ -21,28 +21,33 @@ import static com.google.gerrit.index.FieldDef.storedOnly;
import static com.google.gerrit.index.FieldDef.timestamp;
import static java.util.stream.Collectors.toSet;
-import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.RefState;
import com.google.gerrit.index.SchemaUtil;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.AllUsersNameProvider;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
/** Secondary index schemas for accounts. */
public class AccountField {
public static final FieldDef<AccountState, Integer> ID =
- integer("id").stored().build(a -> a.getAccount().getId().get());
+ integer("id").stored().build(a -> a.account().id().get());
+
+ public static final FieldDef<AccountState, String> ID_STR =
+ exact("id_str").stored().build(a -> String.valueOf(a.account().id().get()));
/**
* External IDs.
@@ -52,7 +57,7 @@ public class AccountField {
*/
public static final FieldDef<AccountState, Iterable<String>> EXTERNAL_ID =
exact("external_id")
- .buildRepeatable(a -> Iterables.transform(a.getExternalIds(), id -> id.key().get()));
+ .buildRepeatable(a -> Iterables.transform(a.externalIds(), id -> id.key().get()));
/**
* Fuzzy prefix match on name and email parts.
@@ -67,7 +72,7 @@ public class AccountField {
public static final FieldDef<AccountState, Iterable<String>> NAME_PART =
prefix("name")
.buildRepeatable(
- a -> getNameParts(a, Iterables.transform(a.getExternalIds(), ExternalId::email)));
+ a -> getNameParts(a, Iterables.transform(a.externalIds(), ExternalId::email)));
/**
* Fuzzy prefix match on name and preferred email parts. Parts of secondary emails are not
@@ -75,13 +80,13 @@ public class AccountField {
*/
public static final FieldDef<AccountState, Iterable<String>> NAME_PART_NO_SECONDARY_EMAIL =
prefix("name2")
- .buildRepeatable(a -> getNameParts(a, Arrays.asList(a.getAccount().getPreferredEmail())));
+ .buildRepeatable(a -> getNameParts(a, Arrays.asList(a.account().preferredEmail())));
public static final FieldDef<AccountState, String> FULL_NAME =
- exact("full_name").build(a -> a.getAccount().getFullName());
+ exact("full_name").build(a -> a.account().fullName());
public static final FieldDef<AccountState, String> ACTIVE =
- exact("inactive").build(a -> a.getAccount().isActive() ? "1" : "0");
+ exact("inactive").build(a -> a.account().isActive() ? "1" : "0");
/**
* All emails (preferred email + secondary emails). Use this field only if the current user is
@@ -93,10 +98,10 @@ public class AccountField {
prefix("email")
.buildRepeatable(
a ->
- FluentIterable.from(a.getExternalIds())
+ FluentIterable.from(a.externalIds())
.transform(ExternalId::email)
- .append(Collections.singleton(a.getAccount().getPreferredEmail()))
- .filter(Predicates.notNull())
+ .append(Collections.singleton(a.account().preferredEmail()))
+ .filter(Objects::nonNull)
.transform(String::toLowerCase)
.toSet());
@@ -104,24 +109,24 @@ public class AccountField {
prefix("preferredemail")
.build(
a -> {
- String preferredEmail = a.getAccount().getPreferredEmail();
+ String preferredEmail = a.account().preferredEmail();
return preferredEmail != null ? preferredEmail.toLowerCase() : null;
});
public static final FieldDef<AccountState, String> PREFERRED_EMAIL_EXACT =
- exact("preferredemail_exact").build(a -> a.getAccount().getPreferredEmail());
+ exact("preferredemail_exact").build(a -> a.account().preferredEmail());
public static final FieldDef<AccountState, Timestamp> REGISTERED =
- timestamp("registered").build(a -> a.getAccount().getRegisteredOn());
+ timestamp("registered").build(a -> a.account().registeredOn());
public static final FieldDef<AccountState, String> USERNAME =
- exact("username").build(a -> a.getUserName().map(String::toLowerCase).orElse(""));
+ exact("username").build(a -> a.userName().map(String::toLowerCase).orElse(""));
public static final FieldDef<AccountState, Iterable<String>> WATCHED_PROJECT =
exact("watchedproject")
.buildRepeatable(
a ->
- FluentIterable.from(a.getProjectWatches().keySet())
+ FluentIterable.from(a.projectWatches().keySet())
.transform(k -> k.project().get())
.toSet());
@@ -136,15 +141,19 @@ public class AccountField {
storedOnly("ref_state")
.buildRepeatable(
a -> {
- if (a.getAccount().getMetaId() == null) {
+ if (a.account().metaId() == null) {
return ImmutableList.of();
}
return ImmutableList.of(
RefState.create(
- RefNames.refsUsers(a.getAccount().getId()),
- ObjectId.fromString(a.getAccount().getMetaId()))
- .toByteArray(a.getAllUsersNameForIndexing()));
+ RefNames.refsUsers(a.account().id()),
+ ObjectId.fromString(a.account().metaId()))
+ // We use the default AllUsers name to avoid having to pass around that
+ // variable just for indexing.
+ // This field is only used for staleness detection which will discover the
+ // default name and replace it with the actually configured name.
+ .toByteArray(new AllUsersName(AllUsersNameProvider.DEFAULT)));
});
/**
@@ -157,13 +166,13 @@ public class AccountField {
storedOnly("external_id_state")
.buildRepeatable(
a ->
- a.getExternalIds().stream()
+ a.externalIds().stream()
.filter(e -> e.blobId() != null)
.map(ExternalId::toByteArray)
.collect(toSet()));
private static final Set<String> getNameParts(AccountState a, Iterable<String> emails) {
- String fullName = a.getAccount().getFullName();
+ String fullName = a.account().fullName();
Set<String> parts = SchemaUtil.getNameParts(fullName, emails);
// Additional values not currently added by getPersonParts.
diff --git a/java/com/google/gerrit/server/index/account/AccountIndex.java b/java/com/google/gerrit/server/index/account/AccountIndex.java
index 5c1b3dcafc..1838edfb25 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndex.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndex.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.index.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.query.account.AccountPredicates;
@@ -27,6 +27,6 @@ public interface AccountIndex extends Index<Account.Id, AccountState> {
@Override
default Predicate<AccountState> keyPredicate(Account.Id id) {
- return AccountPredicates.id(id);
+ return AccountPredicates.id(getSchema(), id);
}
}
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexCollection.java b/java/com/google/gerrit/server/index/account/AccountIndexCollection.java
index 2a14f9bfe9..eb1f7846da 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexCollection.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexCollection.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.index.account;
import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.IndexCollection;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountState;
import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexDefinition.java b/java/com/google/gerrit/server/index/account/AccountIndexDefinition.java
index af23b52406..3a34d47eb8 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexDefinition.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexDefinition.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.index.account;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.IndexDefinition;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountState;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java b/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
index 35b967ceea..643c2491ed 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
@@ -16,22 +16,27 @@ package com.google.gerrit.server.index.account;
import static java.util.Objects.requireNonNull;
+import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.IndexRewriter;
import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.query.IndexPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.index.query.TooManyTermsInQueryException;
import com.google.gerrit.server.account.AccountState;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import org.eclipse.jgit.util.MutableInteger;
@Singleton
public class AccountIndexRewriter implements IndexRewriter<AccountState> {
-
private final AccountIndexCollection indexes;
+ private final IndexConfig config;
@Inject
- AccountIndexRewriter(AccountIndexCollection indexes) {
+ AccountIndexRewriter(AccountIndexCollection indexes, IndexConfig config) {
this.indexes = indexes;
+ this.config = config;
}
@Override
@@ -39,6 +44,32 @@ public class AccountIndexRewriter implements IndexRewriter<AccountState> {
throws QueryParseException {
AccountIndex index = indexes.getSearchIndex();
requireNonNull(index, "no active search index configured for accounts");
+ validateMaxTermsInQuery(in);
return new IndexedAccountQuery(index, in, opts);
}
+
+ /**
+ * Validates whether a query has too many terms.
+ *
+ * @param predicate the predicate for which the leaf predicates should be counted
+ * @throws QueryParseException thrown if the query has too many terms
+ */
+ public void validateMaxTermsInQuery(Predicate<AccountState> predicate)
+ throws QueryParseException {
+ MutableInteger leafTerms = new MutableInteger();
+ validateMaxTermsInQuery(predicate, leafTerms);
+ }
+
+ private void validateMaxTermsInQuery(Predicate<AccountState> predicate, MutableInteger leafTerms)
+ throws TooManyTermsInQueryException {
+ if (!(predicate instanceof IndexPredicate)) {
+ if (++leafTerms.value > config.maxTerms()) {
+ throw new TooManyTermsInQueryException();
+ }
+ }
+
+ for (Predicate<AccountState> childPredicate : predicate.getChildren()) {
+ validateMaxTermsInQuery(childPredicate, leafTerms);
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexer.java b/java/com/google/gerrit/server/index/account/AccountIndexer.java
index 7f4f295a1e..8ced00598f 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexer.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexer.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.index.account;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
public interface AccountIndexer {
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
index 932e2c30aa..b908846336 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
@@ -17,12 +17,13 @@ package com.google.gerrit.server.index.account;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.events.AccountIndexedListener;
import com.google.gerrit.index.Index;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -90,13 +91,21 @@ public class AccountIndexerImpl implements AccountIndexer {
if (accountState.isPresent()) {
try (TraceTimer traceTimer =
TraceContext.newTimer(
- "Replacing account %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ "Replacing account in index",
+ Metadata.builder()
+ .accountId(id.get())
+ .indexVersion(i.getSchema().getVersion())
+ .build())) {
i.replace(accountState.get());
}
} else {
try (TraceTimer traceTimer =
TraceContext.newTimer(
- "Deleting account %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ "Deleting account in index",
+ Metadata.builder()
+ .accountId(id.get())
+ .indexVersion(i.getSchema().getVersion())
+ .build())) {
i.delete(id);
}
}
diff --git a/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java b/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java
index c41814f183..157e290758 100644
--- a/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/account/AccountSchemaDefinitions.java
@@ -49,7 +49,18 @@ public class AccountSchemaDefinitions extends SchemaDefinitions<AccountState> {
@Deprecated static final Schema<AccountState> V9 = schema(V8);
// Lucene index was changed to add additional fields for sorting.
- static final Schema<AccountState> V10 = schema(V9);
+ @Deprecated static final Schema<AccountState> V10 = schema(V9);
+
+ // New numeric types: use dimensional points using the k-d tree geo-spatial data structure
+ // to offer fast single- and multi-dimensional numeric range. As the consequense, integer
+ // document id type is replaced with string document id type.
+ static final Schema<AccountState> V11 =
+ new Schema.Builder<AccountState>()
+ .add(V10)
+ .remove(AccountField.ID)
+ .add(AccountField.ID_STR)
+ .legacyNumericFields(false)
+ .build();
public static final String NAME = "accounts";
public static final AccountSchemaDefinitions INSTANCE = new AccountSchemaDefinitions();
diff --git a/java/com/google/gerrit/server/index/account/AllAccountsIndexer.java b/java/com/google/gerrit/server/index/account/AllAccountsIndexer.java
index acb72366ea..c1077b1c6a 100644
--- a/java/com/google/gerrit/server/index/account/AllAccountsIndexer.java
+++ b/java/com/google/gerrit/server/index/account/AllAccountsIndexer.java
@@ -21,8 +21,8 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.SiteIndexer;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.Accounts;
diff --git a/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java b/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
index 8b9fa27754..b3d961a016 100644
--- a/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
+++ b/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.index.account;
import static com.google.common.base.Preconditions.checkState;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.query.DataSource;
@@ -23,7 +24,6 @@ import com.google.gerrit.index.query.IndexedQuery;
import com.google.gerrit.index.query.Matchable;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountState;
public class IndexedAccountQuery extends IndexedQuery<Account.Id, AccountState>
diff --git a/java/com/google/gerrit/server/index/account/StalenessChecker.java b/java/com/google/gerrit/server/index/account/StalenessChecker.java
index 46647002f3..aad9527092 100644
--- a/java/com/google/gerrit/server/index/account/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/account/StalenessChecker.java
@@ -22,16 +22,17 @@ import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.RefState;
import com.google.gerrit.index.query.FieldBundle;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.IndexUtils;
import com.google.inject.Inject;
@@ -60,6 +61,12 @@ public class StalenessChecker {
AccountField.REF_STATE.getName(),
AccountField.EXTERNAL_ID_STATE.getName());
+ public static final ImmutableSet<String> FIELDS2 =
+ ImmutableSet.of(
+ AccountField.ID_STR.getName(),
+ AccountField.REF_STATE.getName(),
+ AccountField.EXTERNAL_ID_STATE.getName());
+
private final AccountIndexCollection indexes;
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
@@ -92,8 +99,13 @@ public class StalenessChecker {
return false;
}
+ boolean useLegacyNumericFields = i.getSchema().useLegacyNumericFields();
+ ImmutableSet<String> fields = useLegacyNumericFields ? FIELDS : FIELDS2;
Optional<FieldBundle> result =
- i.getRaw(id, QueryOptions.create(indexConfig, 0, 1, IndexUtils.accountFields(FIELDS)));
+ i.getRaw(
+ id,
+ QueryOptions.create(
+ indexConfig, 0, 1, IndexUtils.accountFields(fields, useLegacyNumericFields)));
if (!result.isPresent()) {
// The document is missing in the index.
try (Repository repo = repoManager.openRepository(allUsersName)) {
@@ -106,7 +118,11 @@ public class StalenessChecker {
for (Map.Entry<Project.NameKey, RefState> e :
RefState.parseStates(result.get().getValue(AccountField.REF_STATE)).entries()) {
- try (Repository repo = repoManager.openRepository(e.getKey())) {
+ // Custom All-Users repository names are not indexed. Instead, the default name is used.
+ // Therefore, defer to the currently configured All-Users name.
+ Project.NameKey repoName =
+ e.getKey().get().equals(AllUsersNameProvider.DEFAULT) ? allUsersName : e.getKey();
+ try (Repository repo = repoManager.openRepository(repoName)) {
if (!e.getValue().match(repo)) {
// Ref was modified since the account was indexed.
return true;
diff --git a/java/com/google/gerrit/server/index/change/AllChangesIndexer.java b/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
index a9980ef583..005f4c5691 100644
--- a/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
+++ b/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
@@ -25,10 +25,10 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.index.SiteIndexer;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MultiProgressMonitor;
import com.google.gerrit.server.git.MultiProgressMonitor.Task;
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 394761bb27..07e9b9e4fc 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.index.change;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.gerrit.index.FieldDef.exact;
import static com.google.gerrit.index.FieldDef.fullText;
import static com.google.gerrit.index.FieldDef.intRange;
@@ -42,23 +43,22 @@ import com.google.common.io.Files;
import com.google.common.primitives.Longs;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.converter.ChangeProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetProtoConverter;
+import com.google.gerrit.entities.converter.ProtoConverter;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.RefState;
import com.google.gerrit.index.SchemaUtil;
import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.mail.Address;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.converter.ChangeProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetApprovalProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetProtoConverter;
-import com.google.gerrit.reviewdb.converter.ProtoConverter;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.StarredChangesUtil;
@@ -107,6 +107,9 @@ public class ChangeField {
public static final FieldDef<ChangeData, Integer> LEGACY_ID =
integer("legacy_id").stored().build(cd -> cd.getId().get());
+ public static final FieldDef<ChangeData, String> LEGACY_ID_STR =
+ exact("legacy_id_str").stored().build(cd -> String.valueOf(cd.getId().get()));
+
/** Newer style Change-Id key. */
public static final FieldDef<ChangeData, String> ID =
prefix(ChangeQueryBuilder.FIELD_CHANGE_ID).build(changeGetter(c -> c.getKey().get()));
@@ -128,7 +131,7 @@ public class ChangeField {
/** Reference (aka branch) the change will submit onto. */
public static final FieldDef<ChangeData, String> REF =
- exact(ChangeQueryBuilder.FIELD_REF).build(changeGetter(c -> c.getDest().get()));
+ exact(ChangeQueryBuilder.FIELD_REF).build(changeGetter(c -> c.getDest().branch()));
/** Topic, a short annotation on the branch. */
public static final FieldDef<ChangeData, String> EXACT_TOPIC =
@@ -237,7 +240,6 @@ public class ChangeField {
Set<String> r = new HashSet<>();
for (String path : paths) {
StringBuilder directory = new StringBuilder();
- directory.append("");
r.add(directory.toString());
String nextPart = null;
for (String part : s.split(path)) {
@@ -450,14 +452,8 @@ public class ChangeField {
public static final FieldDef<ChangeData, Iterable<String>> EXACT_COMMIT =
exact(ChangeQueryBuilder.FIELD_EXACTCOMMIT).buildRepeatable(ChangeField::getRevisions);
- private static Set<String> getRevisions(ChangeData cd) {
- Set<String> revisions = new HashSet<>();
- for (PatchSet ps : cd.patchSets()) {
- if (ps.getRevision() != null) {
- revisions.add(ps.getRevision().get());
- }
- }
- return revisions;
+ private static ImmutableSet<String> getRevisions(ChangeData cd) {
+ return cd.patchSets().stream().map(ps -> ps.commitId().name()).collect(toImmutableSet());
}
/** Tracking id extracted from a footer. */
@@ -473,13 +469,12 @@ public class ChangeField {
Set<String> allApprovals = new HashSet<>();
Set<String> distinctApprovals = new HashSet<>();
for (PatchSetApproval a : cd.currentApprovals()) {
- if (a.getValue() != 0 && !a.isLegacySubmit()) {
- allApprovals.add(formatLabel(a.getLabel(), a.getValue(), a.getAccountId()));
- if (owners && cd.change().getOwner().equals(a.getAccountId())) {
- allApprovals.add(
- formatLabel(a.getLabel(), a.getValue(), ChangeQueryBuilder.OWNER_ACCOUNT_ID));
+ if (a.value() != 0 && !a.isLegacySubmit()) {
+ allApprovals.add(formatLabel(a.label(), a.value(), a.accountId()));
+ if (owners && cd.change().getOwner().equals(a.accountId())) {
+ allApprovals.add(formatLabel(a.label(), a.value(), ChangeQueryBuilder.OWNER_ACCOUNT_ID));
}
- distinctApprovals.add(formatLabel(a.getLabel(), a.getValue()));
+ distinctApprovals.add(formatLabel(a.label(), a.value()));
}
}
allApprovals.addAll(distinctApprovals);
@@ -669,8 +664,7 @@ public class ChangeField {
public static final FieldDef<ChangeData, Iterable<String>> GROUP =
exact(ChangeQueryBuilder.FIELD_GROUP)
.buildRepeatable(
- cd ->
- cd.patchSets().stream().flatMap(ps -> ps.getGroups().stream()).collect(toSet()));
+ cd -> cd.patchSets().stream().flatMap(ps -> ps.groups().stream()).collect(toSet()));
/** Serialized patch set object, used for pre-populating results. */
public static final FieldDef<ChangeData, Iterable<byte[]>> PATCH_SET =
@@ -776,7 +770,7 @@ public class ChangeField {
SubmitRecord.Label srl = new SubmitRecord.Label();
srl.label = label.label;
srl.status = label.status;
- srl.appliedBy = label.appliedBy != null ? new Account.Id(label.appliedBy) : null;
+ srl.appliedBy = label.appliedBy != null ? Account.id(label.appliedBy) : null;
rec.labels.add(srl);
}
}
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndex.java b/java/com/google/gerrit/server/index/change/ChangeIndex.java
index 855bfe9f2c..29bff0a322 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndex.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndex.java
@@ -14,12 +14,13 @@
package com.google.gerrit.server.index.change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.LegacyChangeIdPredicate;
+import com.google.gerrit.server.query.change.LegacyChangeIdStrPredicate;
public interface ChangeIndex extends Index<Change.Id, ChangeData> {
public interface Factory
@@ -27,6 +28,8 @@ public interface ChangeIndex extends Index<Change.Id, ChangeData> {
@Override
default Predicate<ChangeData> keyPredicate(Change.Id id) {
- return new LegacyChangeIdPredicate(id);
+ return getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(id)
+ : new LegacyChangeIdStrPredicate(id);
}
}
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexCollection.java b/java/com/google/gerrit/server/index/change/ChangeIndexCollection.java
index a353a2acf5..b8e2f3e3da 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexCollection.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexCollection.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.index.change;
import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.IndexCollection;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexDefinition.java b/java/com/google/gerrit/server/index/change/ChangeIndexDefinition.java
index 79454298f5..7de9e743ca 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexDefinition.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexDefinition.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.index.change;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.IndexDefinition;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java b/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
index 9e81e75dc6..63c52977a6 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
@@ -19,6 +19,8 @@ import static com.google.gerrit.server.query.change.ChangeStatusPredicate.open;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Change.Status;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.IndexRewriter;
@@ -31,8 +33,7 @@ import com.google.gerrit.index.query.NotPredicate;
import com.google.gerrit.index.query.OrPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Status;
+import com.google.gerrit.index.query.TooManyTermsInQueryException;
import com.google.gerrit.server.query.change.AndChangeSource;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
@@ -155,7 +156,7 @@ public class ChangeIndexRewriter implements IndexRewriter<ChangeData> {
MutableInteger leafTerms = new MutableInteger();
Predicate<ChangeData> out = rewriteImpl(in, index, opts, leafTerms);
- if (in == out || out instanceof IndexPredicate) {
+ if (isSameInstance(in, out) || out instanceof IndexPredicate) {
return new IndexedChangeQuery(index, out, opts);
} else if (out == null /* cannot rewrite */) {
return in;
@@ -183,7 +184,7 @@ public class ChangeIndexRewriter implements IndexRewriter<ChangeData> {
throws QueryParseException {
if (isIndexPredicate(in, index)) {
if (++leafTerms.value > config.maxTerms()) {
- throw new QueryParseException("too many terms in query");
+ throw new TooManyTermsInQueryException();
}
return in;
} else if (in instanceof LimitPredicate) {
@@ -207,7 +208,7 @@ public class ChangeIndexRewriter implements IndexRewriter<ChangeData> {
for (int i = 0; i < n; i++) {
Predicate<ChangeData> c = in.getChild(i);
Predicate<ChangeData> nc = rewriteImpl(c, index, opts, leafTerms);
- if (nc == c) {
+ if (isSameInstance(nc, c)) {
isIndexed.set(i);
newChildren.add(c);
} else if (nc == null /* cannot rewrite c */) {
@@ -291,4 +292,9 @@ public class ChangeIndexRewriter implements IndexRewriter<ChangeData> {
return p.getChildCount() > 0
&& (p instanceof AndPredicate || p instanceof OrPredicate || p instanceof NotPredicate);
}
+
+ @SuppressWarnings("ReferenceEquality")
+ private static <T> boolean isSameInstance(T a, T b) {
+ return a == b;
+ }
}
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index 4597cbdd8d..f6d86bf171 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -22,15 +22,18 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.events.ChangeIndexedListener;
import com.google.gerrit.index.Index;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.IndexExecutor;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
@@ -66,6 +69,7 @@ public class ChangeIndexer {
@Nullable private final ChangeIndexCollection indexes;
@Nullable private final ChangeIndex index;
private final ChangeData.Factory changeDataFactory;
+ private final ChangeNotes.Factory notesFactory;
private final ThreadLocalRequestContext context;
private final ListeningExecutorService batchExecutor;
private final ListeningExecutorService executor;
@@ -82,6 +86,7 @@ public class ChangeIndexer {
ChangeIndexer(
@GerritServerConfig Config cfg,
ChangeData.Factory changeDataFactory,
+ ChangeNotes.Factory notesFactory,
ThreadLocalRequestContext context,
PluginSetContext<ChangeIndexedListener> indexedListeners,
StalenessChecker stalenessChecker,
@@ -90,6 +95,7 @@ public class ChangeIndexer {
@Assisted ChangeIndex index) {
this.executor = executor;
this.changeDataFactory = changeDataFactory;
+ this.notesFactory = notesFactory;
this.context = context;
this.indexedListeners = indexedListeners;
this.stalenessChecker = stalenessChecker;
@@ -103,6 +109,7 @@ public class ChangeIndexer {
ChangeIndexer(
@GerritServerConfig Config cfg,
ChangeData.Factory changeDataFactory,
+ ChangeNotes.Factory notesFactory,
ThreadLocalRequestContext context,
PluginSetContext<ChangeIndexedListener> indexedListeners,
StalenessChecker stalenessChecker,
@@ -111,6 +118,7 @@ public class ChangeIndexer {
@Assisted ChangeIndexCollection indexes) {
this.executor = executor;
this.changeDataFactory = changeDataFactory;
+ this.notesFactory = notesFactory;
this.context = context;
this.indexedListeners = indexedListeners;
this.stalenessChecker = stalenessChecker;
@@ -133,6 +141,7 @@ public class ChangeIndexer {
public ListenableFuture<?> indexAsync(Project.NameKey project, Change.Id id) {
IndexTask task = new IndexTask(project, id);
if (queuedIndexTasks.add(task)) {
+ fireChangeScheduledForIndexingEvent(project.get(), id.get());
return submit(task);
}
return Futures.immediateFuture(null);
@@ -158,6 +167,11 @@ public class ChangeIndexer {
* @param cd change to index.
*/
public void index(ChangeData cd) {
+ fireChangeScheduledForIndexingEvent(cd.project().get(), cd.getId().get());
+ doIndex(cd);
+ }
+
+ private void doIndex(ChangeData cd) {
indexImpl(cd);
// Always double-check whether the change might be stale immediately after
@@ -186,18 +200,30 @@ public class ChangeIndexer {
for (Index<?, ChangeData> i : getWriteIndexes()) {
try (TraceTimer traceTimer =
TraceContext.newTimer(
- "Replacing change %d in index version %d",
- cd.getId().get(), i.getSchema().getVersion())) {
+ "Replacing change in index",
+ Metadata.builder()
+ .changeId(cd.getId().get())
+ .patchSetId(cd.currentPatchSet().number())
+ .indexVersion(i.getSchema().getVersion())
+ .build())) {
i.replace(cd);
}
}
fireChangeIndexedEvent(cd.project().get(), cd.getId().get());
}
+ private void fireChangeScheduledForIndexingEvent(String projectName, int id) {
+ indexedListeners.runEach(l -> l.onChangeScheduledForIndexing(projectName, id));
+ }
+
private void fireChangeIndexedEvent(String projectName, int id) {
indexedListeners.runEach(l -> l.onChangeIndexed(projectName, id));
}
+ private void fireChangeScheduledForDeletionFromIndexEvent(int id) {
+ indexedListeners.runEach(l -> l.onChangeScheduledForDeletionFromIndex(id));
+ }
+
private void fireChangeDeletedFromIndexEvent(int id) {
indexedListeners.runEach(l -> l.onChangeDeleted(id));
}
@@ -228,6 +254,7 @@ public class ChangeIndexer {
* @return future for the deleting task.
*/
public ListenableFuture<?> deleteAsync(Change.Id id) {
+ fireChangeScheduledForDeletionFromIndexEvent(id.get());
return submit(new DeleteTask(id));
}
@@ -237,6 +264,11 @@ public class ChangeIndexer {
* @param id change ID to delete.
*/
public void delete(Change.Id id) {
+ fireChangeScheduledForDeletionFromIndexEvent(id.get());
+ doDelete(id);
+ }
+
+ private void doDelete(Change.Id id) {
new DeleteTask(id).call();
}
@@ -327,8 +359,12 @@ public class ChangeIndexer {
@Override
public Void callImpl() throws Exception {
remove();
- ChangeData cd = changeDataFactory.create(project, id);
- index(cd);
+ try {
+ ChangeNotes changeNotes = notesFactory.createChecked(project, id);
+ doIndex(changeDataFactory.create(changeNotes));
+ } catch (NoSuchChangeException e) {
+ doDelete(id);
+ }
return null;
}
@@ -374,7 +410,11 @@ public class ChangeIndexer {
for (ChangeIndex i : getWriteIndexes()) {
try (TraceTimer traceTimer =
TraceContext.newTimer(
- "Deleting change %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ "Deleting change in index",
+ Metadata.builder()
+ .changeId(id.get())
+ .indexVersion(i.getSchema().getVersion())
+ .build())) {
i.delete(id);
}
}
diff --git a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
index cde6a6433d..72153c43b2 100644
--- a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
@@ -22,7 +22,7 @@ import com.google.gerrit.server.query.change.ChangeData;
public class ChangeSchemaDefinitions extends SchemaDefinitions<ChangeData> {
@Deprecated
- static final Schema<ChangeData> V39 =
+ static final Schema<ChangeData> V55 =
schema(
ChangeField.ADDED,
ChangeField.APPROVAL,
@@ -36,11 +36,16 @@ public class ChangeSchemaDefinitions extends SchemaDefinitions<ChangeData> {
ChangeField.COMMIT_MESSAGE,
ChangeField.DELETED,
ChangeField.DELTA,
+ ChangeField.DIRECTORY,
ChangeField.DRAFTBY,
ChangeField.EDITBY,
+ ChangeField.EXACT_AUTHOR,
ChangeField.EXACT_COMMIT,
+ ChangeField.EXACT_COMMITTER,
ChangeField.EXACT_TOPIC,
+ ChangeField.EXTENSION,
ChangeField.FILE_PART,
+ ChangeField.FOOTER,
ChangeField.FUZZY_TOPIC,
ChangeField.GROUP,
ChangeField.HASHTAG,
@@ -49,70 +54,49 @@ public class ChangeSchemaDefinitions extends SchemaDefinitions<ChangeData> {
ChangeField.LABEL,
ChangeField.LEGACY_ID,
ChangeField.MERGEABLE,
+ ChangeField.ONLY_EXTENSIONS,
ChangeField.OWNER,
ChangeField.PATCH_SET,
ChangeField.PATH,
+ ChangeField.PENDING_REVIEWER,
+ ChangeField.PENDING_REVIEWER_BY_EMAIL,
+ ChangeField.PRIVATE,
ChangeField.PROJECT,
ChangeField.PROJECTS,
ChangeField.REF,
ChangeField.REF_STATE,
ChangeField.REF_STATE_PATTERN,
+ ChangeField.REVERT_OF,
ChangeField.REVIEWEDBY,
ChangeField.REVIEWER,
+ ChangeField.REVIEWER_BY_EMAIL,
ChangeField.STAR,
ChangeField.STARBY,
+ ChangeField.STARTED,
ChangeField.STATUS,
ChangeField.STORED_SUBMIT_RECORD_LENIENT,
ChangeField.STORED_SUBMIT_RECORD_STRICT,
ChangeField.SUBMISSIONID,
ChangeField.SUBMIT_RECORD,
+ ChangeField.TOTAL_COMMENT_COUNT,
ChangeField.TR,
ChangeField.UNRESOLVED_COMMENT_COUNT,
- ChangeField.UPDATED);
-
- @Deprecated static final Schema<ChangeData> V40 = schema(V39, ChangeField.PRIVATE);
- @Deprecated static final Schema<ChangeData> V41 = schema(V40, ChangeField.REVIEWER_BY_EMAIL);
- @Deprecated static final Schema<ChangeData> V42 = schema(V41, ChangeField.WIP);
-
- @Deprecated
- static final Schema<ChangeData> V43 =
- schema(V42, ChangeField.EXACT_AUTHOR, ChangeField.EXACT_COMMITTER);
-
- @Deprecated
- static final Schema<ChangeData> V44 =
- schema(
- V43,
- ChangeField.STARTED,
- ChangeField.PENDING_REVIEWER,
- ChangeField.PENDING_REVIEWER_BY_EMAIL);
-
- @Deprecated static final Schema<ChangeData> V45 = schema(V44, ChangeField.REVERT_OF);
-
- @Deprecated static final Schema<ChangeData> V46 = schema(V45);
-
- // Removal of draft change workflow requires reindexing
- @Deprecated static final Schema<ChangeData> V47 = schema(V46);
-
- // Rename of star label 'mute' to 'reviewed' requires reindexing
- @Deprecated static final Schema<ChangeData> V48 = schema(V47);
-
- @Deprecated static final Schema<ChangeData> V49 = schema(V48);
-
- // Bump Lucene version requires reindexing
- @Deprecated static final Schema<ChangeData> V50 = schema(V49);
-
- @Deprecated static final Schema<ChangeData> V51 = schema(V50, ChangeField.TOTAL_COMMENT_COUNT);
-
- @Deprecated static final Schema<ChangeData> V52 = schema(V51, ChangeField.EXTENSION);
-
- @Deprecated static final Schema<ChangeData> V53 = schema(V52, ChangeField.ONLY_EXTENSIONS);
-
- @Deprecated static final Schema<ChangeData> V54 = schema(V53, ChangeField.FOOTER);
-
- @Deprecated static final Schema<ChangeData> V55 = schema(V54, ChangeField.DIRECTORY);
+ ChangeField.UPDATED,
+ ChangeField.WIP);
// The computation of the 'extension' field is changed, hence reindexing is required.
- static final Schema<ChangeData> V56 = schema(V55);
+ @Deprecated static final Schema<ChangeData> V56 = schema(V55);
+
+ // New numeric types: use dimensional points using the k-d tree geo-spatial data structure
+ // to offer fast single- and multi-dimensional numeric range. As the consequense, integer
+ // document id type is replaced with string document id type.
+ static final Schema<ChangeData> V57 =
+ new Schema.Builder<ChangeData>()
+ .add(V56)
+ .remove(ChangeField.LEGACY_ID)
+ .add(ChangeField.LEGACY_ID_STR)
+ .legacyNumericFields(false)
+ .build();
public static final String NAME = "changes";
public static final ChangeSchemaDefinitions INSTANCE = new ChangeSchemaDefinitions();
diff --git a/java/com/google/gerrit/server/index/change/DummyChangeIndex.java b/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
index 9be93f77bb..ae3b729d85 100644
--- a/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
+++ b/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.index.change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
diff --git a/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java b/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
index ed09eed2e9..57a2091390 100644
--- a/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
+++ b/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
@@ -22,6 +22,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.query.DataSource;
@@ -31,7 +32,6 @@ import com.google.gerrit.index.query.Matchable;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.ResultSet;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
import java.util.HashMap;
diff --git a/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java b/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
index fd12345e16..f6d3b6f5bf 100644
--- a/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
+++ b/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
@@ -17,37 +17,30 @@ package com.google.gerrit.server.index.change;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static com.google.gerrit.server.query.change.ChangeData.asChanges;
-import com.google.common.base.Objects;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.QueueProvider.QueueType;
import com.google.gerrit.server.index.IndexExecutor;
import com.google.gerrit.server.index.account.AccountIndexer;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.RequestContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.Collections;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import org.eclipse.jgit.lib.Config;
@@ -58,15 +51,12 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
private final Provider<InternalChangeQuery> queryProvider;
private final ChangeIndexer.Factory indexerFactory;
private final ChangeIndexCollection indexes;
- private final ChangeNotes.Factory notesFactory;
private final AllUsersName allUsersName;
private final AccountCache accountCache;
private final Provider<AccountIndexer> indexer;
private final ListeningExecutorService executor;
private final boolean enabled;
- private final Set<Index> queuedIndexTasks = Collections.newSetFromMap(new ConcurrentHashMap<>());
-
@Inject
ReindexAfterRefUpdate(
@GerritServerConfig Config cfg,
@@ -74,7 +64,6 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
Provider<InternalChangeQuery> queryProvider,
ChangeIndexer.Factory indexerFactory,
ChangeIndexCollection indexes,
- ChangeNotes.Factory notesFactory,
AllUsersName allUsersName,
AccountCache accountCache,
Provider<AccountIndexer> indexer,
@@ -83,7 +72,6 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
this.queryProvider = queryProvider;
this.indexerFactory = indexerFactory;
this.indexes = indexes;
- this.notesFactory = notesFactory;
this.allUsersName = allUsersName;
this.accountCache = accountCache;
this.indexer = indexer;
@@ -113,12 +101,9 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
@Override
public void onSuccess(List<Change> changes) {
for (Change c : changes) {
- Index task = new Index(event, c.getId());
- if (queuedIndexTasks.add(task)) {
- // Don't retry indefinitely; if this fails changes may be stale.
- @SuppressWarnings("unused")
- Future<?> possiblyIgnoredError = executor.submit(task);
- }
+ @SuppressWarnings("unused")
+ Future<?> possiblyIgnoredError =
+ indexerFactory.create(executor, indexes).indexAsync(c.getProject(), c.getId());
}
}
@@ -160,11 +145,11 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
@Override
protected List<Change> impl(RequestContext ctx) {
String ref = event.getRefName();
- Project.NameKey project = new Project.NameKey(event.getProjectName());
+ Project.NameKey project = Project.nameKey(event.getProjectName());
if (ref.equals(RefNames.REFS_CONFIG)) {
return asChanges(queryProvider.get().byProjectOpen(project));
}
- return asChanges(queryProvider.get().byBranchNew(new Branch.NameKey(project, ref)));
+ return asChanges(queryProvider.get().byBranchNew(BranchNameKey.create(project, ref)));
}
@Override
@@ -178,51 +163,4 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
@Override
protected void remove() {}
}
-
- private class Index extends Task<Void> {
- private final Change.Id id;
-
- Index(Event event, Change.Id id) {
- super(event);
- this.id = id;
- }
-
- @Override
- protected Void impl(RequestContext ctx) throws IOException {
- // Reload change, as some time may have passed since GetChanges.
- remove();
- try {
- Change c =
- notesFactory.createChecked(new Project.NameKey(event.getProjectName()), id).getChange();
- indexerFactory.create(executor, indexes).index(c);
- } catch (NoSuchChangeException e) {
- indexerFactory.create(executor, indexes).delete(id);
- }
- return null;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(Index.class, id.get());
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof Index)) {
- return false;
- }
- Index other = (Index) obj;
- return id.get() == other.id.get();
- }
-
- @Override
- public String toString() {
- return "Index change " + id.get() + " of project " + event.getProjectName();
- }
-
- @Override
- protected void remove() {
- queuedIndexTasks.remove(this);
- }
- }
}
diff --git a/java/com/google/gerrit/server/index/change/StalenessChecker.java b/java/com/google/gerrit/server/index/change/StalenessChecker.java
index 338cf3dd5f..47fd7ba58d 100644
--- a/java/com/google/gerrit/server/index/change/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/change/StalenessChecker.java
@@ -29,11 +29,11 @@ import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.RefState;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
@@ -131,8 +131,7 @@ public class StalenessChecker {
String s = new String(b, UTF_8);
List<String> parts = Splitter.on(':').splitToList(s);
RefStatePattern.check(parts.size() == 2, s);
- result.put(
- new Project.NameKey(Url.decode(parts.get(0))), RefStatePattern.create(parts.get(1)));
+ result.put(Project.nameKey(Url.decode(parts.get(0))), RefStatePattern.create(parts.get(1)));
}
return result;
}
diff --git a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
index 3474934d87..0dbbbc5e92 100644
--- a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
+++ b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
@@ -23,8 +23,8 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.SiteIndexer;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.Groups;
diff --git a/java/com/google/gerrit/server/index/group/GroupField.java b/java/com/google/gerrit/server/index/group/GroupField.java
index 29e386731e..a3d913d9b8 100644
--- a/java/com/google/gerrit/server/index/group/GroupField.java
+++ b/java/com/google/gerrit/server/index/group/GroupField.java
@@ -23,13 +23,13 @@ import static com.google.gerrit.index.FieldDef.storedOnly;
import static com.google.gerrit.index.FieldDef.timestamp;
import com.google.common.base.MoreObjects;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.SchemaUtil;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import java.sql.Timestamp;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
/** Secondary index schemas for groups. */
@@ -82,7 +82,7 @@ public class GroupField {
storedOnly("ref_state")
.build(
g -> {
- byte[] a = new byte[Constants.OBJECT_ID_STRING_LENGTH];
+ byte[] a = new byte[ObjectIds.STR_LEN];
MoreObjects.firstNonNull(g.getRefState(), ObjectId.zeroId()).copyTo(a, 0);
return a;
});
diff --git a/java/com/google/gerrit/server/index/group/GroupIndex.java b/java/com/google/gerrit/server/index/group/GroupIndex.java
index 6a430f8e77..daa213186c 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndex.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndex.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.index.group;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.query.group.GroupPredicates;
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexCollection.java b/java/com/google/gerrit/server/index/group/GroupIndexCollection.java
index 531c4463d1..9d74b7dddd 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexCollection.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexCollection.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.index.group;
import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.IndexCollection;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexDefinition.java b/java/com/google/gerrit/server/index/group/GroupIndexDefinition.java
index d117dfd5d2..e403752796 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexDefinition.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexDefinition.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.index.group;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.IndexDefinition;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexer.java b/java/com/google/gerrit/server/index/group/GroupIndexer.java
index 5d9232efa7..25d584054b 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexer.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexer.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.index.group;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
public interface GroupIndexer {
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
index 5982de781c..790066d910 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
@@ -17,12 +17,13 @@ package com.google.gerrit.server.index.group;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.events.GroupIndexedListener;
import com.google.gerrit.index.Index;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -90,13 +91,21 @@ public class GroupIndexerImpl implements GroupIndexer {
if (internalGroup.isPresent()) {
try (TraceTimer traceTimer =
TraceContext.newTimer(
- "Replacing group %s in index version %d", uuid.get(), i.getSchema().getVersion())) {
+ "Replacing group",
+ Metadata.builder()
+ .groupUuid(uuid.get())
+ .indexVersion(i.getSchema().getVersion())
+ .build())) {
i.replace(internalGroup.get());
}
} else {
try (TraceTimer traceTimer =
TraceContext.newTimer(
- "Deleting group %s in index version %d", uuid.get(), i.getSchema().getVersion())) {
+ "Deleting group",
+ Metadata.builder()
+ .groupUuid(uuid.get())
+ .indexVersion(i.getSchema().getVersion())
+ .build())) {
i.delete(uuid);
}
}
diff --git a/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java b/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java
index 6d0f3b6b5c..e64e2eb473 100644
--- a/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/group/GroupSchemaDefinitions.java
@@ -43,7 +43,11 @@ public class GroupSchemaDefinitions extends SchemaDefinitions<InternalGroup> {
@Deprecated static final Schema<InternalGroup> V6 = schema(V5);
// Lucene index was changed to add an additional field for sorting.
- static final Schema<InternalGroup> V7 = schema(V6);
+ @Deprecated static final Schema<InternalGroup> V7 = schema(V6);
+
+ // New numeric types: use dimensional points using the k-d tree geo-spatial data structure
+ // to offer fast single- and multi-dimensional numeric range.
+ static final Schema<InternalGroup> V8 = schema(V7, false);
public static final GroupSchemaDefinitions INSTANCE = new GroupSchemaDefinitions();
diff --git a/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java b/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
index 32393b0687..dfdf3ca171 100644
--- a/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
+++ b/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.index.group;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
@@ -21,7 +22,6 @@ import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.IndexedQuery;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import java.util.HashSet;
import java.util.Set;
diff --git a/java/com/google/gerrit/server/index/group/StalenessChecker.java b/java/com/google/gerrit/server/index/group/StalenessChecker.java
index 7900287f4b..3a721c3f60 100644
--- a/java/com/google/gerrit/server/index/group/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/group/StalenessChecker.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.index.group;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.query.FieldBundle;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/index/project/AllProjectsIndexer.java b/java/com/google/gerrit/server/index/project/AllProjectsIndexer.java
index 305cd25285..b760fd7d7c 100644
--- a/java/com/google/gerrit/server/index/project/AllProjectsIndexer.java
+++ b/java/com/google/gerrit/server/index/project/AllProjectsIndexer.java
@@ -21,10 +21,10 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.SiteIndexer;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectIndex;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.index.IndexExecutor;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/index/project/ProjectIndexDefinition.java b/java/com/google/gerrit/server/index/project/ProjectIndexDefinition.java
index ce2b634b79..6a844b5098 100644
--- a/java/com/google/gerrit/server/index/project/ProjectIndexDefinition.java
+++ b/java/com/google/gerrit/server/index/project/ProjectIndexDefinition.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.index.project;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.index.project.ProjectSchemaDefinitions;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
public class ProjectIndexDefinition
diff --git a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
index 6d6f78d91d..4de83be716 100644
--- a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
@@ -17,12 +17,13 @@ package com.google.gerrit.server.index.project;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.events.ProjectIndexedListener;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.index.project.ProjectIndexer;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -78,8 +79,11 @@ public class ProjectIndexerImpl implements ProjectIndexer {
for (ProjectIndex i : getWriteIndexes()) {
try (TraceTimer traceTimer =
TraceContext.newTimer(
- "Replacing project %s in index version %d",
- nameKey.get(), i.getSchema().getVersion())) {
+ "Replacing project",
+ Metadata.builder()
+ .projectName(nameKey.get())
+ .indexVersion(i.getSchema().getVersion())
+ .build())) {
i.replace(projectData);
}
}
@@ -89,8 +93,11 @@ public class ProjectIndexerImpl implements ProjectIndexer {
for (ProjectIndex i : getWriteIndexes()) {
try (TraceTimer traceTimer =
TraceContext.newTimer(
- "Deleting project %s in index version %d",
- nameKey.get(), i.getSchema().getVersion())) {
+ "Deleting project",
+ Metadata.builder()
+ .projectName(nameKey.get())
+ .indexVersion(i.getSchema().getVersion())
+ .build())) {
i.delete(nameKey);
}
}
diff --git a/java/com/google/gerrit/server/index/project/StalenessChecker.java b/java/com/google/gerrit/server/index/project/StalenessChecker.java
index dc5ebc6ea9..e4c1a7ddb2 100644
--- a/java/com/google/gerrit/server/index/project/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/project/StalenessChecker.java
@@ -17,6 +17,8 @@ package com.google.gerrit.server.index.project;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.RefState;
@@ -25,8 +27,6 @@ import com.google.gerrit.index.project.ProjectField;
import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.index.query.FieldBundle;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import java.util.Optional;
diff --git a/java/com/google/gerrit/server/ioutil/BUILD b/java/com/google/gerrit/server/ioutil/BUILD
index ed58d5bd40..fd0c4f10eb 100644
--- a/java/com/google/gerrit/server/ioutil/BUILD
+++ b/java/com/google/gerrit/server/ioutil/BUILD
@@ -5,10 +5,10 @@ java_library(
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//lib:automaton",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit.archive:jgit-archive",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
+ "//lib:jgit-archive",
],
)
diff --git a/java/com/google/gerrit/server/ioutil/BasicSerialization.java b/java/com/google/gerrit/server/ioutil/BasicSerialization.java
index c7f2ecd54e..296cf22c5f 100644
--- a/java/com/google/gerrit/server/ioutil/BasicSerialization.java
+++ b/java/com/google/gerrit/server/ioutil/BasicSerialization.java
@@ -31,7 +31,7 @@ package com.google.gerrit.server.ioutil;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.gerrit.reviewdb.client.CodedEnum;
+import com.google.gerrit.entities.CodedEnum;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
diff --git a/java/com/google/gerrit/server/logging/BUILD b/java/com/google/gerrit/server/logging/BUILD
index 672cb75ac3..7af34f7cd7 100644
--- a/java/com/google/gerrit/server/logging/BUILD
+++ b/java/com/google/gerrit/server/logging/BUILD
@@ -8,12 +8,15 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/server/util/time",
"//lib:gson",
"//lib:guava",
+ "//lib:jgit",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/flogger:api",
+ "//lib/guice",
"//lib/log:log4j",
],
)
diff --git a/java/com/google/gerrit/server/logging/CallerFinder.java b/java/com/google/gerrit/server/logging/CallerFinder.java
index c27dbbb05c..bd7e608f31 100644
--- a/java/com/google/gerrit/server/logging/CallerFinder.java
+++ b/java/com/google/gerrit/server/logging/CallerFinder.java
@@ -142,13 +142,27 @@ public abstract class CallerFinder {
/**
* The minimum number of calls known to have occurred between the first call to the target class
- * and the call of {@link #findCaller()}. If in doubt, specify zero here to avoid accidentally
+ * and the call of {@link #findCallerLazy()}. If in doubt, specify zero here to avoid accidentally
* skipping past the caller.
*
* @return the number of stack elements to skip when computing the caller
*/
public abstract int skip();
+ /**
+ * Packages that should be ignored and not be considered as caller once a target has been found.
+ *
+ * @return the ignored packages
+ */
+ public abstract ImmutableList<String> ignoredPackages();
+
+ /**
+ * Classes that should be ignored and not be considered as caller once a target has been found.
+ *
+ * @return the qualified names of the ignored classes
+ */
+ public abstract ImmutableList<String> ignoredClasses();
+
@AutoValue.Builder
public abstract static class Builder {
abstract ImmutableList.Builder<Class<?>> targetsBuilder();
@@ -164,10 +178,24 @@ public abstract class CallerFinder {
public abstract Builder skip(int skip);
+ abstract ImmutableList.Builder<String> ignoredPackagesBuilder();
+
+ public Builder addIgnoredPackage(String ignoredPackage) {
+ ignoredPackagesBuilder().add(ignoredPackage);
+ return this;
+ }
+
+ abstract ImmutableList.Builder<String> ignoredClassesBuilder();
+
+ public Builder addIgnoredClass(Class<?> ignoredClass) {
+ ignoredClassesBuilder().add(ignoredClass.getName());
+ return this;
+ }
+
public abstract CallerFinder build();
}
- public LazyArg<String> findCaller() {
+ public LazyArg<String> findCallerLazy() {
return lazy(
() ->
targets().stream()
@@ -194,7 +222,9 @@ public abstract class CallerFinder {
StackTraceElement element = stack[index];
if (isCaller(target, element.getClassName(), matchSubClasses())) {
foundCaller = true;
- } else if (foundCaller) {
+ } else if (foundCaller
+ && !ignoredPackages().contains(getPackageName(element))
+ && !ignoredClasses().contains(element.getClassName())) {
return Optional.of(element.toString());
}
}
@@ -206,6 +236,11 @@ public abstract class CallerFinder {
}
}
+ private static String getPackageName(StackTraceElement element) {
+ String className = element.getClassName();
+ return className.substring(0, className.lastIndexOf("."));
+ }
+
private boolean isCaller(Class<?> target, String className, boolean matchSubClasses)
throws ClassNotFoundException {
if (matchSubClasses) {
diff --git a/java/com/google/gerrit/server/logging/LoggingContext.java b/java/com/google/gerrit/server/logging/LoggingContext.java
index cb7d01e588..36c7e9ee76 100644
--- a/java/com/google/gerrit/server/logging/LoggingContext.java
+++ b/java/com/google/gerrit/server/logging/LoggingContext.java
@@ -14,8 +14,14 @@
package com.google.gerrit.server.logging;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.backend.Tags;
+import com.google.inject.Provider;
+import java.util.List;
import java.util.concurrent.Callable;
import java.util.logging.Level;
@@ -35,6 +41,9 @@ public class LoggingContext extends com.google.common.flogger.backend.system.Log
private static final ThreadLocal<MutableTags> tags = new ThreadLocal<>();
private static final ThreadLocal<Boolean> forceLogging = new ThreadLocal<>();
+ private static final ThreadLocal<Boolean> performanceLogging = new ThreadLocal<>();
+ private static final ThreadLocal<MutablePerformanceLogRecords> performanceLogRecords =
+ new ThreadLocal<>();
private LoggingContext() {}
@@ -47,14 +56,42 @@ public class LoggingContext extends com.google.common.flogger.backend.system.Log
if (runnable instanceof LoggingContextAwareRunnable) {
return runnable;
}
- return new LoggingContextAwareRunnable(runnable);
+
+ // Pass the MutablePerformanceLogRecords instance into the LoggingContextAwareRunnable
+ // constructor so that performance log records that are created in the wrapped runnable are
+ // added to this MutablePerformanceLogRecords instance. This is important since performance
+ // log records are processed only at the end of the request and performance log records that
+ // are created in another thread should not get lost.
+ return new LoggingContextAwareRunnable(
+ runnable, getInstance().getMutablePerformanceLogRecords());
}
public static <T> Callable<T> copy(Callable<T> callable) {
if (callable instanceof LoggingContextAwareCallable) {
return callable;
}
- return new LoggingContextAwareCallable<>(callable);
+
+ // Pass the MutablePerformanceLogRecords instance into the LoggingContextAwareCallable
+ // constructor so that performance log records that are created in the wrapped runnable are
+ // added to this MutablePerformanceLogRecords instance. This is important since performance
+ // log records are processed only at the end of the request and performance log records that
+ // are created in another thread should not get lost.
+ return new LoggingContextAwareCallable<>(
+ callable, getInstance().getMutablePerformanceLogRecords());
+ }
+
+ public boolean isEmpty() {
+ return tags.get() == null
+ && forceLogging.get() == null
+ && performanceLogging.get() == null
+ && performanceLogRecords.get() == null;
+ }
+
+ public void clear() {
+ tags.remove();
+ forceLogging.remove();
+ performanceLogging.remove();
+ performanceLogRecords.remove();
}
@Override
@@ -119,4 +156,113 @@ public class LoggingContext extends com.google.common.flogger.backend.system.Log
}
return Boolean.TRUE.equals(oldValue);
}
+
+ boolean isPerformanceLogging() {
+ Boolean isPerformanceLogging = performanceLogging.get();
+ return isPerformanceLogging != null ? isPerformanceLogging : false;
+ }
+
+ /**
+ * Enables performance logging.
+ *
+ * <p>It's important to enable performance logging only in a context that ensures to consume the
+ * captured performance log records. Otherwise captured performance log records might leak into
+ * other requests that are executed by the same thread (if a thread pool is used to process
+ * requests).
+ *
+ * @param enable whether performance logging should be enabled.
+ * @return whether performance logging was be enabled before invoking this method (old value).
+ */
+ boolean performanceLogging(boolean enable) {
+ Boolean oldValue = performanceLogging.get();
+ if (enable) {
+ performanceLogging.set(true);
+ } else {
+ performanceLogging.remove();
+ }
+ return oldValue != null ? oldValue : false;
+ }
+
+ /**
+ * Adds a performance log record, if performance logging is enabled.
+ *
+ * @param recordProvider Provider for the performance log record. This provider is only invoked if
+ * performance logging is enabled. This means if performance logging is disabled, we avoid the
+ * creation of a {@link PerformanceLogRecord}.
+ */
+ public void addPerformanceLogRecord(Provider<PerformanceLogRecord> recordProvider) {
+ if (!isPerformanceLogging()) {
+ // return early and avoid the creation of a PerformanceLogRecord
+ return;
+ }
+
+ getMutablePerformanceLogRecords().add(recordProvider.get());
+ }
+
+ ImmutableList<PerformanceLogRecord> getPerformanceLogRecords() {
+ MutablePerformanceLogRecords records = performanceLogRecords.get();
+ if (records != null) {
+ return records.list();
+ }
+ return ImmutableList.of();
+ }
+
+ void clearPerformanceLogEntries() {
+ performanceLogRecords.remove();
+ }
+
+ /**
+ * Set the performance log records in this logging context. Existing log records are overwritten.
+ *
+ * <p>This method makes a defensive copy of the passed in list.
+ *
+ * @param newPerformanceLogRecords performance log records that should be set
+ */
+ void setPerformanceLogRecords(List<PerformanceLogRecord> newPerformanceLogRecords) {
+ if (newPerformanceLogRecords.isEmpty()) {
+ performanceLogRecords.remove();
+ return;
+ }
+
+ getMutablePerformanceLogRecords().set(newPerformanceLogRecords);
+ }
+
+ /**
+ * Sets a {@link MutablePerformanceLogRecords} instance for storing performance log records.
+ *
+ * <p><strong>Attention:</strong> The passed in {@link MutablePerformanceLogRecords} instance is
+ * directly stored in the logging context.
+ *
+ * <p>This method is intended to be only used when the logging context is copied to a new thread
+ * to ensure that the performance log records that are added in the new thread are added to the
+ * same {@link MutablePerformanceLogRecords} instance (see {@link LoggingContextAwareRunnable} and
+ * {@link LoggingContextAwareCallable}). This is important since performance log records are
+ * processed only at the end of the request and performance log records that are created in
+ * another thread should not get lost.
+ *
+ * @param mutablePerformanceLogRecords the {@link MutablePerformanceLogRecords} instance in which
+ * performance log records should be stored
+ */
+ void setMutablePerformanceLogRecords(MutablePerformanceLogRecords mutablePerformanceLogRecords) {
+ performanceLogRecords.set(requireNonNull(mutablePerformanceLogRecords));
+ }
+
+ private MutablePerformanceLogRecords getMutablePerformanceLogRecords() {
+ MutablePerformanceLogRecords records = performanceLogRecords.get();
+ if (records == null) {
+ records = new MutablePerformanceLogRecords();
+ performanceLogRecords.set(records);
+ }
+ return records;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("tags", tags.get())
+ .add("forceLogging", forceLogging.get())
+ .add("performanceLogging", performanceLogging.get())
+ .add("performanceLogRecords", performanceLogRecords.get())
+ .toString();
+ }
}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java b/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
index 6aff5c4b32..d2701d7416 100644
--- a/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.logging;
import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.flogger.FluentLogger;
import java.util.concurrent.Callable;
/**
@@ -31,16 +32,30 @@ import java.util.concurrent.Callable;
* @see LoggingContextAwareRunnable
*/
class LoggingContextAwareCallable<T> implements Callable<T> {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final Callable<T> callable;
private final Thread callingThread;
private final ImmutableSetMultimap<String, String> tags;
private final boolean forceLogging;
+ private final boolean performanceLogging;
+ private final MutablePerformanceLogRecords mutablePerformanceLogRecords;
- LoggingContextAwareCallable(Callable<T> callable) {
+ /**
+ * Creates a LoggingContextAwareCallable that wraps the given {@link Callable}.
+ *
+ * @param callable Callable that should be wrapped.
+ * @param mutablePerformanceLogRecords instance of {@link MutablePerformanceLogRecords} to which
+ * performance log records that are created from the runnable are added
+ */
+ LoggingContextAwareCallable(
+ Callable<T> callable, MutablePerformanceLogRecords mutablePerformanceLogRecords) {
this.callable = callable;
this.callingThread = Thread.currentThread();
this.tags = LoggingContext.getInstance().getTagsAsMap();
this.forceLogging = LoggingContext.getInstance().isLoggingForced();
+ this.performanceLogging = LoggingContext.getInstance().isPerformanceLogging();
+ this.mutablePerformanceLogRecords = mutablePerformanceLogRecords;
}
@Override
@@ -50,17 +65,29 @@ class LoggingContextAwareCallable<T> implements Callable<T> {
return callable.call();
}
- // propagate logging context
LoggingContext loggingCtx = LoggingContext.getInstance();
- ImmutableSetMultimap<String, String> oldTags = loggingCtx.getTagsAsMap();
- boolean oldForceLogging = loggingCtx.isLoggingForced();
+
+ if (!loggingCtx.isEmpty()) {
+ logger.atWarning().log("Logging context is not empty: %s", loggingCtx);
+ }
+
+ // propagate logging context
loggingCtx.setTags(tags);
loggingCtx.forceLogging(forceLogging);
+ loggingCtx.performanceLogging(performanceLogging);
+
+ // For the performance log records use the {@link MutablePerformanceLogRecords} instance from
+ // the logging context of the calling thread in the logging context of the new thread. This way
+ // performance log records that are created from the new thread are available from the logging
+ // context of the calling thread. This is important since performance log records are processed
+ // only at the end of the request and performance log records that are created in another thread
+ // should not get lost.
+ loggingCtx.setMutablePerformanceLogRecords(mutablePerformanceLogRecords);
try {
return callable.call();
} finally {
- loggingCtx.setTags(oldTags);
- loggingCtx.forceLogging(oldForceLogging);
+ // Cleanup logging context. This is important if the thread is pooled and reused.
+ loggingCtx.clear();
}
}
}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java b/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
index 0bd7d007ad..23162b10e4 100644
--- a/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.logging;
import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.flogger.FluentLogger;
/**
* Wrapper for a {@link Runnable} that copies the {@link LoggingContext} from the current thread to
@@ -49,16 +50,30 @@ import com.google.common.collect.ImmutableSetMultimap;
* @see LoggingContextAwareCallable
*/
public class LoggingContextAwareRunnable implements Runnable {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final Runnable runnable;
private final Thread callingThread;
private final ImmutableSetMultimap<String, String> tags;
private final boolean forceLogging;
+ private final boolean performanceLogging;
+ private final MutablePerformanceLogRecords mutablePerformanceLogRecords;
- LoggingContextAwareRunnable(Runnable runnable) {
+ /**
+ * Creates a LoggingContextAwareRunnable that wraps the given {@link Runnable}.
+ *
+ * @param runnable Runnable that should be wrapped.
+ * @param mutablePerformanceLogRecords instance of {@link MutablePerformanceLogRecords} to which
+ * performance log records that are created from the runnable are added
+ */
+ LoggingContextAwareRunnable(
+ Runnable runnable, MutablePerformanceLogRecords mutablePerformanceLogRecords) {
this.runnable = runnable;
this.callingThread = Thread.currentThread();
this.tags = LoggingContext.getInstance().getTagsAsMap();
this.forceLogging = LoggingContext.getInstance().isLoggingForced();
+ this.performanceLogging = LoggingContext.getInstance().isPerformanceLogging();
+ this.mutablePerformanceLogRecords = mutablePerformanceLogRecords;
}
public Runnable unwrap() {
@@ -73,17 +88,29 @@ public class LoggingContextAwareRunnable implements Runnable {
return;
}
- // propagate logging context
LoggingContext loggingCtx = LoggingContext.getInstance();
- ImmutableSetMultimap<String, String> oldTags = loggingCtx.getTagsAsMap();
- boolean oldForceLogging = loggingCtx.isLoggingForced();
+
+ if (!loggingCtx.isEmpty()) {
+ logger.atWarning().log("Logging context is not empty: %s", loggingCtx);
+ }
+
+ // propagate logging context
loggingCtx.setTags(tags);
loggingCtx.forceLogging(forceLogging);
+ loggingCtx.performanceLogging(performanceLogging);
+
+ // For the performance log records use the {@link MutablePerformanceLogRecords} instance from
+ // the logging context of the calling thread in the logging context of the new thread. This way
+ // performance log records that are created from the new thread are available from the logging
+ // context of the calling thread. This is important since performance log records are processed
+ // only at the end of the request and performance log records that are created in another thread
+ // should not get lost.
+ loggingCtx.setMutablePerformanceLogRecords(mutablePerformanceLogRecords);
try {
runnable.run();
} finally {
- loggingCtx.setTags(oldTags);
- loggingCtx.forceLogging(oldForceLogging);
+ // Cleanup logging context. This is important if the thread is pooled and reused.
+ loggingCtx.clear();
}
}
}
diff --git a/java/com/google/gerrit/server/logging/Metadata.java b/java/com/google/gerrit/server/logging/Metadata.java
new file mode 100644
index 0000000000..7af204e769
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/Metadata.java
@@ -0,0 +1,339 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.logging;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.flogger.LazyArg;
+import com.google.common.flogger.LazyArgs;
+import com.google.gerrit.common.Nullable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Optional;
+
+/** Metadata that is provided to {@link PerformanceLogger}s as context for performance records. */
+@AutoValue
+public abstract class Metadata {
+ // The numeric ID of an account.
+ public abstract Optional<Integer> accountId();
+
+ // The type of an action (ACCOUNT_UPDATE, CHANGE_UPDATE, GROUP_UPDATE, INDEX_QUERY,
+ // PLUGIN_UPDATE).
+ public abstract Optional<String> actionType();
+
+ // An authentication domain name.
+ public abstract Optional<String> authDomainName();
+
+ // The name of a branch.
+ public abstract Optional<String> branchName();
+
+ // Key of an entity in a cache.
+ public abstract Optional<String> cacheKey();
+
+ // The name of a cache.
+ public abstract Optional<String> cacheName();
+
+ // The name of the implementation class.
+ public abstract Optional<String> className();
+
+ // The numeric ID of a change.
+ public abstract Optional<Integer> changeId();
+
+ // The type of change ID which the user used to identify a change (e.g. numeric ID, triplet etc.).
+ public abstract Optional<String> changeIdType();
+
+ // The type of an event.
+ public abstract Optional<String> eventType();
+
+ // The value of the @Export annotation which was used to register a plugin extension.
+ public abstract Optional<String> exportValue();
+
+ // Path of a file in a repository.
+ public abstract Optional<String> filePath();
+
+ // Garbage collector name.
+ public abstract Optional<String> garbageCollectorName();
+
+ // Git operation (CLONE, FETCH).
+ public abstract Optional<String> gitOperation();
+
+ // The numeric ID of an internal group.
+ public abstract Optional<Integer> groupId();
+
+ // The name of a group.
+ public abstract Optional<String> groupName();
+
+ // The UUID of a group.
+ public abstract Optional<String> groupUuid();
+
+ // HTTP status response code.
+ public abstract Optional<Integer> httpStatus();
+
+ // The name of a secondary index.
+ public abstract Optional<String> indexName();
+
+ // The version of a secondary index.
+ public abstract Optional<Integer> indexVersion();
+
+ // The name of the implementation method.
+ public abstract Optional<String> methodName();
+
+ // One or more resources
+ public abstract Optional<Boolean> multiple();
+
+ // The name of an operation that is performed.
+ public abstract Optional<String> operationName();
+
+ // Partial or full computation
+ public abstract Optional<Boolean> partial();
+
+ // Path of a metadata file in NoteDb.
+ public abstract Optional<String> noteDbFilePath();
+
+ // Name of a metadata ref in NoteDb.
+ public abstract Optional<String> noteDbRefName();
+
+ // Type of a sequence in NoteDb (ACCOUNTS, CHANGES, GROUPS).
+ public abstract Optional<String> noteDbSequenceType();
+
+ // Name of a "table" in NoteDb (if set, always CHANGES).
+ public abstract Optional<String> noteDbTable();
+
+ // The ID of a patch set.
+ public abstract Optional<Integer> patchSetId();
+
+ // Plugin metadata that doesn't fit into any other category.
+ public abstract ImmutableList<PluginMetadata> pluginMetadata();
+
+ // The name of a plugin.
+ public abstract Optional<String> pluginName();
+
+ // The name of a Gerrit project (aka Git repository).
+ public abstract Optional<String> projectName();
+
+ // The type of a Git push to Gerrit (CREATE_REPLACE, NORMAL, AUTOCLOSE).
+ public abstract Optional<String> pushType();
+
+ // The number of resources that is processed.
+ public abstract Optional<Integer> resourceCount();
+
+ // The name of a REST view.
+ public abstract Optional<String> restViewName();
+
+ // The SHA1 of Git commit.
+ public abstract Optional<String> revision();
+
+ // The username of an account.
+ public abstract Optional<String> username();
+
+ /**
+ * Returns a string representation of this instance that is suitable for logging. This is wrapped
+ * in a {@link LazyArg} because it is expensive to evaluate.
+ *
+ * <p>{@link #toString()} formats the {@link Optional} fields as {@code key=Optional[value]} or
+ * {@code key=Optional.empty}. Since this class has many optional fields from which usually only a
+ * few are populated this leads to long string representations such as
+ *
+ * <pre>
+ * Metadata{accountId=Optional.empty, actionType=Optional.empty, authDomainName=Optional.empty,
+ * branchName=Optional.empty, cacheKey=Optional.empty, cacheName=Optional.empty,
+ * className=Optional.empty, changeId=Optional[9212550], changeIdType=Optional.empty,
+ * eventType=Optional.empty, exportValue=Optional.empty, filePath=Optional.empty,
+ * garbageCollectorName=Optional.empty, gitOperation=Optional.empty, groupId=Optional.empty,
+ * groupName=Optional.empty, groupUuid=Optional.empty, httpStatus=Optional.empty,
+ * indexName=Optional.empty, indexVersion=Optional[0], methodName=Optional.empty,
+ * multiple=Optional.empty, operationName=Optional.empty, partial=Optional.empty,
+ * noteDbFilePath=Optional.empty, noteDbRefName=Optional.empty,
+ * noteDbSequenceType=Optional.empty, noteDbTable=Optional.empty, patchSetId=Optional.empty,
+ * pluginMetadata=[], pluginName=Optional.empty, projectName=Optional.empty,
+ * pushType=Optional.empty, resourceCount=Optional.empty, restViewName=Optional.empty,
+ * revision=Optional.empty, username=Optional.empty}
+ * </pre>
+ *
+ * <p>That's hard to read in logs. This is why this method
+ *
+ * <ul>
+ * <li>drops fields which have {@code Optional.empty} as value and
+ * <li>reformats values that are {@code Optional[value]} to {@code value}.
+ * </ul>
+ *
+ * <p>For the example given above the formatted string would look like this:
+ *
+ * <pre>
+ * Metadata{changeId=9212550, indexVersion=0, pluginMetadata=[]}
+ * </pre>
+ *
+ * @return string representation of this instance that is suitable for logging
+ */
+ LazyArg<String> toStringForLoggingLazy() {
+ // Don't use a lambda because different compilers generate different method names for lambdas,
+ // e.g. "lambda$myFunction$0" vs. just "lambda$0" in Eclipse. We need to identify the method
+ // by name to skip it and avoid infinite recursion.
+ return LazyArgs.lazy(this::toStringForLoggingImpl);
+ }
+
+ private String toStringForLoggingImpl() {
+ // Append class name.
+ String className = getClass().getSimpleName();
+ if (className.startsWith("AutoValue_")) {
+ className = className.substring(10);
+ }
+ ToStringHelper stringHelper = MoreObjects.toStringHelper(className);
+
+ // Append key-value pairs for field which are set.
+ Method[] methods = Metadata.class.getDeclaredMethods();
+ Arrays.sort(methods, Comparator.comparing(Method::getName));
+ for (Method method : methods) {
+ if (Modifier.isStatic(method.getModifiers())) {
+ // skip static method
+ continue;
+ }
+
+ if (method.getName().equals("toStringForLoggingLazy")
+ || method.getName().equals("toStringForLoggingImpl")) {
+ // Don't call myself in infinite recursion.
+ continue;
+ }
+
+ if (method.getReturnType().equals(Void.TYPE) || method.getParameterCount() > 0) {
+ // skip method since it's not a getter
+ continue;
+ }
+
+ method.setAccessible(true);
+
+ Object returnValue;
+ try {
+ returnValue = method.invoke(this);
+ } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
+ // should never happen
+ throw new IllegalStateException(e);
+ }
+
+ if (returnValue instanceof Optional) {
+ Optional<?> fieldValueOptional = (Optional<?>) returnValue;
+ if (!fieldValueOptional.isPresent()) {
+ // drop this key-value pair
+ continue;
+ }
+
+ // format as 'key=value' instead of 'key=Optional[value]'
+ stringHelper.add(method.getName(), fieldValueOptional.get());
+ } else {
+ // not an Optional value, keep as is
+ stringHelper.add(method.getName(), returnValue);
+ }
+ }
+
+ return stringHelper.toString();
+ }
+
+ public static Metadata.Builder builder() {
+ return new AutoValue_Metadata.Builder();
+ }
+
+ public static Metadata empty() {
+ return builder().build();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder accountId(int accountId);
+
+ public abstract Builder actionType(@Nullable String actionType);
+
+ public abstract Builder authDomainName(@Nullable String authDomainName);
+
+ public abstract Builder branchName(@Nullable String branchName);
+
+ public abstract Builder cacheKey(@Nullable String cacheKey);
+
+ public abstract Builder cacheName(@Nullable String cacheName);
+
+ public abstract Builder className(@Nullable String className);
+
+ public abstract Builder changeId(int changeId);
+
+ public abstract Builder changeIdType(@Nullable String changeIdType);
+
+ public abstract Builder eventType(@Nullable String eventType);
+
+ public abstract Builder exportValue(@Nullable String exportValue);
+
+ public abstract Builder filePath(@Nullable String filePath);
+
+ public abstract Builder garbageCollectorName(@Nullable String garbageCollectorName);
+
+ public abstract Builder gitOperation(@Nullable String gitOperation);
+
+ public abstract Builder groupId(int groupId);
+
+ public abstract Builder groupName(@Nullable String groupName);
+
+ public abstract Builder groupUuid(@Nullable String groupUuid);
+
+ public abstract Builder httpStatus(int httpStatus);
+
+ public abstract Builder indexName(@Nullable String indexName);
+
+ public abstract Builder indexVersion(int indexVersion);
+
+ public abstract Builder methodName(@Nullable String methodName);
+
+ public abstract Builder multiple(boolean multiple);
+
+ public abstract Builder operationName(String operationName);
+
+ public abstract Builder partial(boolean partial);
+
+ public abstract Builder noteDbFilePath(@Nullable String noteDbFilePath);
+
+ public abstract Builder noteDbRefName(@Nullable String noteDbRefName);
+
+ public abstract Builder noteDbSequenceType(@Nullable String noteDbSequenceType);
+
+ public abstract Builder noteDbTable(@Nullable String noteDbTable);
+
+ public abstract Builder patchSetId(int patchSetId);
+
+ abstract ImmutableList.Builder<PluginMetadata> pluginMetadataBuilder();
+
+ public Builder addPluginMetadata(PluginMetadata pluginMetadata) {
+ pluginMetadataBuilder().add(pluginMetadata);
+ return this;
+ }
+
+ public abstract Builder pluginName(@Nullable String pluginName);
+
+ public abstract Builder projectName(@Nullable String projectName);
+
+ public abstract Builder pushType(@Nullable String pushType);
+
+ public abstract Builder resourceCount(int resourceCount);
+
+ public abstract Builder restViewName(@Nullable String restViewName);
+
+ public abstract Builder revision(@Nullable String revision);
+
+ public abstract Builder username(@Nullable String username);
+
+ public abstract Metadata build();
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/MutablePerformanceLogRecords.java b/java/com/google/gerrit/server/logging/MutablePerformanceLogRecords.java
new file mode 100644
index 0000000000..4ee70d7c19
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/MutablePerformanceLogRecords.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.logging;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Thread-safe store for performance log records.
+ *
+ * <p>This class is intended to keep track of performance log records in {@link LoggingContext}. It
+ * needs to be thread-safe because it gets shared between threads when the logging context is copied
+ * to another thread (see {@link LoggingContextAwareRunnable} and {@link
+ * LoggingContextAwareCallable}. In this case the logging contexts of both threads share the same
+ * instance of this class. This is important since performance log records are processed only at the
+ * end of a request and performance log records that are created in another thread should not get
+ * lost.
+ */
+public class MutablePerformanceLogRecords {
+ private final ArrayList<PerformanceLogRecord> performanceLogRecords = new ArrayList<>();
+
+ public synchronized void add(PerformanceLogRecord record) {
+ performanceLogRecords.add(record);
+ }
+
+ public synchronized void set(List<PerformanceLogRecord> records) {
+ performanceLogRecords.clear();
+ performanceLogRecords.addAll(records);
+ }
+
+ public synchronized ImmutableList<PerformanceLogRecord> list() {
+ return ImmutableList.copyOf(performanceLogRecords);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("performanceLogRecords", performanceLogRecords)
+ .toString();
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/MutableTags.java b/java/com/google/gerrit/server/logging/MutableTags.java
index f70a8dbf1d..83009a6ae4 100644
--- a/java/com/google/gerrit/server/logging/MutableTags.java
+++ b/java/com/google/gerrit/server/logging/MutableTags.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.logging;
import static java.util.Objects.requireNonNull;
+import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
@@ -110,4 +111,10 @@ public class MutableTags {
tagMap.forEach(tagsBuilder::addTag);
tags = tagsBuilder.build();
}
+
+ @Override
+ public String toString() {
+ buildTags();
+ return MoreObjects.toStringHelper(this).add("tags", tags).toString();
+ }
}
diff --git a/java/com/google/gerrit/server/logging/PerformanceLogContext.java b/java/com/google/gerrit/server/logging/PerformanceLogContext.java
new file mode 100644
index 0000000000..b6dafdced2
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/PerformanceLogContext.java
@@ -0,0 +1,117 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.logging;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.Extension;
+import org.eclipse.jgit.lib.Config;
+
+/**
+ * Context for capturing performance log records. When the context is closed the performance log
+ * records are handed over to the registered {@link PerformanceLogger}s.
+ *
+ * <p>Capturing performance log records is disabled if there are no {@link PerformanceLogger}
+ * registered (in this case the captured performance log records would never be used).
+ *
+ * <p>It's important to enable capturing of performance log records in a context that ensures to
+ * consume the captured performance log records. Otherwise captured performance log records might
+ * leak into other requests that are executed by the same thread (if a thread pool is used to
+ * process requests).
+ */
+public class PerformanceLogContext implements AutoCloseable {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ // Do not use PluginSetContext. PluginSetContext traces the plugin latency with a timer metric
+ // which would result in a performance log and we don't want to log the performance of writing
+ // a performance log in the performance log (endless loop).
+ private final DynamicSet<PerformanceLogger> performanceLoggers;
+
+ private final boolean oldPerformanceLogging;
+ private final ImmutableList<PerformanceLogRecord> oldPerformanceLogRecords;
+
+ public PerformanceLogContext(
+ Config gerritConfig, DynamicSet<PerformanceLogger> performanceLoggers) {
+ this.performanceLoggers = performanceLoggers;
+
+ // Just in case remember the old state and reset performance log entries.
+ this.oldPerformanceLogging = LoggingContext.getInstance().isPerformanceLogging();
+ this.oldPerformanceLogRecords = LoggingContext.getInstance().getPerformanceLogRecords();
+ LoggingContext.getInstance().clearPerformanceLogEntries();
+
+ // Do not create performance log entries if performance logging is disabled or if no
+ // PerformanceLogger is registered.
+ boolean enablePerformanceLogging =
+ gerritConfig.getBoolean("tracing", "performanceLogging", true);
+ LoggingContext.getInstance()
+ .performanceLogging(
+ enablePerformanceLogging && !Iterables.isEmpty(performanceLoggers.entries()));
+ }
+
+ @Override
+ public void close() {
+ if (LoggingContext.getInstance().isPerformanceLogging()) {
+ runEach(performanceLoggers, LoggingContext.getInstance().getPerformanceLogRecords());
+ }
+
+ // Restore old state. Required to support nesting of PerformanceLogContext's.
+ LoggingContext.getInstance().performanceLogging(oldPerformanceLogging);
+ LoggingContext.getInstance().setPerformanceLogRecords(oldPerformanceLogRecords);
+ }
+
+ /**
+ * Invokes all performance loggers.
+ *
+ * <p>Similar to how {@code com.google.gerrit.server.plugincontext.PluginContext} invokes plugins
+ * but without recording metrics for invoking {@link PerformanceLogger}s.
+ *
+ * @param performanceLoggers the performance loggers that should be invoked
+ * @param performanceLogRecords the performance log records that should be handed over to the
+ * performance loggers
+ */
+ private static void runEach(
+ DynamicSet<PerformanceLogger> performanceLoggers,
+ ImmutableList<PerformanceLogRecord> performanceLogRecords) {
+ performanceLoggers
+ .entries()
+ .forEach(
+ p -> {
+ try (TraceContext traceContext = newPluginTrace(p)) {
+ performanceLogRecords.forEach(r -> r.writeTo(p.get()));
+ } catch (Throwable e) {
+ logger.atWarning().withCause(e).log(
+ "Failure in %s of plugin %s", p.get().getClass(), p.getPluginName());
+ }
+ });
+ }
+
+ /**
+ * Opens a trace context for a plugin that implements {@link PerformanceLogger}.
+ *
+ * <p>Basically the same as {@code
+ * com.google.gerrit.server.plugincontext.PluginContext#newTrace(Extension<T>)}. We have this
+ * method here to avoid a dependency on PluginContext which lives in
+ * "//java/com/google/gerrit/server". This package ("//java/com/google/gerrit/server/logging")
+ * should have as few dependencies as possible.
+ *
+ * @param extension performance logger extension
+ * @return the trace context
+ */
+ private static TraceContext newPluginTrace(Extension<PerformanceLogger> extension) {
+ return TraceContext.open().addPluginTag(extension.getPluginName());
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/PerformanceLogRecord.java b/java/com/google/gerrit/server/logging/PerformanceLogRecord.java
new file mode 100644
index 0000000000..046eeb3441
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/PerformanceLogRecord.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.logging;
+
+import com.google.auto.value.AutoValue;
+import java.util.Optional;
+
+/**
+ * The record of an operation for which the execution time was measured.
+ *
+ * <p>Metadata to provide additional context can be included by providing a {@link Metadata}
+ * instance.
+ */
+@AutoValue
+public abstract class PerformanceLogRecord {
+ /**
+ * Creates a performance log record without meta data.
+ *
+ * @param operation the name of operation the is was performed
+ * @param durationMs the execution time in milliseconds
+ * @return the performance log record
+ */
+ public static PerformanceLogRecord create(String operation, long durationMs) {
+ return new AutoValue_PerformanceLogRecord(operation, durationMs, Optional.empty());
+ }
+
+ /**
+ * Creates a performance log record with meta data.
+ *
+ * @param operation the name of operation the is was performed
+ * @param durationMs the execution time in milliseconds
+ * @param metadata metadata
+ * @return the performance log record
+ */
+ public static PerformanceLogRecord create(String operation, long durationMs, Metadata metadata) {
+ return new AutoValue_PerformanceLogRecord(operation, durationMs, Optional.of(metadata));
+ }
+
+ public abstract String operation();
+
+ public abstract long durationMs();
+
+ public abstract Optional<Metadata> metadata();
+
+ void writeTo(PerformanceLogger performanceLogger) {
+ if (metadata().isPresent()) {
+ performanceLogger.log(operation(), durationMs(), metadata().get());
+ } else {
+ performanceLogger.log(operation(), durationMs());
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/PerformanceLogger.java b/java/com/google/gerrit/server/logging/PerformanceLogger.java
new file mode 100644
index 0000000000..74a1684549
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/PerformanceLogger.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.logging;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/**
+ * Extension point for logging performance records.
+ *
+ * <p>This extension point is invoked for all operations for which the execution time is measured.
+ * The invocation of the extension point does not happen immediately, but only at the end of a
+ * request (REST call, SSH call, git push). Implementors can write the execution times into a
+ * performance log for further analysis.
+ *
+ * <p>For optimal performance implementors should overwrite the default <code>log</code> methods to
+ * avoid an unneeded instantiation of Metadata.
+ */
+@ExtensionPoint
+public interface PerformanceLogger {
+ /**
+ * Record the execution time of an operation in a performance log.
+ *
+ * @param operation operation that was performed
+ * @param durationMs time that the execution of the operation took (in milliseconds)
+ */
+ default void log(String operation, long durationMs) {
+ log(operation, durationMs, Metadata.empty());
+ }
+
+ /**
+ * Record the execution time of an operation in a performance log.
+ *
+ * @param operation operation that was performed
+ * @param durationMs time that the execution of the operation took (in milliseconds)
+ * @param metadata metadata
+ */
+ void log(String operation, long durationMs, Metadata metadata);
+}
diff --git a/java/com/google/gerrit/server/logging/PluginMetadata.java b/java/com/google/gerrit/server/logging/PluginMetadata.java
new file mode 100644
index 0000000000..21f735960a
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/PluginMetadata.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.logging;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.common.Nullable;
+import java.util.Optional;
+
+/**
+ * Key-value pair for custom metadata that is provided by plugins.
+ *
+ * <p>PluginMetadata allows plugins to include custom metadata into the {@link Metadata} instances
+ * that are provided as context for performance tracing.
+ *
+ * <p>Plugins should use PluginMetadata only for metadata kinds that are not known to Gerrit core
+ * (metadata for which {@link Metadata} doesn't have a dedicated field).
+ */
+@AutoValue
+public abstract class PluginMetadata {
+ public static PluginMetadata create(String key, @Nullable String value) {
+ return new AutoValue_PluginMetadata(key, Optional.ofNullable(value));
+ }
+
+ public abstract String key();
+
+ public abstract Optional<String> value();
+}
diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java
index 5c0406ddd1..21a4ce6f1b 100644
--- a/java/com/google/gerrit/server/logging/TraceContext.java
+++ b/java/com/google/gerrit/server/logging/TraceContext.java
@@ -19,6 +19,7 @@ import static java.util.Objects.requireNonNull;
import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Table;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
@@ -155,116 +156,66 @@ public class TraceContext implements AutoCloseable {
/**
* Opens a new timer that logs the time for an operation if request tracing is enabled.
*
- * <p>If request tracing is not enabled this is a no-op.
- *
- * @param message the message
- * @return the trace timer
- */
- public static TraceTimer newTimer(String message) {
- return new TraceTimer(message);
- }
-
- /**
- * Opens a new timer that logs the time for an operation if request tracing is enabled.
- *
- * <p>If request tracing is not enabled this is a no-op.
- *
- * @param format the message format string
- * @param arg argument for the message
- * @return the trace timer
- */
- public static TraceTimer newTimer(String format, Object arg) {
- return new TraceTimer(format, arg);
- }
-
- /**
- * Opens a new timer that logs the time for an operation if request tracing is enabled.
- *
- * <p>If request tracing is not enabled this is a no-op.
- *
- * @param format the message format string
- * @param arg1 first argument for the message
- * @param arg2 second argument for the message
+ * @param operation the name of operation the is being performed
* @return the trace timer
*/
- public static TraceTimer newTimer(String format, Object arg1, Object arg2) {
- return new TraceTimer(format, arg1, arg2);
+ public static TraceTimer newTimer(String operation) {
+ return new TraceTimer(requireNonNull(operation, "operation is required"));
}
/**
* Opens a new timer that logs the time for an operation if request tracing is enabled.
*
- * <p>If request tracing is not enabled this is a no-op.
- *
- * @param format the message format string
- * @param arg1 first argument for the message
- * @param arg2 second argument for the message
- * @param arg3 third argument for the message
+ * @param operation the name of operation the is being performed
+ * @param metadata metadata
* @return the trace timer
*/
- public static TraceTimer newTimer(String format, Object arg1, Object arg2, Object arg3) {
- return new TraceTimer(format, arg1, arg2, arg3);
- }
-
- /**
- * Opens a new timer that logs the time for an operation if request tracing is enabled.
- *
- * <p>If request tracing is not enabled this is a no-op.
- *
- * @param format the message format string
- * @param arg1 first argument for the message
- * @param arg2 second argument for the message
- * @param arg3 third argument for the message
- * @param arg4 fourth argument for the message
- * @return the trace timer
- */
- public static TraceTimer newTimer(
- String format, Object arg1, Object arg2, Object arg3, Object arg4) {
- return new TraceTimer(format, arg1, arg2, arg3, arg4);
+ public static TraceTimer newTimer(String operation, Metadata metadata) {
+ return new TraceTimer(
+ requireNonNull(operation, "operation is required"),
+ requireNonNull(metadata, "metadata is required"));
}
public static class TraceTimer implements AutoCloseable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Consumer<Long> logFn;
+ private final Consumer<Long> doneLogFn;
private final Stopwatch stopwatch;
- private TraceTimer(String message) {
- this(elapsedMs -> logger.atFine().log(message + " (%d ms)", elapsedMs));
- }
-
- private TraceTimer(String format, @Nullable Object arg) {
- this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg, elapsedMs));
- }
-
- private TraceTimer(String format, @Nullable Object arg1, @Nullable Object arg2) {
- this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, elapsedMs));
- }
-
- private TraceTimer(
- String format, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) {
- this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, arg3, elapsedMs));
+ private TraceTimer(String operation) {
+ this(
+ () -> logger.atFine().log("Starting timer for %s", operation),
+ elapsedMs -> {
+ LoggingContext.getInstance()
+ .addPerformanceLogRecord(() -> PerformanceLogRecord.create(operation, elapsedMs));
+ logger.atFine().log("%s done (%d ms)", operation, elapsedMs);
+ });
}
- private TraceTimer(
- String format,
- @Nullable Object arg1,
- @Nullable Object arg2,
- @Nullable Object arg3,
- @Nullable Object arg4) {
+ private TraceTimer(String operation, Metadata metadata) {
this(
- elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, arg3, arg4, elapsedMs));
+ () ->
+ logger.atFine().log(
+ "Starting timer for %s (%s)", operation, metadata.toStringForLoggingLazy()),
+ elapsedMs -> {
+ LoggingContext.getInstance()
+ .addPerformanceLogRecord(
+ () -> PerformanceLogRecord.create(operation, elapsedMs, metadata));
+ logger.atFine().log(
+ "%s (%s) done (%d ms)", operation, metadata.toStringForLoggingLazy(), elapsedMs);
+ });
}
- private TraceTimer(Consumer<Long> logFn) {
- this.logFn = logFn;
+ private TraceTimer(Runnable startLogFn, Consumer<Long> doneLogFn) {
+ startLogFn.run();
+ this.doneLogFn = doneLogFn;
this.stopwatch = Stopwatch.createStarted();
}
@Override
public void close() {
stopwatch.stop();
- logFn.accept(stopwatch.elapsed(TimeUnit.MILLISECONDS));
+ doneLogFn.accept(stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
@@ -286,6 +237,12 @@ public class TraceContext implements AutoCloseable {
return this;
}
+ public ImmutableMap<String, String> getTags() {
+ ImmutableMap.Builder<String, String> tagMap = ImmutableMap.builder();
+ tags.cellSet().forEach(c -> tagMap.put(c.getRowKey(), c.getColumnKey()));
+ return tagMap.build();
+ }
+
public TraceContext addPluginTag(String pluginName) {
return addTag(PLUGIN_TAG, pluginName);
}
@@ -299,6 +256,15 @@ public class TraceContext implements AutoCloseable {
return this;
}
+ public boolean isTracing() {
+ return LoggingContext.getInstance().isLoggingForced();
+ }
+
+ public Optional<String> getTraceId() {
+ return LoggingContext.getInstance().getTagsAsMap().get(RequestId.Type.TRACE_ID.name()).stream()
+ .findFirst();
+ }
+
@Override
public void close() {
for (Table.Cell<String, String, Boolean> cell : tags.cellSet()) {
diff --git a/java/com/google/gerrit/server/mail/EmailTokenVerifier.java b/java/com/google/gerrit/server/mail/EmailTokenVerifier.java
index 5b5f33d522..2ff5fc36e1 100644
--- a/java/com/google/gerrit/server/mail/EmailTokenVerifier.java
+++ b/java/com/google/gerrit/server/mail/EmailTokenVerifier.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.mail;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.mail.send.RegisterNewEmailSender;
diff --git a/java/com/google/gerrit/server/mail/MailUtil.java b/java/com/google/gerrit/server/mail/MailUtil.java
index 4afcc7b2a2..1040a55264 100644
--- a/java/com/google/gerrit/server/mail/MailUtil.java
+++ b/java/com/google/gerrit/server/mail/MailUtil.java
@@ -18,8 +18,8 @@ import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import com.google.gerrit.common.FooterConstants;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.account.AccountResolver;
import java.io.IOException;
@@ -62,7 +62,7 @@ public class MailUtil {
@SuppressWarnings("deprecation")
private static Account.Id toAccountId(AccountResolver accountResolver, String nameOrEmail)
throws UnprocessableEntityException, IOException, ConfigInvalidException {
- return accountResolver.resolveByNameOrEmail(nameOrEmail).asUnique().getAccount().getId();
+ return accountResolver.resolveByNameOrEmail(nameOrEmail).asUnique().account().id();
}
private static boolean isReviewer(FooterLine candidateFooterLine) {
diff --git a/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java b/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
index 897a5f21ac..77be665ebd 100644
--- a/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
+++ b/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.io.BaseEncoding;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.mail.send.RegisterNewEmailSender;
import com.google.inject.AbstractModule;
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index b1acab9961..158db1c4a0 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -18,7 +18,14 @@ import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.Side;
import com.google.gerrit.extensions.registration.DynamicItem;
@@ -26,23 +33,20 @@ import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.extensions.validators.CommentForValidation;
+import com.google.gerrit.extensions.validators.CommentValidationFailure;
+import com.google.gerrit.extensions.validators.CommentValidator;
import com.google.gerrit.mail.HtmlParser;
import com.google.gerrit.mail.MailComment;
import com.google.gerrit.mail.MailHeaderParser;
import com.google.gerrit.mail.MailMessage;
import com.google.gerrit.mail.MailMetadata;
import com.google.gerrit.mail.TextParser;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.PublishCommentUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.Emails;
@@ -54,6 +58,7 @@ import com.google.gerrit.server.mail.send.InboundEmailRejectionSender;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.update.BatchUpdate;
@@ -83,6 +88,15 @@ import java.util.Set;
public class MailProcessor {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final ImmutableMap<MailComment.CommentType, CommentForValidation.CommentType>
+ MAIL_COMMENT_TYPE_TO_VALIDATION_TYPE =
+ ImmutableMap.of(
+ MailComment.CommentType.CHANGE_MESSAGE,
+ CommentForValidation.CommentType.CHANGE_MESSAGE,
+ MailComment.CommentType.FILE_COMMENT, CommentForValidation.CommentType.FILE_COMMENT,
+ MailComment.CommentType.INLINE_COMMENT,
+ CommentForValidation.CommentType.INLINE_COMMENT);
+
private final Emails emails;
private final InboundEmailRejectionSender.Factory emailRejectionSender;
private final RetryHelper retryHelper;
@@ -98,6 +112,7 @@ public class MailProcessor {
private final ApprovalsUtil approvalsUtil;
private final AccountCache accountCache;
private final DynamicItem<UrlFormatter> urlFormatter;
+ private final PluginSetContext<CommentValidator> commentValidators;
@Inject
public MailProcessor(
@@ -115,7 +130,8 @@ public class MailProcessor {
ApprovalsUtil approvalsUtil,
CommentAdded commentAdded,
AccountCache accountCache,
- DynamicItem<UrlFormatter> urlFormatter) {
+ DynamicItem<UrlFormatter> urlFormatter,
+ PluginSetContext<CommentValidator> commentValidators) {
this.emails = emails;
this.emailRejectionSender = emailRejectionSender;
this.retryHelper = retryHelper;
@@ -131,6 +147,7 @@ public class MailProcessor {
this.approvalsUtil = approvalsUtil;
this.accountCache = accountCache;
this.urlFormatter = urlFormatter;
+ this.commentValidators = commentValidators;
}
/**
@@ -187,7 +204,7 @@ public class MailProcessor {
logger.atWarning().log("Mail: Account %s doesn't exist. Will delete message.", accountId);
return;
}
- if (!accountState.get().getAccount().isActive()) {
+ if (!accountState.get().account().isActive()) {
logger.atWarning().log("Mail: Account %s is inactive. Will delete message.", accountId);
sendRejectionEmail(message, InboundEmailRejectionSender.Error.INACTIVE_ACCOUNT);
return;
@@ -211,7 +228,7 @@ public class MailProcessor {
throws UpdateException, RestApiException {
try (ManualRequestContext ctx = oneOffRequestContext.openAs(sender)) {
List<ChangeData> changeDataList =
- queryProvider.get().byLegacyChangeId(new Change.Id(metadata.changeNumber));
+ queryProvider.get().byLegacyChangeId(Change.id(metadata.changeNumber));
if (changeDataList.size() != 1) {
logger.atSevere().log(
"Message %s references unique change %s,"
@@ -259,7 +276,22 @@ public class MailProcessor {
return;
}
- Op o = new Op(new PatchSet.Id(cd.getId(), metadata.patchSet), parsedComments, message.id());
+ ImmutableList<CommentForValidation> parsedCommentsForValidation =
+ parsedComments.stream()
+ .map(
+ comment ->
+ CommentForValidation.create(
+ MAIL_COMMENT_TYPE_TO_VALIDATION_TYPE.get(comment.getType()),
+ comment.getMessage()))
+ .collect(ImmutableList.toImmutableList());
+ ImmutableList<CommentValidationFailure> commentValidationFailures =
+ PublishCommentUtil.findInvalidComments(commentValidators, parsedCommentsForValidation);
+ if (!commentValidationFailures.isEmpty()) {
+ sendRejectionEmail(message, InboundEmailRejectionSender.Error.COMMENT_REJECTED);
+ return;
+ }
+
+ Op o = new Op(PatchSet.id(cd.getId(), metadata.patchSet), parsedComments, message.id());
BatchUpdate batchUpdate = buf.create(project, ctx.getUser(), TimeUtil.nowTs());
batchUpdate.addOp(cd.getId(), o);
batchUpdate.execute();
@@ -302,7 +334,7 @@ public class MailProcessor {
persistentCommentFromMailComment(ctx, c, targetPatchSetForComment(ctx, c, patchSet)));
}
commentsUtil.putComments(
- ctx.getUpdate(ctx.getChange().currentPatchSetId()), Status.PUBLISHED, comments);
+ ctx.getUpdate(ctx.getChange().currentPatchSetId()), Comment.Status.PUBLISHED, comments);
return true;
}
@@ -330,7 +362,7 @@ public class MailProcessor {
approvalsUtil
.byPatchSetUser(
notes, psId, ctx.getAccountId(), ctx.getRevWalk(), ctx.getRepoView().getConfig())
- .forEach(a -> approvals.put(a.getLabel(), a.getValue()));
+ .forEach(a -> approvals.put(a.label(), a.value()));
// Fire Gerrit event. Note that approvals can't be granted via email, so old and new approvals
// are always the same here.
commentAdded.fire(
@@ -362,7 +394,7 @@ public class MailProcessor {
if (mailComment.getInReplyTo() != null) {
return psUtil.get(
ctx.getNotes(),
- new PatchSet.Id(ctx.getChange().getId(), mailComment.getInReplyTo().key.patchSetId));
+ PatchSet.id(ctx.getChange().getId(), mailComment.getInReplyTo().key.patchSetId));
}
return current;
}
@@ -386,7 +418,7 @@ public class MailProcessor {
commentsUtil.newComment(
ctx,
fileName,
- patchSetForComment.getId(),
+ patchSetForComment.id(),
(short) side.ordinal(),
mailComment.getMessage(),
false,
@@ -399,7 +431,7 @@ public class MailProcessor {
comment.range = mailComment.getInReplyTo().range;
comment.unresolved = mailComment.getInReplyTo().unresolved;
}
- CommentsUtil.setCommentRevId(comment, patchListCache, ctx.getChange(), patchSetForComment);
+ CommentsUtil.setCommentCommitId(comment, patchListCache, ctx.getChange(), patchSetForComment);
return comment;
}
}
diff --git a/java/com/google/gerrit/server/mail/send/AbandonedSender.java b/java/com/google/gerrit/server/mail/send/AbandonedSender.java
index 1017965d2e..a6fb4de07a 100644
--- a/java/com/google/gerrit/server/mail/send/AbandonedSender.java
+++ b/java/com/google/gerrit/server/mail/send/AbandonedSender.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -25,13 +25,13 @@ import com.google.inject.assistedinject.Assisted;
public class AbandonedSender extends ReplyToChangeSender {
public interface Factory extends ReplyToChangeSender.Factory<AbandonedSender> {
@Override
- AbandonedSender create(Project.NameKey project, Change.Id change);
+ AbandonedSender create(Project.NameKey project, Change.Id changeId);
}
@Inject
public AbandonedSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
- super(ea, "abandon", ChangeEmail.newChangeData(ea, project, id));
+ EmailArguments args, @Assisted Project.NameKey project, @Assisted Change.Id changeId) {
+ super(args, "abandon", ChangeEmail.newChangeData(args, project, changeId));
}
@Override
diff --git a/java/com/google/gerrit/server/mail/send/AddKeySender.java b/java/com/google/gerrit/server/mail/send/AddKeySender.java
index 6a4918cf36..8b3d3f7348 100644
--- a/java/com/google/gerrit/server/mail/send/AddKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/AddKeySender.java
@@ -37,8 +37,8 @@ public class AddKeySender extends OutgoingEmail {
@AssistedInject
public AddKeySender(
- EmailArguments ea, @Assisted IdentifiedUser user, @Assisted AccountSshKey sshKey) {
- super(ea, "addkey");
+ EmailArguments args, @Assisted IdentifiedUser user, @Assisted AccountSshKey sshKey) {
+ super(args, "addkey");
this.user = user;
this.sshKey = sshKey;
this.gpgKeys = null;
@@ -46,8 +46,8 @@ public class AddKeySender extends OutgoingEmail {
@AssistedInject
public AddKeySender(
- EmailArguments ea, @Assisted IdentifiedUser user, @Assisted List<String> gpgKeys) {
- super(ea, "addkey");
+ EmailArguments args, @Assisted IdentifiedUser user, @Assisted List<String> gpgKeys) {
+ super(args, "addkey");
this.user = user;
this.sshKey = null;
this.gpgKeys = gpgKeys;
@@ -79,7 +79,7 @@ public class AddKeySender extends OutgoingEmail {
}
public String getEmail() {
- return user.getAccount().getPreferredEmail();
+ return user.getAccount().preferredEmail();
}
public String getUserNameEmail() {
diff --git a/java/com/google/gerrit/server/mail/send/AddReviewerSender.java b/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
index 22abd9c38e..96d9483c38 100644
--- a/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
+++ b/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
@@ -14,22 +14,22 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
/** Asks a user to review a change. */
public class AddReviewerSender extends NewChangeSender {
public interface Factory {
- AddReviewerSender create(Project.NameKey project, Change.Id id);
+ AddReviewerSender create(Project.NameKey project, Change.Id changeId);
}
@Inject
public AddReviewerSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
- super(ea, newChangeData(ea, project, id));
+ EmailArguments args, @Assisted Project.NameKey project, @Assisted Change.Id changeId) {
+ super(args, newChangeData(args, project, changeId));
}
@Override
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 55e2fd483d..19c1fa28d5 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -20,18 +20,18 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetInfo;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.mail.MailHeader;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
@@ -84,11 +84,11 @@ public abstract class ChangeEmail extends NotificationEmail {
protected Set<Account.Id> authors;
protected boolean emailOnlyAuthors;
- protected ChangeEmail(EmailArguments ea, String mc, ChangeData cd) {
- super(ea, mc, cd.change().getDest());
- changeData = cd;
- change = cd.change();
- emailOnlyAuthors = false;
+ protected ChangeEmail(EmailArguments args, String messageClass, ChangeData changeData) {
+ super(args, messageClass, changeData.change().getDest());
+ this.changeData = changeData;
+ this.change = changeData.change();
+ this.emailOnlyAuthors = false;
}
@Override
@@ -156,10 +156,10 @@ public abstract class ChangeEmail extends NotificationEmail {
}
if (patchSet != null) {
- setHeader(MailHeader.PATCH_SET.fieldName(), patchSet.getPatchSetId() + "");
+ setHeader(MailHeader.PATCH_SET.fieldName(), patchSet.number() + "");
if (patchSetInfo == null) {
try {
- patchSetInfo = args.patchSetInfoFactory.get(changeData.notes(), patchSet.getId());
+ patchSetInfo = args.patchSetInfoFactory.get(changeData.notes(), patchSet.id());
} catch (PatchSetInfoNotAvailableException | StorageException err) {
patchSetInfo = null;
}
@@ -205,11 +205,8 @@ public abstract class ChangeEmail extends NotificationEmail {
}
private void setCommitIdHeader() {
- if (patchSet != null
- && patchSet.getRevision() != null
- && patchSet.getRevision().get() != null
- && patchSet.getRevision().get().length() > 0) {
- setHeader(MailHeader.COMMIT.fieldName(), patchSet.getRevision().get());
+ if (patchSet != null) {
+ setHeader(MailHeader.COMMIT.fieldName(), patchSet.commitId().name());
}
}
@@ -290,11 +287,11 @@ public abstract class ChangeEmail extends NotificationEmail {
/** Get the patch list corresponding to patch set patchSetId of this change. */
protected PatchList getPatchList(int patchSetId) throws PatchListNotAvailableException {
PatchSet ps;
- if (patchSetId == patchSet.getPatchSetId()) {
+ if (patchSetId == patchSet.number()) {
ps = patchSet;
} else {
try {
- ps = args.patchSetUtil.get(changeData.notes(), new PatchSet.Id(change.getId(), patchSetId));
+ ps = args.patchSetUtil.get(changeData.notes(), PatchSet.id(change.getId(), patchSetId));
} catch (StorageException e) {
throw new PatchListNotAvailableException("Failed to get patchSet", e);
}
@@ -338,7 +335,7 @@ public abstract class ChangeEmail extends NotificationEmail {
protected void removeUsersThatIgnoredTheChange() {
for (Map.Entry<Account.Id, Collection<String>> e : stars.asMap().entrySet()) {
if (e.getValue().contains(StarredChangesUtil.IGNORE_LABEL)) {
- args.accountCache.get(e.getKey()).ifPresent(a -> removeUser(a.getAccount()));
+ args.accountCache.get(e.getKey()).ifPresent(a -> removeUser(a.account()));
}
}
}
@@ -349,7 +346,7 @@ public abstract class ChangeEmail extends NotificationEmail {
return new Watchers();
}
- ProjectWatch watch = new ProjectWatch(args, branch.getParentKey(), projectState, changeData);
+ ProjectWatch watch = new ProjectWatch(args, branch.project(), projectState, changeData);
return watch.getWatchers(type, includeWatchersFromNotifyConfig);
}
@@ -415,7 +412,7 @@ public abstract class ChangeEmail extends NotificationEmail {
case ALL:
default:
if (patchSet != null) {
- authors.add(patchSet.getUploader());
+ authors.add(patchSet.uploader());
}
if (patchSetInfo != null) {
if (patchSetInfo.getAuthor().getAccount() != null) {
@@ -465,8 +462,8 @@ public abstract class ChangeEmail extends NotificationEmail {
soyContext.put("change", changeData);
Map<String, Object> patchSetData = new HashMap<>();
- patchSetData.put("patchSetId", patchSet.getPatchSetId());
- patchSetData.put("refName", patchSet.getRefName());
+ patchSetData.put("patchSetId", patchSet.number());
+ patchSetData.put("refName", patchSet.refName());
soyContext.put("patchSet", patchSetData);
Map<String, Object> patchSetInfoData = new HashMap<>();
@@ -476,7 +473,7 @@ public abstract class ChangeEmail extends NotificationEmail {
footers.add(MailHeader.CHANGE_ID.withDelimiter() + change.getKey().get());
footers.add(MailHeader.CHANGE_NUMBER.withDelimiter() + Integer.toString(change.getChangeId()));
- footers.add(MailHeader.PATCH_SET.withDelimiter() + patchSet.getPatchSetId());
+ footers.add(MailHeader.PATCH_SET.withDelimiter() + patchSet.number());
footers.add(MailHeader.OWNER.withDelimiter() + getNameEmailFor(change.getOwner()));
if (change.getAssignee() != null) {
footers.add(MailHeader.ASSIGNEE.withDelimiter() + getNameEmailFor(change.getAssignee()));
diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java
index 7b11ce65a0..48d342e55b 100644
--- a/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -17,21 +17,21 @@ package com.google.gerrit.server.mail.send;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
-import com.google.common.collect.Ordering;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.FilenameComparator;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.KeyUtil;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.exceptions.NoSuchEntityException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.mail.MailHeader;
import com.google.gerrit.mail.MailProcessingUtil;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -41,7 +41,6 @@ import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.util.LabelVote;
-import com.google.gwtorm.client.KeyUtil;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -55,7 +54,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import org.apache.james.mime4j.dom.field.FieldName;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
@@ -65,7 +63,7 @@ public class CommentSender extends ReplyToChangeSender {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public interface Factory {
- CommentSender create(Project.NameKey project, Change.Id id);
+ CommentSender create(Project.NameKey project, Change.Id changeId);
}
private class FileCommentGroup {
@@ -113,12 +111,12 @@ public class CommentSender extends ReplyToChangeSender {
@Inject
public CommentSender(
- EmailArguments ea,
+ EmailArguments args,
CommentsUtil commentsUtil,
@GerritServerConfig Config cfg,
@Assisted Project.NameKey project,
- @Assisted Change.Id id) {
- super(ea, "comment", newChangeData(ea, project, id));
+ @Assisted Change.Id changeId) {
+ super(args, "comment", newChangeData(args, project, changeId));
this.commentsUtil = commentsUtil;
this.incomingEmailEnabled =
cfg.getEnum("receiveemail", null, "protocol", Protocol.NONE).ordinal()
@@ -128,14 +126,6 @@ public class CommentSender extends ReplyToChangeSender {
public void setComments(List<Comment> comments) {
inlineComments = comments;
-
- Set<String> paths = new HashSet<>();
- for (Comment c : comments) {
- if (!Patch.isMagic(c.key.filename)) {
- paths.add(c.key.filename);
- }
- }
- changeData.setCurrentFilePaths(Ordering.natural().sortedCopy(paths));
}
public void setPatchSetComment(String comment) {
diff --git a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
index 9895e07069..1f58abb2ce 100644
--- a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.mail.send;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -34,18 +34,18 @@ public class CreateChangeSender extends NewChangeSender {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public interface Factory {
- CreateChangeSender create(Project.NameKey project, Change.Id id);
+ CreateChangeSender create(Project.NameKey project, Change.Id changeId);
}
private final PermissionBackend permissionBackend;
@Inject
public CreateChangeSender(
- EmailArguments ea,
+ EmailArguments args,
PermissionBackend permissionBackend,
@Assisted Project.NameKey project,
- @Assisted Change.Id id) {
- super(ea, newChangeData(ea, project, id));
+ @Assisted Change.Id changeId) {
+ super(args, newChangeData(args, project, changeId));
this.permissionBackend = permissionBackend;
}
diff --git a/java/com/google/gerrit/server/mail/send/DeleteKeySender.java b/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
index 39e21a52a2..c9bb1e4eeb 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
@@ -38,8 +38,8 @@ public class DeleteKeySender extends OutgoingEmail {
@AssistedInject
public DeleteKeySender(
- EmailArguments ea, @Assisted IdentifiedUser user, @Assisted AccountSshKey sshKey) {
- super(ea, "deletekey");
+ EmailArguments args, @Assisted IdentifiedUser user, @Assisted AccountSshKey sshKey) {
+ super(args, "deletekey");
this.user = user;
this.gpgKeyFingerprints = Collections.emptyList();
this.sshKey = sshKey;
@@ -47,8 +47,10 @@ public class DeleteKeySender extends OutgoingEmail {
@AssistedInject
public DeleteKeySender(
- EmailArguments ea, @Assisted IdentifiedUser user, @Assisted List<String> gpgKeyFingerprints) {
- super(ea, "deletekey");
+ EmailArguments args,
+ @Assisted IdentifiedUser user,
+ @Assisted List<String> gpgKeyFingerprints) {
+ super(args, "deletekey");
this.user = user;
this.gpgKeyFingerprints = gpgKeyFingerprints;
this.sshKey = null;
@@ -75,7 +77,7 @@ public class DeleteKeySender extends OutgoingEmail {
}
public String getEmail() {
- return user.getAccount().getPreferredEmail();
+ return user.getAccount().preferredEmail();
}
public String getUserNameEmail() {
diff --git a/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java b/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
index f941acc582..4f42679255 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -36,13 +36,13 @@ public class DeleteReviewerSender extends ReplyToChangeSender {
public interface Factory extends ReplyToChangeSender.Factory<DeleteReviewerSender> {
@Override
- DeleteReviewerSender create(Project.NameKey project, Change.Id change);
+ DeleteReviewerSender create(Project.NameKey project, Change.Id changeId);
}
@Inject
public DeleteReviewerSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
- super(ea, "deleteReviewer", newChangeData(ea, project, id));
+ EmailArguments args, @Assisted Project.NameKey project, @Assisted Change.Id changeId) {
+ super(args, "deleteReviewer", newChangeData(args, project, changeId));
}
public void addReviewers(Collection<Account.Id> cc) {
diff --git a/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java b/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
index 195d53df1e..76f9b813d9 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -25,13 +25,13 @@ import com.google.inject.assistedinject.Assisted;
public class DeleteVoteSender extends ReplyToChangeSender {
public interface Factory extends ReplyToChangeSender.Factory<DeleteVoteSender> {
@Override
- DeleteVoteSender create(Project.NameKey project, Change.Id change);
+ DeleteVoteSender create(Project.NameKey project, Change.Id changeId);
}
@Inject
protected DeleteVoteSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
- super(ea, "deleteVote", newChangeData(ea, project, id));
+ EmailArguments args, @Assisted Project.NameKey project, @Assisted Change.Id changeId) {
+ super(args, "deleteVote", newChangeData(args, project, changeId));
}
@Override
diff --git a/java/com/google/gerrit/server/mail/send/EmailArguments.java b/java/com/google/gerrit/server/mail/send/EmailArguments.java
index fe2f74b2da..ede5765993 100644
--- a/java/com/google/gerrit/server/mail/send/EmailArguments.java
+++ b/java/com/google/gerrit/server/mail/send/EmailArguments.java
@@ -45,7 +45,7 @@ import com.google.gerrit.server.ssh.SshAdvertisedAddresses;
import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import com.google.template.soy.tofu.SoyTofu;
+import com.google.template.soy.jbcsrc.api.SoySauce;
import java.util.List;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
@@ -75,7 +75,7 @@ public class EmailArguments {
final ChangeQueryBuilder queryBuilder;
final ChangeData.Factory changeDataFactory;
- final SoyTofu soyTofu;
+ final SoySauce soySauce;
final EmailSettings settings;
final DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners;
final Provider<InternalAccountQuery> accountQueryProvider;
@@ -105,7 +105,7 @@ public class EmailArguments {
AllProjectsName allProjectsName,
ChangeQueryBuilder queryBuilder,
ChangeData.Factory changeDataFactory,
- @MailTemplates SoyTofu soyTofu,
+ @MailTemplates SoySauce soySauce,
EmailSettings settings,
@SshAdvertisedAddresses List<String> sshAddresses,
SitePaths site,
@@ -134,7 +134,7 @@ public class EmailArguments {
this.allProjectsName = allProjectsName;
this.queryBuilder = queryBuilder;
this.changeDataFactory = changeDataFactory;
- this.soyTofu = soyTofu;
+ this.soySauce = soySauce;
this.settings = settings;
this.sshAddresses = sshAddresses;
this.site = site;
diff --git a/java/com/google/gerrit/server/mail/send/FromAddressGenerator.java b/java/com/google/gerrit/server/mail/send/FromAddressGenerator.java
index 5baabe95c5..61fa50d268 100644
--- a/java/com/google/gerrit/server/mail/send/FromAddressGenerator.java
+++ b/java/com/google/gerrit/server/mail/send/FromAddressGenerator.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
/** Constructs an address to send email from. */
public interface FromAddressGenerator {
diff --git a/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java b/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
index b77909e2f3..1bf1ff1f28 100644
--- a/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
+++ b/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.mail.send;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.common.data.ParameterizedString;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
@@ -123,9 +123,9 @@ public class FromAddressGeneratorProvider implements Provider<FromAddressGenerat
public Address from(Account.Id fromId) {
String senderName;
if (fromId != null) {
- Optional<Account> a = accountCache.get(fromId).map(AccountState::getAccount);
- String fullName = a.map(Account::getFullName).orElse(null);
- String userEmail = a.map(Account::getPreferredEmail).orElse(null);
+ Optional<Account> a = accountCache.get(fromId).map(AccountState::account);
+ String fullName = a.map(Account::fullName).orElse(null);
+ String userEmail = a.map(Account::preferredEmail).orElse(null);
if (canRelay(userEmail)) {
return new Address(fullName, userEmail);
}
@@ -208,8 +208,7 @@ public class FromAddressGeneratorProvider implements Provider<FromAddressGenerat
final String senderName;
if (fromId != null) {
- String fullName =
- accountCache.get(fromId).map(a -> a.getAccount().getFullName()).orElse(null);
+ String fullName = accountCache.get(fromId).map(a -> a.account().fullName()).orElse(null);
if (fullName == null || "".equals(fullName)) {
fullName = anonymousCowardName;
}
@@ -235,7 +234,7 @@ public class FromAddressGeneratorProvider implements Provider<FromAddressGenerat
byte[] bytes = hash.digest(data.getBytes(UTF_8));
return Base64.encodeBase64URLSafeString(bytes);
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("No MD5 available", e);
+ throw new IllegalStateException("No MD5 available", e);
}
}
}
diff --git a/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java b/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
index ca332ff275..2db2d6d21e 100644
--- a/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
+++ b/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
@@ -31,8 +31,8 @@ public class HttpPasswordUpdateSender extends OutgoingEmail {
@AssistedInject
public HttpPasswordUpdateSender(
- EmailArguments ea, @Assisted IdentifiedUser user, @Assisted String operation) {
- super(ea, "HttpPasswordUpdate");
+ EmailArguments args, @Assisted IdentifiedUser user, @Assisted String operation) {
+ super(args, "HttpPasswordUpdate");
this.user = user;
this.operation = operation;
}
@@ -59,7 +59,7 @@ public class HttpPasswordUpdateSender extends OutgoingEmail {
}
public String getEmail() {
- return user.getAccount().getPreferredEmail();
+ return user.getAccount().preferredEmail();
}
public String getUserNameEmail() {
diff --git a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
index b5d384d730..110f26ac22 100644
--- a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
+++ b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
@@ -32,7 +32,8 @@ public class InboundEmailRejectionSender extends OutgoingEmail {
PARSING_ERROR,
INACTIVE_ACCOUNT,
UNKNOWN_ACCOUNT,
- INTERNAL_EXCEPTION;
+ INTERNAL_EXCEPTION,
+ COMMENT_REJECTED
}
public interface Factory {
@@ -45,8 +46,11 @@ public class InboundEmailRejectionSender extends OutgoingEmail {
@Inject
public InboundEmailRejectionSender(
- EmailArguments ea, @Assisted Address to, @Assisted String threadId, @Assisted Error reason) {
- super(ea, "error");
+ EmailArguments args,
+ @Assisted Address to,
+ @Assisted String threadId,
+ @Assisted Error reason) {
+ super(args, "error");
this.to = requireNonNull(to);
this.threadId = requireNonNull(threadId);
this.reason = requireNonNull(reason);
diff --git a/java/com/google/gerrit/server/mail/send/MailSoyTofuProvider.java b/java/com/google/gerrit/server/mail/send/MailSoySauceProvider.java
index 3bb44c701a..92220eb890 100644
--- a/java/com/google/gerrit/server/mail/send/MailSoyTofuProvider.java
+++ b/java/com/google/gerrit/server/mail/send/MailSoySauceProvider.java
@@ -17,22 +17,23 @@ package com.google.gerrit.server.mail.send;
import com.google.common.io.CharStreams;
import com.google.common.io.Resources;
import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
import com.google.template.soy.SoyFileSet;
+import com.google.template.soy.jbcsrc.api.SoySauce;
import com.google.template.soy.shared.SoyAstCache;
-import com.google.template.soy.tofu.SoyTofu;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-/** Configures Soy Tofu object for rendering email templates. */
+/** Configures Soy Sauce object for rendering email templates. */
@Singleton
-public class MailSoyTofuProvider implements Provider<SoyTofu> {
+public class MailSoySauceProvider implements Provider<SoySauce> {
// Note: will fail to construct the tofu object if this array is empty.
private static final String[] TEMPLATES = {
@@ -80,24 +81,32 @@ public class MailSoyTofuProvider implements Provider<SoyTofu> {
private final SitePaths site;
private final SoyAstCache cache;
+ private final PluginSetContext<MailSoyTemplateProvider> templateProviders;
@Inject
- MailSoyTofuProvider(SitePaths site, SoyAstCache cache) {
+ MailSoySauceProvider(
+ SitePaths site,
+ SoyAstCache cache,
+ PluginSetContext<MailSoyTemplateProvider> templateProviders) {
this.site = site;
this.cache = cache;
+ this.templateProviders = templateProviders;
}
@Override
- public SoyTofu get() throws ProvisionException {
+ public SoySauce get() throws ProvisionException {
SoyFileSet.Builder builder = SoyFileSet.builder();
builder.setSoyAstCache(cache);
for (String name : TEMPLATES) {
- addTemplate(builder, name);
+ addTemplate(builder, "com/google/gerrit/server/mail/", name);
}
- return builder.build().compileToTofu();
+ templateProviders.runEach(
+ e -> e.getFileNames().forEach(p -> addTemplate(builder, e.getPath(), p)));
+ return builder.build().compileTemplates();
}
- private void addTemplate(SoyFileSet.Builder builder, String name) throws ProvisionException {
+ private void addTemplate(SoyFileSet.Builder builder, String resourcePath, String name)
+ throws ProvisionException {
// Load as a file in the mail templates directory if present.
Path tmpl = site.mail_dir.resolve(name);
if (Files.isRegularFile(tmpl)) {
@@ -115,7 +124,9 @@ public class MailSoyTofuProvider implements Provider<SoyTofu> {
}
// Otherwise load the template as a resource.
- String resourcePath = "com/google/gerrit/server/mail/" + name;
- builder.add(Resources.getResource(resourcePath));
+ if (!resourcePath.endsWith("/")) {
+ resourcePath += "/";
+ }
+ builder.add(Resources.getResource(resourcePath + name));
}
}
diff --git a/java/com/google/gerrit/server/mail/send/MailSoyTemplateProvider.java b/java/com/google/gerrit/server/mail/send/MailSoyTemplateProvider.java
new file mode 100644
index 0000000000..3a6ff64c37
--- /dev/null
+++ b/java/com/google/gerrit/server/mail/send/MailSoyTemplateProvider.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.mail.send;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import java.util.Set;
+
+/**
+ * Extension point to provide soy templates that should be registered so that they can be used for
+ * sending emails from a plugin.
+ */
+@ExtensionPoint
+public interface MailSoyTemplateProvider {
+ /**
+ * Return the name of the resource path that contains the soy template files that are returned by
+ * {@link #getFileNames()}.
+ *
+ * @return resource path of the templates
+ */
+ String getPath();
+
+ /**
+ * Return the names of the soy template files.
+ *
+ * <p>These files are expected to exist in the resource path that is returned by {@link
+ * #getPath()}.
+ *
+ * @return names of the template files, including the {@code .soy} file extension
+ */
+ Set<String> getFileNames();
+}
diff --git a/java/com/google/gerrit/server/mail/send/MergedSender.java b/java/com/google/gerrit/server/mail/send/MergedSender.java
index cbc4117e26..b28a4dc9e4 100644
--- a/java/com/google/gerrit/server/mail/send/MergedSender.java
+++ b/java/com/google/gerrit/server/mail/send/MergedSender.java
@@ -19,12 +19,12 @@ import com.google.common.collect.Table;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -32,15 +32,15 @@ import com.google.inject.assistedinject.Assisted;
/** Send notice about a change successfully merged. */
public class MergedSender extends ReplyToChangeSender {
public interface Factory {
- MergedSender create(Project.NameKey project, Change.Id id);
+ MergedSender create(Project.NameKey project, Change.Id changeId);
}
private final LabelTypes labelTypes;
@Inject
public MergedSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
- super(ea, "merged", newChangeData(ea, project, id));
+ EmailArguments args, @Assisted Project.NameKey project, @Assisted Change.Id changeId) {
+ super(args, "merged", newChangeData(args, project, changeId));
labelTypes = changeData.getLabelTypes();
}
@@ -69,15 +69,15 @@ public class MergedSender extends ReplyToChangeSender {
Table<Account.Id, String, PatchSetApproval> pos = HashBasedTable.create();
Table<Account.Id, String, PatchSetApproval> neg = HashBasedTable.create();
for (PatchSetApproval ca :
- args.approvalsUtil.byPatchSet(changeData.notes(), patchSet.getId(), null, null)) {
- LabelType lt = labelTypes.byLabel(ca.getLabelId());
+ args.approvalsUtil.byPatchSet(changeData.notes(), patchSet.id(), null, null)) {
+ LabelType lt = labelTypes.byLabel(ca.labelId());
if (lt == null) {
continue;
}
- if (ca.getValue() > 0) {
- pos.put(ca.getAccountId(), lt.getName(), ca);
- } else if (ca.getValue() < 0) {
- neg.put(ca.getAccountId(), lt.getName(), ca);
+ if (ca.value() > 0) {
+ pos.put(ca.accountId(), lt.getName(), ca);
+ } else if (ca.value() < 0) {
+ neg.put(ca.accountId(), lt.getName(), ca);
}
}
@@ -117,7 +117,7 @@ public class MergedSender extends ReplyToChangeSender {
} else {
txt.append(lt.getName());
txt.append('=');
- txt.append(LabelValue.formatValue(ca.getValue()));
+ txt.append(LabelValue.formatValue(ca.value()));
}
}
txt.append('\n');
diff --git a/java/com/google/gerrit/server/mail/send/NewChangeSender.java b/java/com/google/gerrit/server/mail/send/NewChangeSender.java
index a31596bdb0..83c3a944cb 100644
--- a/java/com/google/gerrit/server/mail/send/NewChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/NewChangeSender.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.query.change.ChangeData;
import java.util.ArrayList;
import java.util.Collection;
@@ -32,8 +32,8 @@ public abstract class NewChangeSender extends ChangeEmail {
private final Set<Account.Id> extraCC = new HashSet<>();
private final Set<Address> extraCCByEmail = new HashSet<>();
- protected NewChangeSender(EmailArguments ea, ChangeData cd) {
- super(ea, "newchange", cd);
+ protected NewChangeSender(EmailArguments args, ChangeData changeData) {
+ super(args, "newchange", changeData);
}
public void addReviewers(Collection<Account.Id> cc) {
diff --git a/java/com/google/gerrit/server/mail/send/NotificationEmail.java b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
index 9952ba6ff6..0fb5c6f9d6 100644
--- a/java/com/google/gerrit/server/mail/send/NotificationEmail.java
+++ b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
@@ -17,13 +17,13 @@ package com.google.gerrit.server.mail.send;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.mail.Address;
import com.google.gerrit.mail.MailHeader;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
import java.util.HashMap;
@@ -33,10 +33,10 @@ import java.util.Map;
public abstract class NotificationEmail extends OutgoingEmail {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- protected Branch.NameKey branch;
+ protected BranchNameKey branch;
- protected NotificationEmail(EmailArguments ea, String mc, Branch.NameKey branch) {
- super(ea, mc);
+ protected NotificationEmail(EmailArguments args, String messageClass, BranchNameKey branch) {
+ super(args, messageClass);
this.branch = branch;
}
@@ -50,7 +50,7 @@ public abstract class NotificationEmail extends OutgoingEmail {
// Set a reasonable list id so that filters can be used to sort messages
setHeader(
"List-Id",
- "<gerrit-" + branch.getParentKey().get().replace('/', '-') + "." + getGerritHost() + ">");
+ "<gerrit-" + branch.project().get().replace('/', '-') + "." + getGerritHost() + ">");
if (getSettingsUrl() != null) {
setHeader("List-Unsubscribe", "<" + getSettingsUrl() + ">");
}
@@ -104,7 +104,7 @@ public abstract class NotificationEmail extends OutgoingEmail {
protected void setupSoyContext() {
super.setupSoyContext();
- String projectName = branch.getParentKey().get();
+ String projectName = branch.project().get();
soyContext.put("projectName", projectName);
// shortProjectName is the project name with the path abbreviated.
soyContext.put("shortProjectName", getShortProjectName(projectName));
@@ -118,11 +118,11 @@ public abstract class NotificationEmail extends OutgoingEmail {
soyContextEmailData.put("sshHost", getSshHost());
Map<String, String> branchData = new HashMap<>();
- branchData.put("shortName", branch.getShortName());
+ branchData.put("shortName", branch.shortName());
soyContext.put("branch", branchData);
- footers.add(MailHeader.PROJECT.withDelimiter() + branch.getParentKey().get());
- footers.add(MailHeader.BRANCH.withDelimiter() + branch.getShortName());
+ footers.add(MailHeader.PROJECT.withDelimiter() + branch.project().get());
+ footers.add(MailHeader.BRANCH.withDelimiter() + branch.shortName());
}
@VisibleForTesting
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 3bb710bb02..4f214edf43 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -20,6 +20,9 @@ import static java.util.Objects.requireNonNull;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.UserIdentity;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
@@ -28,14 +31,12 @@ import com.google.gerrit.mail.Address;
import com.google.gerrit.mail.EmailHeader;
import com.google.gerrit.mail.EmailHeader.AddressList;
import com.google.gerrit.mail.MailHeader;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
import com.google.gerrit.server.validators.ValidationException;
-import com.google.template.soy.data.SanitizedContent;
+import com.google.template.soy.jbcsrc.api.SoySauce;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@@ -55,6 +56,7 @@ import org.eclipse.jgit.util.SystemReader;
/** Sends an email to one or more interested parties. */
public abstract class OutgoingEmail {
+ private static final String SOY_TEMPLATE_NAMESPACE = "com.google.gerrit.server.mail.template.";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
protected String messageClass;
@@ -71,10 +73,10 @@ public abstract class OutgoingEmail {
protected Account.Id fromId;
protected NotifyResolver.Result notify = NotifyResolver.Result.all();
- protected OutgoingEmail(EmailArguments ea, String mc) {
- args = ea;
- messageClass = mc;
- headers = new LinkedHashMap<>();
+ protected OutgoingEmail(EmailArguments args, String messageClass) {
+ this.args = args;
+ this.messageClass = messageClass;
+ this.headers = new LinkedHashMap<>();
}
public void setFrom(Account.Id id) {
@@ -119,7 +121,7 @@ public abstract class OutgoingEmail {
if (fromId != null) {
Optional<AccountState> fromUser = args.accountCache.get(fromId);
if (fromUser.isPresent()) {
- GeneralPreferencesInfo senderPrefs = fromUser.get().getGeneralPreferences();
+ GeneralPreferencesInfo senderPrefs = fromUser.get().generalPreferences();
if (senderPrefs != null && senderPrefs.getEmailStrategy() == CC_ON_OWN_COMMENTS) {
// If we are impersonating a user, make sure they receive a CC of
// this message so they can always review and audit what we sent
@@ -130,7 +132,7 @@ public abstract class OutgoingEmail {
// If they don't want a copy, but we queued one up anyway,
// drop them from the recipient lists.
//
- removeUser(fromUser.get().getAccount());
+ removeUser(fromUser.get().account());
}
}
}
@@ -140,14 +142,14 @@ public abstract class OutgoingEmail {
for (Account.Id id : rcptTo) {
Optional<AccountState> thisUser = args.accountCache.get(id);
if (thisUser.isPresent()) {
- Account thisUserAccount = thisUser.get().getAccount();
- GeneralPreferencesInfo prefs = thisUser.get().getGeneralPreferences();
+ Account thisUserAccount = thisUser.get().account();
+ GeneralPreferencesInfo prefs = thisUser.get().generalPreferences();
if (prefs == null || prefs.getEmailStrategy() == DISABLED) {
removeUser(thisUserAccount);
} else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) {
removeUser(thisUserAccount);
smtpRcptToPlaintextOnly.add(
- new Address(thisUserAccount.getFullName(), thisUserAccount.getPreferredEmail()));
+ new Address(thisUserAccount.fullName(), thisUserAccount.preferredEmail()));
}
}
if (smtpRcptTo.isEmpty() && smtpRcptToPlaintextOnly.isEmpty()) {
@@ -262,10 +264,10 @@ public abstract class OutgoingEmail {
protected String getFromLine() {
StringBuilder f = new StringBuilder();
- Optional<Account> account = args.accountCache.get(fromId).map(AccountState::getAccount);
+ Optional<Account> account = args.accountCache.get(fromId).map(AccountState::account);
if (account.isPresent()) {
- String name = account.get().getFullName();
- String email = account.get().getPreferredEmail();
+ String name = account.get().fullName();
+ String email = account.get().preferredEmail();
if ((name != null && !name.isEmpty()) || (email != null && !email.isEmpty())) {
f.append("From");
if (name != null && !name.isEmpty()) {
@@ -333,17 +335,17 @@ public abstract class OutgoingEmail {
}
/** Lookup a human readable name for an account, usually the "full name". */
- protected String getNameFor(Account.Id accountId) {
+ protected String getNameFor(@Nullable Account.Id accountId) {
if (accountId == null) {
return args.gerritPersonIdent.getName();
}
- Optional<Account> account = args.accountCache.get(accountId).map(AccountState::getAccount);
+ Optional<Account> account = args.accountCache.get(accountId).map(AccountState::account);
String name = null;
if (account.isPresent()) {
- name = account.get().getFullName();
+ name = account.get().fullName();
if (name == null) {
- name = account.get().getPreferredEmail();
+ name = account.get().preferredEmail();
}
}
if (name == null) {
@@ -359,11 +361,18 @@ public abstract class OutgoingEmail {
* @param accountId user to fetch.
* @return name/email of account, or Anonymous Coward if unset.
*/
- protected String getNameEmailFor(Account.Id accountId) {
- Optional<Account> account = args.accountCache.get(accountId).map(AccountState::getAccount);
+ protected String getNameEmailFor(@Nullable Account.Id accountId) {
+ if (accountId == null) {
+ return args.gerritPersonIdent.getName()
+ + " <"
+ + args.gerritPersonIdent.getEmailAddress()
+ + ">";
+ }
+
+ Optional<Account> account = args.accountCache.get(accountId).map(AccountState::account);
if (account.isPresent()) {
- String name = account.get().getFullName();
- String email = account.get().getPreferredEmail();
+ String name = account.get().fullName();
+ String email = account.get().preferredEmail();
if (name != null && email != null) {
return name + " <" + email + ">";
} else if (name != null) {
@@ -388,9 +397,9 @@ public abstract class OutgoingEmail {
return null;
}
- Account account = accountState.get().getAccount();
- String name = account.getFullName();
- String email = account.getPreferredEmail();
+ Account account = accountState.get().account();
+ String name = account.fullName();
+ String email = account.preferredEmail();
if (name != null && email != null) {
return name + " <" + email + ">";
} else if (email != null) {
@@ -398,7 +407,7 @@ public abstract class OutgoingEmail {
} else if (name != null) {
return name;
}
- return accountState.get().getUserName().orElse(null);
+ return accountState.get().userName().orElse(null);
}
protected boolean shouldSendMessage() {
@@ -520,17 +529,17 @@ public abstract class OutgoingEmail {
}
private Address toAddress(Account.Id id) {
- Optional<Account> accountState = args.accountCache.get(id).map(AccountState::getAccount);
+ Optional<Account> accountState = args.accountCache.get(id).map(AccountState::account);
if (!accountState.isPresent()) {
return null;
}
Account account = accountState.get();
- String e = account.getPreferredEmail();
+ String e = account.preferredEmail();
if (!account.isActive() || e == null) {
return null;
}
- return new Address(account.getFullName(), e);
+ return new Address(account.fullName(), e);
}
protected void setupSoyContext() {
@@ -552,24 +561,23 @@ public abstract class OutgoingEmail {
return args.instanceNameProvider.get();
}
- private String soyTemplate(String name, SanitizedContent.ContentKind kind) {
- return args.soyTofu
- .newRenderer("com.google.gerrit.server.mail.template." + name)
- .setContentKind(kind)
- .setData(soyContext)
- .render();
- }
-
+ /** Renders a soy template of kind="text". */
protected String textTemplate(String name) {
- return soyTemplate(name, SanitizedContent.ContentKind.TEXT);
+ return configureRenderer(name).renderText().get();
}
+ /** Renders a soy template of kind="html". */
protected String soyHtmlTemplate(String name) {
- return soyTemplate(name, SanitizedContent.ContentKind.HTML);
+ return configureRenderer(name).renderHtml().get().toString();
+ }
+
+ /** Configures a soy renderer for the given template name and rendering data map. */
+ private SoySauce.Renderer configureRenderer(String templateName) {
+ return args.soySauce.renderTemplate(SOY_TEMPLATE_NAMESPACE + templateName).setData(soyContext);
}
protected void removeUser(Account user) {
- String fromEmail = user.getPreferredEmail();
+ String fromEmail = user.preferredEmail();
for (Iterator<Address> j = smtpRcptTo.iterator(); j.hasNext(); ) {
if (j.next().getEmail().equals(fromEmail)) {
j.remove();
diff --git a/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index 0a468b411c..2530d7e7fc 100644
--- a/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -19,12 +19,12 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountState;
@@ -66,9 +66,8 @@ public class ProjectWatch {
Set<Account.Id> projectWatchers = new HashSet<>();
for (AccountState a : args.accountQueryProvider.get().byWatchedProject(project)) {
- Account.Id accountId = a.getAccount().getId();
- for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e :
- a.getProjectWatches().entrySet()) {
+ Account.Id accountId = a.account().id();
+ for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e : a.projectWatches().entrySet()) {
if (project.equals(e.getKey().project())
&& add(matching, accountId, e.getKey(), e.getValue(), type)) {
// We only want to prevent matching All-Projects if this filter hits
@@ -78,10 +77,9 @@ public class ProjectWatch {
}
for (AccountState a : args.accountQueryProvider.get().byWatchedProject(args.allProjectsName)) {
- for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e :
- a.getProjectWatches().entrySet()) {
+ for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e : a.projectWatches().entrySet()) {
if (args.allProjectsName.equals(e.getKey().project())) {
- Account.Id accountId = a.getAccount().getId();
+ Account.Id accountId = a.account().id();
if (!projectWatchers.contains(accountId)) {
add(matching, accountId, e.getKey(), e.getValue(), type);
}
@@ -97,9 +95,9 @@ public class ProjectWatch {
for (NotifyConfig nc : state.getConfig().getNotifyConfigs()) {
if (nc.isNotify(type)) {
try {
- add(matching, nc);
+ add(matching, state.getNameKey(), nc);
} catch (QueryParseException e) {
- logger.atWarning().log(
+ logger.atInfo().log(
"Project %s has invalid notify %s filter \"%s\": %s",
state.getName(), nc.getName(), nc.getFilter(), e.getMessage());
}
@@ -146,17 +144,27 @@ public class ProjectWatch {
}
}
- private void add(Watchers matching, NotifyConfig nc) throws QueryParseException {
- for (GroupReference ref : nc.getGroups()) {
- CurrentUser user = new SingleGroupUser(ref.getUUID());
+ private void add(Watchers matching, Project.NameKey projectName, NotifyConfig nc)
+ throws QueryParseException {
+ logger.atFine().log("Checking watchers for notify config %s from project %s", nc, projectName);
+ for (GroupReference groupRef : nc.getGroups()) {
+ CurrentUser user = new SingleGroupUser(groupRef.getUUID());
if (filterMatch(user, nc.getFilter())) {
- deliverToMembers(matching.list(nc.getHeader()), ref.getUUID());
+ deliverToMembers(matching.list(nc.getHeader()), groupRef.getUUID());
+ logger.atFine().log("Added watchers for group %s", groupRef);
+ } else {
+ logger.atFine().log("The filter did not match for group %s; skip notification", groupRef);
}
}
if (!nc.getAddresses().isEmpty()) {
if (filterMatch(null, nc.getFilter())) {
matching.list(nc.getHeader()).emails.addAll(nc.getAddresses());
+ logger.atFine().log("Added watchers for these addresses: %s", nc.getAddresses());
+ } else {
+ logger.atFine().log(
+ "The filter did not match; skip notification for these addresses: %s",
+ nc.getAddresses());
}
}
}
@@ -172,19 +180,24 @@ public class ProjectWatch {
AccountGroup.UUID uuid = q.remove(q.size() - 1);
GroupDescription.Basic group = args.groupBackend.get(uuid);
if (group == null) {
+ logger.atFine().log("group %s not found, skip notification", uuid);
continue;
}
if (!Strings.isNullOrEmpty(group.getEmailAddress())) {
// If the group has an email address, do not expand membership.
matching.emails.add(new Address(group.getEmailAddress()));
+ logger.atFine().log(
+ "notify group email address %s; skip expanding to members", group.getEmailAddress());
continue;
}
if (!(group instanceof GroupDescription.Internal)) {
// Non-internal groups cannot be expanded by the server.
+ logger.atFine().log("group %s is not an internal group, skip notification", uuid);
continue;
}
+ logger.atFine().log("adding the members of group %s as watchers", uuid);
GroupDescription.Internal ig = (GroupDescription.Internal) group;
matching.accounts.addAll(ig.getMembers());
for (AccountGroup.UUID m : ig.getSubgroups()) {
@@ -201,8 +214,9 @@ public class ProjectWatch {
ProjectWatchKey key,
Set<NotifyType> watchedTypes,
NotifyType type) {
- IdentifiedUser user = args.identifiedUserFactory.create(accountId);
+ logger.atFine().log("Checking project watch %s of account %s", key, accountId);
+ IdentifiedUser user = args.identifiedUserFactory.create(accountId);
try {
if (filterMatch(user, key.filter())) {
// If we are set to notify on this type, add the user.
@@ -210,10 +224,14 @@ public class ProjectWatch {
if (watchedTypes.contains(type)) {
matching.bcc.accounts.add(accountId);
}
+ logger.atFine().log("Added account %s as watcher", accountId);
return true;
}
+ logger.atFine().log("The filter did not match for account %s; skip notification", accountId);
} catch (QueryParseException e) {
// Ignore broken filter expressions.
+ logger.atInfo().log(
+ "Account %s has invalid filter in project watch %s: %s", accountId, key, e.getMessage());
}
return false;
}
diff --git a/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java b/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
index 436736b560..91d8e817af 100644
--- a/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
@@ -36,14 +36,14 @@ public class RegisterNewEmailSender extends OutgoingEmail {
@Inject
public RegisterNewEmailSender(
- EmailArguments ea,
- EmailTokenVerifier etv,
+ EmailArguments args,
+ EmailTokenVerifier tokenVerifier,
IdentifiedUser callingUser,
@Assisted final String address) {
- super(ea, "registernewemail");
- tokenVerifier = etv;
- user = callingUser;
- addr = address;
+ super(args, "registernewemail");
+ this.tokenVerifier = tokenVerifier;
+ this.user = callingUser;
+ this.addr = address;
}
@Override
diff --git a/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java b/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
index 30bcdeb1d4..909c52aef8 100644
--- a/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
+++ b/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -32,7 +32,7 @@ import java.util.Set;
/** Send notice of new patch sets for reviewers. */
public class ReplacePatchSetSender extends ReplyToChangeSender {
public interface Factory {
- ReplacePatchSetSender create(Project.NameKey project, Change.Id id);
+ ReplacePatchSetSender create(Project.NameKey project, Change.Id changeId);
}
private final Set<Account.Id> reviewers = new HashSet<>();
@@ -40,8 +40,8 @@ public class ReplacePatchSetSender extends ReplyToChangeSender {
@Inject
public ReplacePatchSetSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
- super(ea, "newpatchset", newChangeData(ea, project, id));
+ EmailArguments args, @Assisted Project.NameKey project, @Assisted Change.Id changeId) {
+ super(args, "newpatchset", newChangeData(args, project, changeId));
}
public void addReviewers(Collection<Account.Id> cc) {
diff --git a/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java b/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
index 960c3a84d9..c765430437 100644
--- a/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.query.change.ChangeData;
/** Alert a user to a reply to a change, usually commentary made during review. */
@@ -26,8 +26,8 @@ public abstract class ReplyToChangeSender extends ChangeEmail {
T create(Project.NameKey project, Change.Id id);
}
- protected ReplyToChangeSender(EmailArguments ea, String mc, ChangeData cd) {
- super(ea, mc, cd);
+ protected ReplyToChangeSender(EmailArguments args, String messageClass, ChangeData changeData) {
+ super(args, messageClass, changeData);
}
@Override
diff --git a/java/com/google/gerrit/server/mail/send/RestoredSender.java b/java/com/google/gerrit/server/mail/send/RestoredSender.java
index 0d998aa9b9..2a4c55646f 100644
--- a/java/com/google/gerrit/server/mail/send/RestoredSender.java
+++ b/java/com/google/gerrit/server/mail/send/RestoredSender.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -25,13 +25,13 @@ import com.google.inject.assistedinject.Assisted;
public class RestoredSender extends ReplyToChangeSender {
public interface Factory extends ReplyToChangeSender.Factory<RestoredSender> {
@Override
- RestoredSender create(Project.NameKey project, Change.Id id);
+ RestoredSender create(Project.NameKey project, Change.Id changeId);
}
@Inject
public RestoredSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
- super(ea, "restore", ChangeEmail.newChangeData(ea, project, id));
+ EmailArguments args, @Assisted Project.NameKey project, @Assisted Change.Id changeId) {
+ super(args, "restore", ChangeEmail.newChangeData(args, project, changeId));
}
@Override
diff --git a/java/com/google/gerrit/server/mail/send/RevertedSender.java b/java/com/google/gerrit/server/mail/send/RevertedSender.java
index 48b5d9954f..dadd0d2ec1 100644
--- a/java/com/google/gerrit/server/mail/send/RevertedSender.java
+++ b/java/com/google/gerrit/server/mail/send/RevertedSender.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -24,13 +24,13 @@ import com.google.inject.assistedinject.Assisted;
/** Send notice about a change being reverted. */
public class RevertedSender extends ReplyToChangeSender {
public interface Factory {
- RevertedSender create(Project.NameKey project, Change.Id id);
+ RevertedSender create(Project.NameKey project, Change.Id changeId);
}
@Inject
public RevertedSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
- super(ea, "revert", ChangeEmail.newChangeData(ea, project, id));
+ EmailArguments args, @Assisted Project.NameKey project, @Assisted Change.Id changeId) {
+ super(args, "revert", ChangeEmail.newChangeData(args, project, changeId));
}
@Override
diff --git a/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java b/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
index a120769f61..850f775e1e 100644
--- a/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
+++ b/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
@@ -14,28 +14,28 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
public class SetAssigneeSender extends ChangeEmail {
public interface Factory {
- SetAssigneeSender create(Project.NameKey project, Change.Id id, Account.Id assignee);
+ SetAssigneeSender create(Project.NameKey project, Change.Id changeId, Account.Id assignee);
}
private final Account.Id assignee;
@Inject
public SetAssigneeSender(
- EmailArguments ea,
+ EmailArguments args,
@Assisted Project.NameKey project,
- @Assisted Change.Id id,
+ @Assisted Change.Id changeId,
@Assisted Account.Id assignee) {
- super(ea, "setassignee", newChangeData(ea, project, id));
+ super(args, "setassignee", newChangeData(args, project, changeId));
this.assignee = assignee;
}
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index 1c6057d6ff..0acf20e7f3 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -20,11 +20,12 @@ import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.metrics.Timer1;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -49,8 +50,8 @@ public abstract class AbstractChangeNotes<T> {
public final ChangeNoteJson changeNoteJson;
public final GitRepositoryManager repoManager;
public final AllUsersName allUsers;
- public final LegacyChangeNoteRead legacyChangeNoteRead;
public final NoteDbMetrics metrics;
+ public final String serverId;
// Providers required to avoid dependency cycles.
@@ -62,16 +63,16 @@ public abstract class AbstractChangeNotes<T> {
GitRepositoryManager repoManager,
AllUsersName allUsers,
ChangeNoteJson changeNoteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
NoteDbMetrics metrics,
- Provider<ChangeNotesCache> cache) {
+ Provider<ChangeNotesCache> cache,
+ @GerritServerId String serverId) {
this.failOnLoadForTest = new AtomicBoolean();
this.repoManager = repoManager;
this.allUsers = allUsers;
- this.legacyChangeNoteRead = legacyChangeNoteRead;
this.changeNoteJson = changeNoteJson;
this.metrics = metrics;
this.cache = cache;
+ this.serverId = serverId;
}
}
@@ -139,7 +140,7 @@ public abstract class AbstractChangeNotes<T> {
if (args.failOnLoadForTest.get()) {
throw new StorageException("Reading from NoteDb is disabled");
}
- try (Timer1.Context timer = args.metrics.readLatency.start(CHANGES);
+ try (Timer1.Context<NoteDbTable> timer = args.metrics.readLatency.start(CHANGES);
Repository repo = args.repoManager.openRepository(getProjectName());
// Call openHandle even if reading is disabled, to trigger
// auto-rebuilding before this object may get passed to a ChangeUpdate.
@@ -153,6 +154,7 @@ public abstract class AbstractChangeNotes<T> {
return self();
}
+ @Nullable
protected ObjectId readRef(Repository repo) throws IOException {
Ref ref = repo.getRefDatabase().exactRef(getRefName());
return ref != null ? ref.getObjectId() : null;
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
index daef7e7a3f..ee3ccd6d01 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
@@ -19,11 +19,11 @@ import static com.google.common.base.Preconditions.checkState;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
@@ -147,7 +147,7 @@ public abstract class AbstractChangeUpdate {
}
public void setPatchSetId(PatchSet.Id psId) {
- checkArgument(psId == null || psId.getParentKey().equals(getId()));
+ checkArgument(psId == null || psId.changeId().equals(getId()));
this.psId = psId;
}
@@ -193,6 +193,14 @@ public abstract class AbstractChangeUpdate {
}
/**
+ * Whether to allow bypassing the check that an update does not exceed the max update count on an
+ * object.
+ */
+ protected boolean bypassMaxUpdates() {
+ return false;
+ }
+
+ /**
* Apply this update to the given inserter.
*
* @param rw walk for reading back any objects needed for the update.
@@ -267,7 +275,7 @@ public abstract class AbstractChangeUpdate {
}
protected void verifyComment(Comment c) {
- checkArgument(c.revId != null, "RevId required for comment: %s", c);
+ checkArgument(c.getCommitId() != null, "commit ID required for comment: %s", c);
checkArgument(
c.author.getId().equals(getAccountId()),
"The author for the following comment does not match the author of this %s (%s): %s",
diff --git a/java/com/google/gerrit/server/notedb/AllUsersAsyncUpdate.java b/java/com/google/gerrit/server/notedb/AllUsersAsyncUpdate.java
new file mode 100644
index 0000000000..112f6875b4
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/AllUsersAsyncUpdate.java
@@ -0,0 +1,116 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.notedb;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.git.RefUpdateUtil;
+import com.google.gerrit.server.FanOutExecutor;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.transport.PushCertificate;
+
+/**
+ * Performs an update on {@code All-Users} asynchronously if required. No-op in case no updates were
+ * scheduled for asynchronous execution.
+ */
+public class AllUsersAsyncUpdate {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final ExecutorService executor;
+ private final AllUsersName allUsersName;
+ private final GitRepositoryManager repoManager;
+ private final ListMultimap<String, ChangeDraftUpdate> draftUpdates;
+
+ private PersonIdent serverIdent;
+
+ @Inject
+ AllUsersAsyncUpdate(
+ @FanOutExecutor ExecutorService executor,
+ AllUsersName allUsersName,
+ GitRepositoryManager repoManager) {
+ this.executor = executor;
+ this.allUsersName = allUsersName;
+ this.repoManager = repoManager;
+ this.draftUpdates = MultimapBuilder.hashKeys().arrayListValues().build();
+ }
+
+ void setDraftUpdates(ListMultimap<String, ChangeDraftUpdate> draftUpdates) {
+ checkState(isEmpty(), "attempted to set draft comment updates for async execution twice");
+ boolean allPublishOnly =
+ draftUpdates.values().stream().allMatch(ChangeDraftUpdate::canRunAsync);
+ checkState(allPublishOnly, "not all updates can be run asynchronously");
+ // Add deep copies to avoid any threading issues.
+ for (Map.Entry<String, ChangeDraftUpdate> entry : draftUpdates.entries()) {
+ this.draftUpdates.put(entry.getKey(), entry.getValue().copy());
+ }
+ if (draftUpdates.size() > 0) {
+ // Save the PersonIdent for later so that we get consistent time stamps in the commit and ref
+ // log.
+ serverIdent = Iterables.get(draftUpdates.entries(), 0).getValue().serverIdent;
+ }
+ }
+
+ /** Returns true if no operations should be performed on the repo. */
+ boolean isEmpty() {
+ return draftUpdates.isEmpty();
+ }
+
+ /** Executes repository update asynchronously. No-op in case no updates were scheduled. */
+ void execute(PersonIdent refLogIdent, String refLogMessage, PushCertificate pushCert) {
+ if (isEmpty()) {
+ return;
+ }
+
+ @SuppressWarnings("unused")
+ Future<?> possiblyIgnoredError =
+ executor.submit(
+ () -> {
+ try (OpenRepo allUsersRepo = OpenRepo.open(repoManager, allUsersName)) {
+ allUsersRepo.addUpdates(draftUpdates);
+ allUsersRepo.flush();
+ BatchRefUpdate bru = allUsersRepo.repo.getRefDatabase().newBatchUpdate();
+ bru.setPushCertificate(pushCert);
+ if (refLogMessage != null) {
+ bru.setRefLogMessage(refLogMessage, false);
+ } else {
+ bru.setRefLogMessage(
+ firstNonNull(NoteDbUtil.guessRestApiHandler(), "Update NoteDb refs async"),
+ false);
+ }
+ bru.setRefLogIdent(refLogIdent != null ? refLogIdent : serverIdent);
+ bru.setAtomic(true);
+ allUsersRepo.cmds.addTo(bru);
+ bru.setAllowNonFastForwards(true);
+ RefUpdateUtil.executeChecked(bru, allUsersRepo.rw);
+ } catch (IOException e) {
+ logger.atSevere().withCause(e).log(
+ "Failed to delete draft comments asynchronously after publishing them");
+ }
+ });
+ }
+}
diff --git a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
index 8773d9b4f4..05fdee9a6e 100644
--- a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
@@ -15,18 +15,17 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkState;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.auto.value.AutoValue;
import com.google.common.collect.Sets;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllUsersName;
import com.google.inject.assistedinject.Assisted;
@@ -35,7 +34,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -74,19 +73,25 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
@AutoValue
abstract static class Key {
- abstract String revId();
+ abstract ObjectId commitId();
abstract Comment.Key key();
}
+ enum DeleteReason {
+ DELETED,
+ PUBLISHED,
+ FIXED
+ }
+
private static Key key(Comment c) {
- return new AutoValue_ChangeDraftUpdate_Key(c.revId, c.key);
+ return new AutoValue_ChangeDraftUpdate_Key(c.getCommitId(), c.key);
}
private final AllUsersName draftsProject;
private List<Comment> put = new ArrayList<>();
- private Set<Key> delete = new HashSet<>();
+ private Map<Key, DeleteReason> delete = new HashMap<>();
@AssistedInject
private ChangeDraftUpdate(
@@ -117,40 +122,92 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
}
public void putComment(Comment c) {
+ checkState(!put.contains(c), "comment already added");
verifyComment(c);
put.add(c);
}
+ /**
+ * Marks a comment for deletion. Called when the comment is deleted because the user published it.
+ */
+ public void markCommentPublished(Comment c) {
+ checkState(!delete.containsKey(key(c)), "comment already marked for deletion");
+ verifyComment(c);
+ delete.put(key(c), DeleteReason.PUBLISHED);
+ }
+
+ /**
+ * Marks a comment for deletion. Called when the comment is deleted because the user removed it.
+ */
public void deleteComment(Comment c) {
+ checkState(!delete.containsKey(key(c)), "comment already marked for deletion");
verifyComment(c);
- delete.add(key(c));
+ delete.put(key(c), DeleteReason.DELETED);
+ }
+
+ /**
+ * Marks a comment for deletion. Called when the comment should have been deleted previously, but
+ * wasn't, so we're fixing it up.
+ */
+ public void deleteComment(ObjectId commitId, Comment.Key key) {
+ Key commentKey = new AutoValue_ChangeDraftUpdate_Key(commitId, key);
+ checkState(!delete.containsKey(commentKey), "comment already marked for deletion");
+ delete.put(commentKey, DeleteReason.FIXED);
+ }
+
+ /**
+ * Returns true if all we do in this operations is deletes caused by publishing or fixing up
+ * comments.
+ */
+ public boolean canRunAsync() {
+ return put.isEmpty()
+ && delete.values().stream()
+ .allMatch(r -> r == DeleteReason.PUBLISHED || r == DeleteReason.FIXED);
}
- public void deleteComment(String revId, Comment.Key key) {
- delete.add(new AutoValue_ChangeDraftUpdate_Key(revId, key));
+ /**
+ * Returns a copy of the current {@link ChangeDraftUpdate} that contains references to all
+ * deletions. Copying of {@link ChangeDraftUpdate} is only allowed if it contains no new comments.
+ */
+ ChangeDraftUpdate copy() {
+ checkState(
+ put.isEmpty(),
+ "copying ChangeDraftUpdate is allowed only if it doesn't contain new comments");
+ ChangeDraftUpdate clonedUpdate =
+ new ChangeDraftUpdate(
+ authorIdent,
+ draftsProject,
+ noteUtil,
+ new Change(getChange()),
+ accountId,
+ realAccountId,
+ authorIdent,
+ when);
+ clonedUpdate.delete.putAll(delete);
+ return clonedUpdate;
}
private CommitBuilder storeCommentsInNotes(
RevWalk rw, ObjectInserter ins, ObjectId curr, CommitBuilder cb)
throws ConfigInvalidException, IOException {
RevisionNoteMap<ChangeRevisionNote> rnm = getRevisionNoteMap(rw, curr);
- Set<RevId> updatedRevs = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
+ Set<ObjectId> updatedCommits = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
for (Comment c : put) {
- if (!delete.contains(key(c))) {
- cache.get(new RevId(c.revId)).putComment(c);
+ if (!delete.keySet().contains(key(c))) {
+ cache.get(c.getCommitId()).putComment(c);
}
}
- for (Key k : delete) {
- cache.get(new RevId(k.revId())).deleteComment(k.key());
+ for (Key k : delete.keySet()) {
+ cache.get(k.commitId()).deleteComment(k.key());
}
- Map<RevId, RevisionNoteBuilder> builders = cache.getBuilders();
+ Map<ObjectId, RevisionNoteBuilder> builders = cache.getBuilders();
boolean touchedAnyRevs = false;
- for (Map.Entry<RevId, RevisionNoteBuilder> e : builders.entrySet()) {
- updatedRevs.add(e.getKey());
- ObjectId id = ObjectId.fromString(e.getKey().get());
+ for (Map.Entry<ObjectId, RevisionNoteBuilder> e : builders.entrySet()) {
+ updatedCommits.add(e.getKey());
+ ObjectId id = e.getKey();
byte[] data = e.getValue().build(noteUtil.getChangeNoteJson());
if (!Arrays.equals(data, e.getValue().baseRaw)) {
touchedAnyRevs = true;
@@ -204,12 +261,7 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
// Even though reading from changes might not be enabled, we need to
// parse any existing revision notes so we can merge them.
return RevisionNoteMap.parse(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- getId(),
- rw.getObjectReader(),
- noteMap,
- PatchLineComment.Status.DRAFT);
+ noteUtil.getChangeNoteJson(), rw.getObjectReader(), noteMap, Comment.Status.DRAFT);
}
@Override
diff --git a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
index 628dfd2a73..b221ef51b3 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.notedb;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.config.GerritServerId;
import com.google.inject.Inject;
import java.util.Date;
@@ -63,22 +63,13 @@ public class ChangeNoteUtil {
static final String UNRESOLVED = "Unresolved";
static final String TAG = FOOTER_TAG.getName();
- private final LegacyChangeNoteRead legacyChangeNoteRead;
private final ChangeNoteJson changeNoteJson;
private final String serverId;
@Inject
- public ChangeNoteUtil(
- ChangeNoteJson changeNoteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
- @GerritServerId String serverId) {
+ public ChangeNoteUtil(ChangeNoteJson changeNoteJson, @GerritServerId String serverId) {
this.serverId = serverId;
this.changeNoteJson = changeNoteJson;
- this.legacyChangeNoteRead = legacyChangeNoteRead;
- }
-
- public LegacyChangeNoteRead getLegacyChangeNoteRead() {
- return legacyChangeNoteRead;
}
public ChangeNoteJson getChangeNoteJson() {
@@ -96,8 +87,8 @@ public class ChangeNoteUtil {
@VisibleForTesting
public PersonIdent newIdent(Account author, Date when, PersonIdent serverIdent) {
return new PersonIdent(
- "Gerrit User " + author.getId(),
- author.getId().get() + "@" + serverId,
+ "Gerrit User " + author.id(),
+ author.id().get() + "@" + serverId,
when,
serverIdent.getTimeZone());
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index a4021cb28c..bd673d6dec 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -16,18 +16,21 @@ package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
@@ -36,18 +39,18 @@ import com.google.common.collect.Sets.SetView;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.client.RobotComment;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
@@ -63,7 +66,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
@@ -78,7 +80,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
static final Ordering<PatchSetApproval> PSA_BY_TIME =
- Ordering.from(comparing(PatchSetApproval::getGranted));
+ Ordering.from(comparing(PatchSetApproval::granted));
public static final Ordering<ChangeMessage> MESSAGE_BY_TIME =
Ordering.from(comparing(ChangeMessage::getWrittenOn));
@@ -127,7 +129,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
public static Change newChange(Project.NameKey project, Change.Id changeId) {
return new Change(
- null, changeId, null, new Branch.NameKey(project, "INVALID_NOTE_DB_ONLY"), null);
+ null, changeId, null, BranchNameKey.create(project, "INVALID_NOTE_DB_ONLY"), null);
}
public ChangeNotes create(Project.NameKey project, Change.Id changeId) {
@@ -218,6 +220,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return idStream.map(id -> scanOneChange(project, sr, id)).filter(Objects::nonNull);
}
+ @Nullable
private ChangeNotesResult scanOneChange(Project.NameKey project, ScanResult sr, Change.Id id) {
if (!sr.fromMetaRefs().contains(id)) {
// Stray patch set refs can happen due to normal error conditions, e.g. failed
@@ -226,10 +229,15 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
}
// TODO(dborowitz): See discussion in BatchUpdate#newChangeContext.
- Change change = ChangeNotes.Factory.newChange(project, id);
-
- logger.atFine().log("adding change %s found in project %s", id, project);
- return toResult(change);
+ try {
+ Change change = ChangeNotes.Factory.newChange(project, id);
+ logger.atFine().log("adding change %s found in project %s", id, project);
+ return toResult(change);
+ } catch (InvalidServerIdException ise) {
+ logger.atWarning().withCause(ise).log(
+ "skipping change %d in project %s because of an invalid server id", id.get(), project);
+ return null;
+ }
}
@Nullable
@@ -340,9 +348,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
if (patchSets == null) {
ImmutableSortedMap.Builder<PatchSet.Id, PatchSet> b =
ImmutableSortedMap.orderedBy(comparing(PatchSet.Id::get));
- for (Map.Entry<PatchSet.Id, PatchSet> e : state.patchSets()) {
- b.put(e.getKey(), new PatchSet(e.getValue()));
- }
+ b.putAll(state.patchSets());
patchSets = b.build();
}
return patchSets;
@@ -350,12 +356,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
public ImmutableListMultimap<PatchSet.Id, PatchSetApproval> getApprovals() {
if (approvals == null) {
- ImmutableListMultimap.Builder<PatchSet.Id, PatchSetApproval> b =
- ImmutableListMultimap.builder();
- for (Map.Entry<PatchSet.Id, PatchSetApproval> e : state.approvals()) {
- b.put(e.getKey(), new PatchSetApproval(e.getValue()));
- }
- approvals = b.build();
+ approvals = ImmutableListMultimap.copyOf(state.approvals());
}
return approvals;
}
@@ -383,9 +384,24 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return state.reviewerUpdates();
}
- /** @return an ImmutableSet of Account.Ids of all users that have been assigned to this change. */
+ /**
+ * @return an ImmutableSet of Account.Ids of all users that have been assigned to this change. The
+ * order of the set is the order in which they were assigned.
+ */
public ImmutableSet<Account.Id> getPastAssignees() {
- return state.pastAssignees();
+ return Lists.reverse(state.assigneeUpdates()).stream()
+ .map(AssigneeStatusUpdate::currentAssignee)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * @return an ImmutableList of AssigneeStatusUpdate of all the updates to the assignee field to
+ * this change. The order of the list is from most recent updates to least recent.
+ */
+ public ImmutableList<AssigneeStatusUpdate> getAssigneeUpdates() {
+ return state.assigneeUpdates();
}
/** @return a ImmutableSet of all hashtags for this change sorted in alphabetical order. */
@@ -412,7 +428,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
}
/** @return inline comments on each revision. */
- public ImmutableListMultimap<RevId, Comment> getComments() {
+ public ImmutableListMultimap<ObjectId, Comment> getComments() {
return state.publishedComments();
}
@@ -427,11 +443,15 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return commentKeys;
}
- public ImmutableListMultimap<RevId, Comment> getDraftComments(Account.Id author) {
+ public int getUpdateCount() {
+ return state.updateCount();
+ }
+
+ public ImmutableListMultimap<ObjectId, Comment> getDraftComments(Account.Id author) {
return getDraftComments(author, null);
}
- public ImmutableListMultimap<RevId, Comment> getDraftComments(
+ public ImmutableListMultimap<ObjectId, Comment> getDraftComments(
Account.Id author, @Nullable Ref ref) {
loadDraftComments(author, ref);
// Filter out any zombie draft comments. These are drafts that are also in
@@ -442,7 +462,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
draftCommentNotes.getComments(), e -> !getCommentKeys().contains(e.getValue().key)));
}
- public ImmutableListMultimap<RevId, RobotComment> getRobotComments() {
+ public ImmutableListMultimap<ObjectId, RobotComment> getRobotComments() {
loadRobotComments();
return robotCommentNotes.getComments();
}
@@ -517,6 +537,19 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
ChangeNotesCache.Value v =
args.cache.get().get(getProjectName(), getChangeId(), rev, handle::walk);
state = v.state();
+
+ String stateServerId = state.serverId();
+ /**
+ * In earlier Gerrit versions serverId wasn't part of the change notes cache. That's why the
+ * earlier cached entries don't have the serverId attribute. That's fine because in earlier
+ * gerrit version serverId was already validated. Another approach to simplify the check would
+ * be to bump the cache version, but that would invalidate all persistent cache entries, what we
+ * rather try to avoid.
+ */
+ if (!Strings.isNullOrEmpty(stateServerId) && !args.serverId.equals(stateServerId)) {
+ throw new InvalidServerIdException(args.serverId, stateServerId);
+ }
+
state.copyColumnsTo(change);
revisionNoteMap = v.revisionNoteMap();
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesCache.java b/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
index e2af855f45..7fde297f23 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
@@ -20,10 +20,10 @@ import com.google.common.cache.Cache;
import com.google.common.collect.Table;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.cache.CacheModule;
@@ -61,7 +61,7 @@ public class ChangeNotesCache {
.weigher(Weigher.class)
.maximumWeight(10 << 20)
.diskLimit(-1)
- .version(1)
+ .version(2)
.keySerializer(Key.Serializer.INSTANCE)
.valueSerializer(ChangeNotesState.Serializer.INSTANCE);
}
@@ -98,8 +98,8 @@ public class ChangeNotesCache {
public Key deserialize(byte[] in) {
ChangeNotesKeyProto proto = Protos.parseUnchecked(ChangeNotesKeyProto.parser(), in);
return Key.create(
- new Project.NameKey(proto.getProject()),
- new Change.Id(proto.getChangeId()),
+ Project.nameKey(proto.getProject()),
+ Change.id(proto.getChangeId()),
ObjectIdConverter.create().fromByteString(proto.getId()));
}
}
@@ -112,8 +112,11 @@ public class ChangeNotesCache {
// Single pointer overhead.
private static final int P = 8;
+ // Single int overhead.
+ private static final int I = 4;
+
// Single IntKey overhead.
- private static final int K = O + 4;
+ private static final int K = O + I;
// Single Timestamp overhead.
private static final int T = O + 8;
@@ -145,11 +148,8 @@ public class ChangeNotesCache {
+ str(state.columns().originalSubject())
+ P
+ str(state.columns().submissionId())
- + ptr(state.columns().assignee(), K) // assignee
+ P // status
+ P
- + set(state.pastAssignees(), K)
- + P
+ set(state.hashtags(), str(10))
+ P
+ list(state.patchSets(), patchSet())
@@ -166,6 +166,8 @@ public class ChangeNotesCache {
+ P
+ list(state.reviewerUpdates(), 4 * O + K + K + P)
+ P
+ + list(state.assigneeUpdates(), 4 * O + K + K)
+ + P
+ list(state.submitRecords(), P + list(2, str(4) + P + K) + P)
+ P
+ list(state.changeMessages(), changeMessage())
@@ -173,11 +175,8 @@ public class ChangeNotesCache {
+ map(state.publishedComments().asMap(), comment())
+ 1 // isPrivate
+ 1 // workInProgress
- + 1; // reviewStarted
- }
-
- private static int ptr(Object o, int size) {
- return o != null ? P + size : P;
+ + 1 // reviewStarted
+ + I; // updateCount
}
private static int str(String s) {
@@ -351,12 +350,7 @@ public class ChangeNotesCache {
"Load change notes for change %s of project %s", key.changeId(), key.project());
ChangeNotesParser parser =
new ChangeNotesParser(
- key.changeId(),
- key.id(),
- walkSupplier.get(),
- args.changeNoteJson,
- args.legacyChangeNoteRead,
- args.metrics);
+ key.changeId(), key.id(), walkSupplier.get(), args.changeNoteJson, args.metrics);
ChangeNotesState result = parser.parseAll();
// This assignment only happens if call() was actually called, which only
// happens when Cache#get(K, Callable<V>) incurs a cache miss.
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index d3b34b9d60..60162cdfbd 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -40,7 +40,6 @@ import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.joining;
-import com.google.auto.value.AutoValue;
import com.google.common.base.Enums;
import com.google.common.base.Splitter;
import com.google.common.collect.HashBasedTable;
@@ -48,6 +47,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
@@ -56,18 +56,17 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.mail.Address;
import com.google.gerrit.metrics.Timer1;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
@@ -101,26 +100,8 @@ import org.eclipse.jgit.util.RawParseUtils;
class ChangeNotesParser {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- // Sentinel RevId indicating a mutable field on a patch set was parsed, but
- // the parser does not yet know its commit SHA-1.
- private static final RevId PARTIAL_PATCH_SET = new RevId("INVALID PARTIAL PATCH SET");
-
- @AutoValue
- abstract static class ApprovalKey {
- abstract PatchSet.Id psId();
-
- abstract Account.Id accountId();
-
- abstract String label();
-
- private static ApprovalKey create(PatchSet.Id psId, Account.Id accountId, String label) {
- return new AutoValue_ChangeNotesParser_ApprovalKey(psId, accountId, label);
- }
- }
-
// Private final members initialized in the constructor.
private final ChangeNoteJson changeNoteJson;
- private final LegacyChangeNoteRead legacyChangeNoteRead;
private final NoteDbMetrics metrics;
private final Change.Id id;
@@ -133,26 +114,26 @@ class ChangeNotesParser {
private final Table<Address, ReviewerStateInternal, Timestamp> reviewersByEmail;
private final List<Account.Id> allPastReviewers;
private final List<ReviewerStatusUpdate> reviewerUpdates;
+ private final List<AssigneeStatusUpdate> assigneeUpdates;
private final List<SubmitRecord> submitRecords;
- private final ListMultimap<RevId, Comment> comments;
- private final Map<PatchSet.Id, PatchSet> patchSets;
+ private final ListMultimap<ObjectId, Comment> comments;
+ private final Map<PatchSet.Id, PatchSet.Builder> patchSets;
private final Set<PatchSet.Id> deletedPatchSets;
private final Map<PatchSet.Id, PatchSetState> patchSetStates;
private final List<PatchSet.Id> currentPatchSets;
- private final Map<ApprovalKey, PatchSetApproval> approvals;
- private final List<PatchSetApproval> bufferedApprovals;
+ private final Map<PatchSetApproval.Key, PatchSetApproval.Builder> approvals;
+ private final List<PatchSetApproval.Builder> bufferedApprovals;
private final List<ChangeMessage> allChangeMessages;
// Non-final private members filled in during the parsing process.
private String branch;
private Change.Status status;
private String topic;
- private Optional<Account.Id> assignee;
- private List<Account.Id> pastAssignees;
private Set<String> hashtags;
private Timestamp createdOn;
private Timestamp lastUpdatedOn;
private Account.Id ownerId;
+ private String serverId;
private String changeId;
private String subject;
private String originalSubject;
@@ -166,19 +147,18 @@ class ChangeNotesParser {
private ReviewerSet pendingReviewers;
private ReviewerByEmailSet pendingReviewersByEmail;
private Change.Id revertOf;
+ private int updateCount;
ChangeNotesParser(
Change.Id changeId,
ObjectId tip,
ChangeNotesRevWalk walk,
ChangeNoteJson changeNoteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
NoteDbMetrics metrics) {
this.id = changeId;
this.tip = tip;
this.walk = walk;
this.changeNoteJson = changeNoteJson;
- this.legacyChangeNoteRead = legacyChangeNoteRead;
this.metrics = metrics;
approvals = new LinkedHashMap<>();
bufferedApprovals = new ArrayList<>();
@@ -188,6 +168,7 @@ class ChangeNotesParser {
pendingReviewersByEmail = ReviewerByEmailSet.empty();
allPastReviewers = new ArrayList<>();
reviewerUpdates = new ArrayList<>();
+ assigneeUpdates = new ArrayList<>();
submitRecords = Lists.newArrayListWithExpectedSize(1);
allChangeMessages = new ArrayList<>();
comments = MultimapBuilder.hashKeys().arrayListValues().build();
@@ -204,7 +185,7 @@ class ChangeNotesParser {
walk.reset();
walk.markStart(walk.parseCommit(tip));
- try (Timer1.Context timer = metrics.parseLatency.start(CHANGES)) {
+ try (Timer1.Context<NoteDbTable> timer = metrics.parseLatency.start(CHANGES)) {
ChangeNotesCommit commit;
while ((commit = walk.next()) != null) {
parse(commit);
@@ -232,25 +213,24 @@ class ChangeNotesParser {
return revisionNoteMap;
}
- private ChangeNotesState buildState() {
+ private ChangeNotesState buildState() throws ConfigInvalidException {
return ChangeNotesState.create(
tip.copy(),
id,
- new Change.Key(changeId),
+ Change.key(changeId),
createdOn,
lastUpdatedOn,
ownerId,
+ serverId,
branch,
buildCurrentPatchSetId(),
subject,
topic,
originalSubject,
submissionId,
- assignee != null ? assignee.orElse(null) : null,
status,
- Sets.newLinkedHashSet(Lists.reverse(pastAssignees)),
firstNonNull(hashtags, ImmutableSet.of()),
- patchSets,
+ buildPatchSets(),
buildApprovals(),
ReviewerSet.fromTable(Tables.transpose(reviewers)),
ReviewerByEmailSet.fromTable(Tables.transpose(reviewersByEmail)),
@@ -258,20 +238,37 @@ class ChangeNotesParser {
pendingReviewersByEmail,
allPastReviewers,
buildReviewerUpdates(),
+ assigneeUpdates,
submitRecords,
buildAllMessages(),
comments,
firstNonNull(isPrivate, false),
firstNonNull(workInProgress, false),
firstNonNull(hasReviewStarted, true),
- revertOf);
+ revertOf,
+ updateCount);
+ }
+
+ private Map<PatchSet.Id, PatchSet> buildPatchSets() throws ConfigInvalidException {
+ Map<PatchSet.Id, PatchSet> result = Maps.newHashMapWithExpectedSize(patchSets.size());
+ for (Map.Entry<PatchSet.Id, PatchSet.Builder> e : patchSets.entrySet()) {
+ try {
+ PatchSet ps = e.getValue().build();
+ result.put(ps.id(), ps);
+ } catch (Exception ex) {
+ ConfigInvalidException cie = parseException("Error building patch set %s", e.getKey());
+ cie.initCause(ex);
+ throw cie;
+ }
+ }
+ return result;
}
private PatchSet.Id buildCurrentPatchSetId() {
// currentPatchSets are in parse order, i.e. newest first. Pick the first
// patch set that was marked as current, excluding deleted patch sets.
for (PatchSet.Id psId : currentPatchSets) {
- if (patchSets.containsKey(psId)) {
+ if (patchSetCommitParsed(psId)) {
return psId;
}
}
@@ -281,14 +278,14 @@ class ChangeNotesParser {
private ListMultimap<PatchSet.Id, PatchSetApproval> buildApprovals() {
ListMultimap<PatchSet.Id, PatchSetApproval> result =
MultimapBuilder.hashKeys().arrayListValues().build();
- for (PatchSetApproval a : approvals.values()) {
- if (!patchSets.containsKey(a.getPatchSetId())) {
+ for (PatchSetApproval.Builder a : approvals.values()) {
+ if (!patchSetCommitParsed(a.key().patchSetId())) {
continue; // Patch set deleted or missing.
- } else if (allPastReviewers.contains(a.getAccountId())
- && !reviewers.containsRow(a.getAccountId())) {
+ } else if (allPastReviewers.contains(a.key().accountId())
+ && !reviewers.containsRow(a.key().accountId())) {
continue; // Reviewer was explicitly removed.
}
- result.put(a.getPatchSetId(), a);
+ result.put(a.key().patchSetId(), a.build());
}
result.keySet().forEach(k -> result.get(k).sort(ChangeNotes.PSA_BY_TIME));
return result;
@@ -311,6 +308,7 @@ class ChangeNotesParser {
}
private void parse(ChangeNotesCommit commit) throws ConfigInvalidException {
+ updateCount++;
Timestamp ts = new Timestamp(commit.getCommitterIdent().getWhen().getTime());
createdOn = ts;
@@ -334,6 +332,10 @@ class ChangeNotesParser {
Account.Id accountId = parseIdent(commit);
if (accountId != null) {
ownerId = accountId;
+ PersonIdent personIdent = commit.getAuthorIdent();
+ serverId = NoteDbUtil.extractHostPartFromPersonIdent(personIdent);
+ } else {
+ serverId = "UNKNOWN_SERVER_ID";
}
Account.Id realAccountId = parseRealAccountId(commit, accountId);
@@ -355,17 +357,29 @@ class ChangeNotesParser {
}
parseHashtags(commit);
- parseAssignee(commit);
+ parseAssigneeUpdates(ts, commit);
if (submissionId == null) {
submissionId = parseSubmissionId(commit);
}
+ if (lastUpdatedOn == null || ts.after(lastUpdatedOn)) {
+ lastUpdatedOn = ts;
+ }
+
+ if (deletedPatchSets.contains(psId)) {
+ // Do not update PS details as PS was deleted and this meta data is of no relevance.
+ return;
+ }
+
+ // Parse mutable patch set fields first so they can be recorded in the PendingPatchSetFields.
+ parseDescription(psId, commit);
+ parseGroups(psId, commit);
+
ObjectId currRev = parseRevision(commit);
if (currRev != null) {
parsePatchSet(psId, currRev, accountId, ts);
}
- parseGroups(psId, commit);
parseCurrentPatchSet(psId, commit);
if (submitRecords.isEmpty()) {
@@ -405,12 +419,6 @@ class ChangeNotesParser {
previousWorkInProgressFooter = null;
parseWorkInProgress(commit);
-
- if (lastUpdatedOn == null || ts.after(lastUpdatedOn)) {
- lastUpdatedOn = ts;
- }
-
- parseDescription(psId, commit);
}
private String parseSubmissionId(ChangeNotesCommit commit) throws ConfigInvalidException {
@@ -437,7 +445,7 @@ class ChangeNotesParser {
return effectiveAccountId;
}
PersonIdent ident = RawParseUtils.parsePersonIdent(realUser);
- return legacyChangeNoteRead.parseIdent(ident, id);
+ return parseIdent(ident);
}
private String parseTopic(ChangeNotesCommit commit) throws ConfigInvalidException {
@@ -483,24 +491,23 @@ class ChangeNotesParser {
if (accountId == null) {
throw parseException("patch set %s requires an identified user as uploader", psId.get());
}
- PatchSet ps = patchSets.get(psId);
- if (ps == null) {
- ps = new PatchSet(psId);
- patchSets.put(psId, ps);
- } else if (!ps.getRevision().equals(PARTIAL_PATCH_SET)) {
- if (deletedPatchSets.contains(psId)) {
- // Do not update PS details as PS was deleted and this meta data is of
- // no relevance
- return;
- }
+ if (patchSetCommitParsed(psId)) {
+ ObjectId commitId = patchSets.get(psId).commitId().orElseThrow(IllegalStateException::new);
throw new ConfigInvalidException(
String.format(
"Multiple revisions parsed for patch set %s: %s and %s",
- psId.get(), patchSets.get(psId).getRevision(), rev.name()));
- }
- ps.setRevision(new RevId(rev.name()));
- ps.setUploader(accountId);
- ps.setCreatedOn(ts);
+ psId.get(), commitId.name(), rev.name()));
+ }
+ patchSets
+ .computeIfAbsent(psId, id -> PatchSet.builder())
+ .id(psId)
+ .commitId(rev)
+ .uploader(accountId)
+ .createdOn(ts);
+ // Fields not set here:
+ // * Groups, parsed earlier in parseGroups.
+ // * Description, parsed earlier in parseDescription.
+ // * Push certificate, parsed later in parseNotes.
}
private void parseGroups(PatchSet.Id psId, ChangeNotesCommit commit)
@@ -509,15 +516,11 @@ class ChangeNotesParser {
if (groupsStr == null) {
return;
}
- PatchSet ps = patchSets.get(psId);
- if (ps == null) {
- ps = new PatchSet(psId);
- ps.setRevision(PARTIAL_PATCH_SET);
- patchSets.put(psId, ps);
- } else if (!ps.getGroups().isEmpty()) {
- return;
+ checkPatchSetCommitNotParsed(psId, FOOTER_GROUPS);
+ PatchSet.Builder pending = patchSets.computeIfAbsent(psId, id -> PatchSet.builder());
+ if (pending.groups().isEmpty()) {
+ pending.groups(PatchSet.splitGroups(groupsStr));
}
- ps.setGroups(PatchSet.splitGroups(groupsStr));
}
private void parseCurrentPatchSet(PatchSet.Id psId, ChangeNotesCommit commit)
@@ -560,10 +563,8 @@ class ChangeNotesParser {
}
}
- private void parseAssignee(ChangeNotesCommit commit) throws ConfigInvalidException {
- if (pastAssignees == null) {
- pastAssignees = Lists.newArrayList();
- }
+ private void parseAssigneeUpdates(Timestamp ts, ChangeNotesCommit commit)
+ throws ConfigInvalidException {
String assigneeValue = parseOneFooter(commit, FOOTER_ASSIGNEE);
if (assigneeValue != null) {
Optional<Account.Id> parsedAssignee;
@@ -572,14 +573,9 @@ class ChangeNotesParser {
parsedAssignee = Optional.empty();
} else {
PersonIdent ident = RawParseUtils.parsePersonIdent(assigneeValue);
- parsedAssignee = Optional.ofNullable(legacyChangeNoteRead.parseIdent(ident, id));
- }
- if (assignee == null) {
- assignee = parsedAssignee;
- }
- if (parsedAssignee.isPresent()) {
- pastAssignees.add(parsedAssignee.get());
+ parsedAssignee = Optional.ofNullable(parseIdent(ident));
}
+ assigneeUpdates.add(AssigneeStatusUpdate.create(ts, ownerId, parsedAssignee));
}
}
@@ -612,9 +608,9 @@ class ChangeNotesParser {
// exception is the legacy SUBM approval, which is never considered post-submit, but might end
// up sorted after the submit during rebuilding.
if (status == Change.Status.MERGED) {
- for (PatchSetApproval psa : bufferedApprovals) {
- if (!psa.isLegacySubmit()) {
- psa.setPostSubmit(true);
+ for (PatchSetApproval.Builder psa : bufferedApprovals) {
+ if (!psa.key().isLegacySubmit()) {
+ psa.postSubmit(true);
}
}
}
@@ -630,7 +626,7 @@ class ChangeNotesParser {
if (psId == null) {
throw invalidFooter(FOOTER_PATCH_SET, psIdStr);
}
- return new PatchSet.Id(id, psId);
+ return PatchSet.id(id, psId);
}
private PatchSetState parsePatchSetState(ChangeNotesCommit commit) throws ConfigInvalidException {
@@ -658,16 +654,14 @@ class ChangeNotesParser {
List<String> descLines = commit.getFooterLineValues(FOOTER_PATCH_SET_DESCRIPTION);
if (descLines.isEmpty()) {
return;
- } else if (descLines.size() == 1) {
+ }
+
+ checkPatchSetCommitNotParsed(psId, FOOTER_PATCH_SET_DESCRIPTION);
+ if (descLines.size() == 1) {
String desc = descLines.get(0).trim();
- PatchSet ps = patchSets.get(psId);
- if (ps == null) {
- ps = new PatchSet(psId);
- ps.setRevision(PARTIAL_PATCH_SET);
- patchSets.put(psId, ps);
- }
- if (ps.getDescription() == null) {
- ps.setDescription(desc);
+ PatchSet.Builder pending = patchSets.computeIfAbsent(psId, p -> PatchSet.builder());
+ if (!pending.description().isPresent()) {
+ pending.description(Optional.of(desc));
}
} else {
throw expectedOneFooter(FOOTER_PATCH_SET_DESCRIPTION, descLines);
@@ -686,8 +680,7 @@ class ChangeNotesParser {
}
ChangeMessage changeMessage =
- new ChangeMessage(
- new ChangeMessage.Key(psId.getParentKey(), commit.name()), accountId, ts, psId);
+ new ChangeMessage(ChangeMessage.key(psId.changeId(), commit.name()), accountId, ts, psId);
changeMessage.setMessage(changeMsgString.get());
changeMessage.setTag(tag);
changeMessage.setRealAuthor(realAccountId);
@@ -713,24 +706,24 @@ class ChangeNotesParser {
ChangeNotesCommit tipCommit = walk.parseCommit(tip);
revisionNoteMap =
RevisionNoteMap.parse(
- changeNoteJson,
- legacyChangeNoteRead,
- id,
- reader,
- NoteMap.read(reader, tipCommit),
- PatchLineComment.Status.PUBLISHED);
- Map<RevId, ChangeRevisionNote> rns = revisionNoteMap.revisionNotes;
-
- for (Map.Entry<RevId, ChangeRevisionNote> e : rns.entrySet()) {
+ changeNoteJson, reader, NoteMap.read(reader, tipCommit), Comment.Status.PUBLISHED);
+ Map<ObjectId, ChangeRevisionNote> rns = revisionNoteMap.revisionNotes;
+
+ for (Map.Entry<ObjectId, ChangeRevisionNote> e : rns.entrySet()) {
for (Comment c : e.getValue().getEntities()) {
comments.put(e.getKey(), c);
}
}
- for (PatchSet ps : patchSets.values()) {
- ChangeRevisionNote rn = rns.get(ps.getRevision());
+ for (PatchSet.Builder b : patchSets.values()) {
+ ObjectId commitId =
+ b.commitId()
+ .orElseThrow(
+ () ->
+ new IllegalStateException("never parsed commit ID for patch set " + b.id()));
+ ChangeRevisionNote rn = rns.get(commitId);
if (rn != null && rn.getPushCert() != null) {
- ps.setPushCertificate(rn.getPushCert());
+ b.pushCertificate(Optional.of(rn.getPushCert()));
}
}
}
@@ -741,7 +734,7 @@ class ChangeNotesParser {
if (accountId == null) {
throw parseException("patch set %s requires an identified user as uploader", psId.get());
}
- PatchSetApproval psa;
+ PatchSetApproval.Builder psa;
if (line.startsWith("-")) {
psa = parseRemoveApproval(psId, accountId, realAccountId, ts, line);
} else {
@@ -750,7 +743,7 @@ class ChangeNotesParser {
bufferedApprovals.add(psa);
}
- private PatchSetApproval parseAddApproval(
+ private PatchSetApproval.Builder parseAddApproval(
PatchSet.Id psId, Account.Id committerId, Account.Id realAccountId, Timestamp ts, String line)
throws ConfigInvalidException {
// There are potentially 3 accounts involved here:
@@ -772,7 +765,7 @@ class ChangeNotesParser {
labelVoteStr = line.substring(0, s);
PersonIdent ident = RawParseUtils.parsePersonIdent(line.substring(s + 1));
checkFooter(ident != null, FOOTER_LABEL, line);
- effectiveAccountId = legacyChangeNoteRead.parseIdent(ident, id);
+ effectiveAccountId = parseIdent(ident);
} else {
labelVoteStr = line;
effectiveAccountId = committerId;
@@ -787,23 +780,20 @@ class ChangeNotesParser {
throw pe;
}
- PatchSetApproval psa =
- new PatchSetApproval(
- new PatchSetApproval.Key(psId, effectiveAccountId, new LabelId(l.label())),
- l.value(),
- ts);
- psa.setTag(tag);
+ PatchSetApproval.Builder psa =
+ PatchSetApproval.builder()
+ .key(PatchSetApproval.key(psId, effectiveAccountId, LabelId.create(l.label())))
+ .value(l.value())
+ .granted(ts)
+ .tag(Optional.ofNullable(tag));
if (!Objects.equals(realAccountId, committerId)) {
- psa.setRealAccountId(realAccountId);
- }
- ApprovalKey k = ApprovalKey.create(psId, effectiveAccountId, l.label());
- if (!approvals.containsKey(k)) {
- approvals.put(k, psa);
+ psa.realAccountId(realAccountId);
}
+ approvals.putIfAbsent(psa.key(), psa);
return psa;
}
- private PatchSetApproval parseRemoveApproval(
+ private PatchSetApproval.Builder parseRemoveApproval(
PatchSet.Id psId, Account.Id committerId, Account.Id realAccountId, Timestamp ts, String line)
throws ConfigInvalidException {
// See comments in parseAddApproval about the various users involved.
@@ -814,7 +804,7 @@ class ChangeNotesParser {
label = line.substring(1, s);
PersonIdent ident = RawParseUtils.parsePersonIdent(line.substring(s + 1));
checkFooter(ident != null, FOOTER_LABEL, line);
- effectiveAccountId = legacyChangeNoteRead.parseIdent(ident, id);
+ effectiveAccountId = parseIdent(ident);
} else {
label = line.substring(1);
effectiveAccountId = committerId;
@@ -830,16 +820,15 @@ class ChangeNotesParser {
// Store an actual 0-vote approval in the map for a removed approval, because ApprovalCopier
// needs an actual approval in order to block copying an earlier approval over a later delete.
- PatchSetApproval remove =
- new PatchSetApproval(
- new PatchSetApproval.Key(psId, effectiveAccountId, new LabelId(label)), (short) 0, ts);
+ PatchSetApproval.Builder remove =
+ PatchSetApproval.builder()
+ .key(PatchSetApproval.key(psId, effectiveAccountId, LabelId.create(label)))
+ .value(0)
+ .granted(ts);
if (!Objects.equals(realAccountId, committerId)) {
- remove.setRealAccountId(realAccountId);
- }
- ApprovalKey k = ApprovalKey.create(psId, effectiveAccountId, label);
- if (!approvals.containsKey(k)) {
- approvals.put(k, remove);
+ remove.realAccountId(realAccountId);
}
+ approvals.putIfAbsent(remove.key(), remove);
return remove;
}
@@ -874,7 +863,7 @@ class ChangeNotesParser {
label.label = line.substring(c + 2, c2);
PersonIdent ident = RawParseUtils.parsePersonIdent(line.substring(c2 + 2));
checkFooter(ident != null, FOOTER_SUBMITTED_WITH, line);
- label.appliedBy = legacyChangeNoteRead.parseIdent(ident, id);
+ label.appliedBy = parseIdent(ident);
} else {
label.label = line.substring(c + 2);
}
@@ -890,7 +879,7 @@ class ChangeNotesParser {
if (a.getName().equals(c.getName()) && a.getEmailAddress().equals(c.getEmailAddress())) {
return null;
}
- return legacyChangeNoteRead.parseIdent(commit.getAuthorIdent(), id);
+ return parseIdent(commit.getAuthorIdent());
}
private void parseReviewer(Timestamp ts, ReviewerStateInternal state, String line)
@@ -899,7 +888,7 @@ class ChangeNotesParser {
if (ident == null) {
throw invalidFooter(state.getFooterKey(), line);
}
- Account.Id accountId = legacyChangeNoteRead.parseIdent(ident, id);
+ Account.Id accountId = parseIdent(ident);
reviewerUpdates.add(ReviewerStatusUpdate.create(ts, ownerId, accountId, state));
if (!reviewers.containsRow(accountId)) {
reviewers.put(accountId, state, ts);
@@ -976,7 +965,7 @@ class ChangeNotesParser {
if (revertOf == null) {
throw invalidFooter(FOOTER_REVERT_OF, footer);
}
- return new Change.Id(revertOf);
+ return Change.id(revertOf);
}
private void pruneReviewers() {
@@ -1003,13 +992,8 @@ class ChangeNotesParser {
private void updatePatchSetStates() {
Set<PatchSet.Id> missing = new TreeSet<>(comparing(PatchSet.Id::get));
- for (Iterator<PatchSet> it = patchSets.values().iterator(); it.hasNext(); ) {
- PatchSet ps = it.next();
- if (ps.getRevision().equals(PARTIAL_PATCH_SET)) {
- missing.add(ps.getId());
- it.remove();
- }
- }
+ patchSets.keySet().stream().filter(p -> !patchSetCommitParsed(p)).forEach(p -> missing.add(p));
+
for (Map.Entry<PatchSet.Id, PatchSetState> e : patchSetStates.entrySet()) {
switch (e.getValue()) {
case PUBLISHED:
@@ -1030,10 +1014,10 @@ class ChangeNotesParser {
pruneEntitiesForMissingPatchSets(allChangeMessages, ChangeMessage::getPatchSetId, missing);
pruned +=
pruneEntitiesForMissingPatchSets(
- comments.values(), c -> new PatchSet.Id(id, c.key.patchSetId), missing);
+ comments.values(), c -> PatchSet.id(id, c.key.patchSetId), missing);
pruned +=
pruneEntitiesForMissingPatchSets(
- approvals.values(), PatchSetApproval::getPatchSetId, missing);
+ approvals.values(), psa -> psa.key().patchSetId(), missing);
if (!missing.isEmpty()) {
logger.atWarning().log(
@@ -1046,7 +1030,7 @@ class ChangeNotesParser {
int pruned = 0;
for (Iterator<T> it = ents.iterator(); it.hasNext(); ) {
PatchSet.Id psId = psIdFunc.apply(it.next());
- if (!patchSets.containsKey(psId)) {
+ if (!patchSetCommitParsed(psId)) {
pruned++;
missing.add(psId);
it.remove();
@@ -1089,7 +1073,27 @@ class ChangeNotesParser {
}
}
+ private void checkPatchSetCommitNotParsed(PatchSet.Id psId, FooterKey footer)
+ throws ConfigInvalidException {
+ if (patchSetCommitParsed(psId)) {
+ throw parseException(
+ "%s field found for patch set %s before patch set was originally defined",
+ footer.getName(), psId.get());
+ }
+ }
+
+ private boolean patchSetCommitParsed(PatchSet.Id psId) {
+ PatchSet.Builder pending = patchSets.get(psId);
+ return pending != null && pending.commitId().isPresent();
+ }
+
private ConfigInvalidException parseException(String fmt, Object... args) {
return ChangeNotes.parseException(id, fmt, args);
}
+
+ private Account.Id parseIdent(PersonIdent ident) throws ConfigInvalidException {
+ return NoteDbUtil.parseIdent(ident)
+ .orElseThrow(
+ () -> parseException("cannot retrieve account id: %s", ident.getEmailAddress()));
+ }
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index fd260e7449..896cca3ba4 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
@@ -35,26 +34,27 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.converter.ChangeMessageProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetProtoConverter;
+import com.google.gerrit.entities.converter.ProtoConverter;
import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.mail.Address;
import com.google.gerrit.proto.Protos;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.converter.ChangeMessageProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetApprovalProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetProtoConverter;
-import com.google.gerrit.reviewdb.converter.ProtoConverter;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
+import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.AssigneeStatusUpdateProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ChangeColumnsProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
@@ -68,6 +68,7 @@ import com.google.protobuf.MessageLite;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
@@ -97,15 +98,14 @@ public abstract class ChangeNotesState {
Timestamp createdOn,
Timestamp lastUpdatedOn,
Account.Id owner,
+ String serverId,
String branch,
@Nullable PatchSet.Id currentPatchSetId,
String subject,
@Nullable String topic,
@Nullable String originalSubject,
@Nullable String submissionId,
- @Nullable Account.Id assignee,
@Nullable Change.Status status,
- Set<Account.Id> pastAssignees,
Set<String> hashtags,
Map<PatchSet.Id, PatchSet> patchSets,
ListMultimap<PatchSet.Id, PatchSetApproval> approvals,
@@ -115,13 +115,15 @@ public abstract class ChangeNotesState {
ReviewerByEmailSet pendingReviewersByEmail,
List<Account.Id> allPastReviewers,
List<ReviewerStatusUpdate> reviewerUpdates,
+ List<AssigneeStatusUpdate> assigneeUpdates,
List<SubmitRecord> submitRecords,
List<ChangeMessage> changeMessages,
- ListMultimap<RevId, Comment> publishedComments,
+ ListMultimap<ObjectId, Comment> publishedComments,
boolean isPrivate,
boolean workInProgress,
boolean reviewStarted,
- @Nullable Change.Id revertOf) {
+ @Nullable Change.Id revertOf,
+ int updateCount) {
requireNonNull(
metaId,
() ->
@@ -146,14 +148,13 @@ public abstract class ChangeNotesState {
.topic(topic)
.originalSubject(originalSubject)
.submissionId(submissionId)
- .assignee(assignee)
.isPrivate(isPrivate)
.workInProgress(workInProgress)
.reviewStarted(reviewStarted)
.revertOf(revertOf)
.build())
- .pastAssignees(pastAssignees)
.hashtags(hashtags)
+ .serverId(serverId)
.patchSets(patchSets.entrySet())
.approvals(approvals.entries())
.reviewers(reviewers)
@@ -162,9 +163,11 @@ public abstract class ChangeNotesState {
.pendingReviewersByEmail(pendingReviewersByEmail)
.allPastReviewers(allPastReviewers)
.reviewerUpdates(reviewerUpdates)
+ .assigneeUpdates(assigneeUpdates)
.submitRecords(submitRecords)
.changeMessages(changeMessages)
.publishedComments(publishedComments)
+ .updateCount(updateCount)
.build();
}
@@ -208,9 +211,6 @@ public abstract class ChangeNotesState {
@Nullable
abstract String submissionId();
- @Nullable
- abstract Account.Id assignee();
-
abstract boolean isPrivate();
abstract boolean workInProgress();
@@ -244,8 +244,6 @@ public abstract class ChangeNotesState {
abstract Builder submissionId(@Nullable String submissionId);
- abstract Builder assignee(@Nullable Account.Id assignee);
-
abstract Builder status(@Nullable Change.Status status);
abstract Builder isPrivate(boolean isPrivate);
@@ -271,10 +269,11 @@ public abstract class ChangeNotesState {
abstract ChangeColumns columns();
// Other related to this Change.
- abstract ImmutableSet<Account.Id> pastAssignees();
-
abstract ImmutableSet<String> hashtags();
+ @Nullable
+ abstract String serverId();
+
abstract ImmutableList<Map.Entry<PatchSet.Id, PatchSet>> patchSets();
abstract ImmutableList<Map.Entry<PatchSet.Id, PatchSetApproval>> approvals();
@@ -291,11 +290,15 @@ public abstract class ChangeNotesState {
abstract ImmutableList<ReviewerStatusUpdate> reviewerUpdates();
+ abstract ImmutableList<AssigneeStatusUpdate> assigneeUpdates();
+
abstract ImmutableList<SubmitRecord> submitRecords();
abstract ImmutableList<ChangeMessage> changeMessages();
- abstract ImmutableListMultimap<RevId, Comment> publishedComments();
+ abstract ImmutableListMultimap<ObjectId, Comment> publishedComments();
+
+ abstract int updateCount();
Change newChange(Project.NameKey project) {
ChangeColumns c = requireNonNull(columns(), "columns are required");
@@ -304,7 +307,7 @@ public abstract class ChangeNotesState {
c.changeKey(),
changeId(),
c.owner(),
- new Branch.NameKey(project, c.branch()),
+ BranchNameKey.create(project, c.branch()),
c.createdOn());
copyNonConstructorColumnsTo(change);
return change;
@@ -318,7 +321,7 @@ public abstract class ChangeNotesState {
this);
change.setKey(c.changeKey());
change.setOwner(c.owner());
- change.setDest(new Branch.NameKey(change.getProject(), c.branch()));
+ change.setDest(BranchNameKey.create(change.getProject(), c.branch()));
change.setCreatedOn(c.createdOn());
copyNonConstructorColumnsTo(change);
}
@@ -331,7 +334,9 @@ public abstract class ChangeNotesState {
change.setTopic(Strings.emptyToNull(c.topic()));
change.setLastUpdatedOn(c.lastUpdatedOn());
change.setSubmissionId(c.submissionId());
- change.setAssignee(c.assignee());
+ if (!assigneeUpdates().isEmpty()) {
+ change.setAssignee(assigneeUpdates().get(0).currentAssignee().orElse(null));
+ }
change.setPrivate(c.isPrivate());
change.setWorkInProgress(c.workInProgress());
change.setReviewStarted(c.reviewStarted());
@@ -351,7 +356,6 @@ public abstract class ChangeNotesState {
static Builder empty(Change.Id changeId) {
return new AutoValue_ChangeNotesState.Builder()
.changeId(changeId)
- .pastAssignees(ImmutableSet.of())
.hashtags(ImmutableSet.of())
.patchSets(ImmutableList.of())
.approvals(ImmutableList.of())
@@ -361,9 +365,11 @@ public abstract class ChangeNotesState {
.pendingReviewersByEmail(ReviewerByEmailSet.empty())
.allPastReviewers(ImmutableList.of())
.reviewerUpdates(ImmutableList.of())
+ .assigneeUpdates(ImmutableList.of())
.submitRecords(ImmutableList.of())
.changeMessages(ImmutableList.of())
- .publishedComments(ImmutableListMultimap.of());
+ .publishedComments(ImmutableListMultimap.of())
+ .updateCount(0);
}
abstract Builder metaId(ObjectId metaId);
@@ -372,7 +378,7 @@ public abstract class ChangeNotesState {
abstract Builder columns(ChangeColumns columns);
- abstract Builder pastAssignees(Set<Account.Id> pastAssignees);
+ abstract Builder serverId(String serverId);
abstract Builder hashtags(Iterable<String> hashtags);
@@ -392,11 +398,15 @@ public abstract class ChangeNotesState {
abstract Builder reviewerUpdates(List<ReviewerStatusUpdate> reviewerUpdates);
+ abstract Builder assigneeUpdates(List<AssigneeStatusUpdate> assigneeUpdates);
+
abstract Builder submitRecords(List<SubmitRecord> submitRecords);
abstract Builder changeMessages(List<ChangeMessage> changeMessages);
- abstract Builder publishedComments(ListMultimap<RevId, Comment> publishedComments);
+ abstract Builder publishedComments(ListMultimap<ObjectId, Comment> publishedComments);
+
+ abstract Builder updateCount(int updateCount);
abstract ChangeNotesState build();
}
@@ -421,7 +431,10 @@ public abstract class ChangeNotesState {
.setChangeId(object.changeId().get())
.setColumns(toChangeColumnsProto(object.columns()));
- object.pastAssignees().forEach(a -> b.addPastAssignee(a.get()));
+ if (object.serverId() != null) {
+ b.setServerId(object.serverId());
+ b.setHasServerId(true);
+ }
object.hashtags().forEach(b::addHashtag);
object
.patchSets()
@@ -452,6 +465,7 @@ public abstract class ChangeNotesState {
object.allPastReviewers().forEach(a -> b.addPastReviewer(a.get()));
object.reviewerUpdates().forEach(u -> b.addReviewerUpdate(toReviewerStatusUpdateProto(u)));
+ object.assigneeUpdates().forEach(u -> b.addAssigneeUpdate(toAssigneeStatusUpdateProto(u)));
object
.submitRecords()
.forEach(r -> b.addSubmitRecord(GSON.toJson(new StoredSubmitRecord(r))));
@@ -459,6 +473,7 @@ public abstract class ChangeNotesState {
.changeMessages()
.forEach(m -> b.addChangeMessage(toByteString(m, ChangeMessageProtoConverter.INSTANCE)));
object.publishedComments().values().forEach(c -> b.addPublishedComment(GSON.toJson(c)));
+ b.setUpdateCount(object.updateCount());
return Protos.toByteArray(b.build());
}
@@ -490,9 +505,6 @@ public abstract class ChangeNotesState {
if (cols.submissionId() != null) {
b.setSubmissionId(cols.submissionId()).setHasSubmissionId(true);
}
- if (cols.assignee() != null) {
- b.setAssignee(cols.assignee().get()).setHasAssignee(true);
- }
if (cols.status() != null) {
b.setStatus(STATUS_CONVERTER.reverse().convert(cols.status())).setHasStatus(true);
}
@@ -532,40 +544,47 @@ public abstract class ChangeNotesState {
.build();
}
+ private static AssigneeStatusUpdateProto toAssigneeStatusUpdateProto(AssigneeStatusUpdate u) {
+ AssigneeStatusUpdateProto.Builder builder =
+ AssigneeStatusUpdateProto.newBuilder()
+ .setDate(u.date().getTime())
+ .setUpdatedBy(u.updatedBy().get())
+ .setHasCurrentAssignee(u.currentAssignee().isPresent());
+
+ u.currentAssignee().ifPresent(assignee -> builder.setCurrentAssignee(assignee.get()));
+ return builder.build();
+ }
+
@Override
public ChangeNotesState deserialize(byte[] in) {
ChangeNotesStateProto proto = Protos.parseUnchecked(ChangeNotesStateProto.parser(), in);
- Change.Id changeId = new Change.Id(proto.getChangeId());
+ Change.Id changeId = Change.id(proto.getChangeId());
ChangeNotesState.Builder b =
builder()
.metaId(ObjectIdConverter.create().fromByteString(proto.getMetaId()))
.changeId(changeId)
.columns(toChangeColumns(changeId, proto.getColumns()))
- .pastAssignees(
- proto.getPastAssigneeList().stream()
- .map(Account.Id::new)
- .collect(toImmutableSet()))
+ .serverId(proto.getHasServerId() ? proto.getServerId() : null)
.hashtags(proto.getHashtagList())
.patchSets(
proto.getPatchSetList().stream()
.map(bytes -> parseProtoFrom(PatchSetProtoConverter.INSTANCE, bytes))
- .map(ps -> Maps.immutableEntry(ps.getId(), ps))
+ .map(ps -> Maps.immutableEntry(ps.id(), ps))
.collect(toImmutableList()))
.approvals(
proto.getApprovalList().stream()
.map(bytes -> parseProtoFrom(PatchSetApprovalProtoConverter.INSTANCE, bytes))
- .map(a -> Maps.immutableEntry(a.getPatchSetId(), a))
+ .map(a -> Maps.immutableEntry(a.patchSetId(), a))
.collect(toImmutableList()))
.reviewers(toReviewerSet(proto.getReviewerList()))
.reviewersByEmail(toReviewerByEmailSet(proto.getReviewerByEmailList()))
.pendingReviewers(toReviewerSet(proto.getPendingReviewerList()))
.pendingReviewersByEmail(toReviewerByEmailSet(proto.getPendingReviewerByEmailList()))
.allPastReviewers(
- proto.getPastReviewerList().stream()
- .map(Account.Id::new)
- .collect(toImmutableList()))
+ proto.getPastReviewerList().stream().map(Account::id).collect(toImmutableList()))
.reviewerUpdates(toReviewerStatusUpdateList(proto.getReviewerUpdateList()))
+ .assigneeUpdates(toAssigneeStatusUpdateList(proto.getAssigneeUpdateList()))
.submitRecords(
proto.getSubmitRecordList().stream()
.map(r -> GSON.fromJson(r, StoredSubmitRecord.class).toSubmitRecord())
@@ -577,7 +596,8 @@ public abstract class ChangeNotesState {
.publishedComments(
proto.getPublishedCommentList().stream()
.map(r -> GSON.fromJson(r, Comment.class))
- .collect(toImmutableListMultimap(c -> new RevId(c.revId), c -> c)));
+ .collect(toImmutableListMultimap(Comment::getCommitId, c -> c)))
+ .updateCount(proto.getUpdateCount());
return b.build();
}
@@ -590,13 +610,13 @@ public abstract class ChangeNotesState {
private static ChangeColumns toChangeColumns(Change.Id changeId, ChangeColumnsProto proto) {
ChangeColumns.Builder b =
ChangeColumns.builder()
- .changeKey(new Change.Key(proto.getChangeKey()))
+ .changeKey(Change.key(proto.getChangeKey()))
.createdOn(new Timestamp(proto.getCreatedOn()))
.lastUpdatedOn(new Timestamp(proto.getLastUpdatedOn()))
- .owner(new Account.Id(proto.getOwner()))
+ .owner(Account.id(proto.getOwner()))
.branch(proto.getBranch());
if (proto.getHasCurrentPatchSetId()) {
- b.currentPatchSetId(new PatchSet.Id(changeId, proto.getCurrentPatchSetId()));
+ b.currentPatchSetId(PatchSet.id(changeId, proto.getCurrentPatchSetId()));
}
b.subject(proto.getSubject());
if (proto.getHasTopic()) {
@@ -608,9 +628,6 @@ public abstract class ChangeNotesState {
if (proto.getHasSubmissionId()) {
b.submissionId(proto.getSubmissionId());
}
- if (proto.getHasAssignee()) {
- b.assignee(new Account.Id(proto.getAssignee()));
- }
if (proto.getHasStatus()) {
b.status(STATUS_CONVERTER.convert(proto.getStatus()));
}
@@ -618,7 +635,7 @@ public abstract class ChangeNotesState {
.workInProgress(proto.getWorkInProgress())
.reviewStarted(proto.getReviewStarted());
if (proto.getHasRevertOf()) {
- b.revertOf(new Change.Id(proto.getRevertOf()));
+ b.revertOf(Change.id(proto.getRevertOf()));
}
return b.build();
}
@@ -629,7 +646,7 @@ public abstract class ChangeNotesState {
for (ReviewerSetEntryProto e : protos) {
b.put(
REVIEWER_STATE_CONVERTER.convert(e.getState()),
- new Account.Id(e.getAccountId()),
+ Account.id(e.getAccountId()),
new Timestamp(e.getTimestamp()));
}
return ReviewerSet.fromTable(b.build());
@@ -655,11 +672,26 @@ public abstract class ChangeNotesState {
b.add(
ReviewerStatusUpdate.create(
new Timestamp(proto.getDate()),
- new Account.Id(proto.getUpdatedBy()),
- new Account.Id(proto.getReviewer()),
+ Account.id(proto.getUpdatedBy()),
+ Account.id(proto.getReviewer()),
REVIEWER_STATE_CONVERTER.convert(proto.getState())));
}
return b.build();
}
+
+ private static ImmutableList<AssigneeStatusUpdate> toAssigneeStatusUpdateList(
+ List<AssigneeStatusUpdateProto> protos) {
+ ImmutableList.Builder<AssigneeStatusUpdate> b = ImmutableList.builder();
+ for (AssigneeStatusUpdateProto proto : protos) {
+ b.add(
+ AssigneeStatusUpdate.create(
+ new Timestamp(proto.getDate()),
+ Account.id(proto.getUpdatedBy()),
+ proto.getHasCurrentAssignee()
+ ? Optional.of(Account.id(proto.getCurrentAssignee()))
+ : Optional.empty()));
+ }
+ return b.build();
+ }
}
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java b/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
index 66dd5e805a..b6443f15b9 100644
--- a/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
+++ b/java/com/google/gerrit/server/notedb/ChangeRevisionNote.java
@@ -16,10 +16,7 @@ package com.google.gerrit.server.notedb;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.common.primitives.Bytes;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.entities.Comment;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -30,30 +27,16 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.util.MutableInteger;
-import org.eclipse.jgit.util.RawParseUtils;
class ChangeRevisionNote extends RevisionNote<Comment> {
- private static final byte[] CERT_HEADER = "certificate version ".getBytes(UTF_8);
- // See org.eclipse.jgit.transport.PushCertificateParser.END_SIGNATURE
- private static final byte[] END_SIGNATURE = "-----END PGP SIGNATURE-----\n".getBytes(UTF_8);
-
private final ChangeNoteJson noteJson;
- private final LegacyChangeNoteRead legacyChangeNoteRead;
- private final Change.Id changeId;
- private final PatchLineComment.Status status;
+ private final Comment.Status status;
private String pushCert;
ChangeRevisionNote(
- ChangeNoteJson noteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
- Change.Id changeId,
- ObjectReader reader,
- ObjectId noteId,
- PatchLineComment.Status status) {
+ ChangeNoteJson noteJson, ObjectReader reader, ObjectId noteId, Comment.Status status) {
super(reader, noteId);
- this.legacyChangeNoteRead = legacyChangeNoteRead;
this.noteJson = noteJson;
- this.changeId = changeId;
this.status = status;
}
@@ -67,29 +50,13 @@ class ChangeRevisionNote extends RevisionNote<Comment> {
MutableInteger p = new MutableInteger();
p.value = offset;
- if (isJson(raw, p.value)) {
- RevisionNoteData data = parseJson(noteJson, raw, p.value);
- if (status == PatchLineComment.Status.PUBLISHED) {
- pushCert = data.pushCert;
- } else {
- pushCert = null;
- }
- return data.comments;
- }
-
- if (status == PatchLineComment.Status.PUBLISHED) {
- pushCert = parsePushCert(changeId, raw, p);
- trimLeadingEmptyLines(raw, p);
+ RevisionNoteData data = parseJson(noteJson, raw, p.value);
+ if (status == Comment.Status.PUBLISHED) {
+ pushCert = data.pushCert;
} else {
pushCert = null;
}
- List<Comment> comments = legacyChangeNoteRead.parseNote(raw, p, changeId);
- comments.forEach(c -> c.legacyFormat = true);
- return comments;
- }
-
- static boolean isJson(byte[] raw, int offset) {
- return raw[offset] == '{' || raw[offset] == '[';
+ return data.comments;
}
private RevisionNoteData parseJson(ChangeNoteJson noteUtil, byte[] raw, int offset)
@@ -99,18 +66,4 @@ class ChangeRevisionNote extends RevisionNote<Comment> {
return noteUtil.getGson().fromJson(r, RevisionNoteData.class);
}
}
-
- private static String parsePushCert(Change.Id changeId, byte[] bytes, MutableInteger p)
- throws ConfigInvalidException {
- if (RawParseUtils.match(bytes, p.value, CERT_HEADER) < 0) {
- return null;
- }
- int end = Bytes.indexOf(bytes, END_SIGNATURE);
- if (end < 0) {
- throw ChangeNotes.parseException(changeId, "invalid push certificate in note");
- }
- int start = p.value;
- p.value = end + END_SIGNATURE.length;
- return new String(bytes, start, p.value, UTF_8);
- }
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 0cde36350b..b2f85fcf62 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.notedb;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ASSIGNEE;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
@@ -39,7 +39,7 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_TAG;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_TOPIC;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_WORK_IN_PROGRESS;
import static com.google.gerrit.server.notedb.NoteDbUtil.sanitizeFooter;
-import static java.util.Comparator.comparing;
+import static java.util.Comparator.naturalOrder;
import static java.util.Objects.requireNonNull;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -50,21 +50,18 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.LabelVote;
-import com.google.gwtorm.client.IntKey;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -173,7 +170,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private static Table<String, Account.Id, Optional<Short>> approvals(
Comparator<String> nameComparator) {
- return TreeBasedTable.create(nameComparator, comparing(IntKey::get));
+ return TreeBasedTable.create(nameComparator, naturalOrder());
}
@AssistedInject
@@ -250,11 +247,6 @@ public class ChangeUpdate extends AbstractChangeUpdate {
checkArgument(!this.submitRecords.isEmpty(), "no submit records specified at submit time");
}
- @Deprecated // Only until we improve ChangeRebuilder to call merge().
- public void setSubmissionId(String submissionId) {
- this.submissionId = submissionId;
- }
-
public void setSubjectForCommit(String commitSubject) {
this.commitSubject = commitSubject;
}
@@ -280,18 +272,14 @@ public class ChangeUpdate extends AbstractChangeUpdate {
this.psDescription = psDescription;
}
- public void putComment(PatchLineComment.Status status, Comment c) {
+ public void putComment(Comment.Status status, Comment c) {
verifyComment(c);
createDraftUpdateIfNull();
- if (status == PatchLineComment.Status.DRAFT) {
+ if (status == Comment.Status.DRAFT) {
draftUpdate.putComment(c);
} else {
comments.add(c);
- // Always delete the corresponding comment from drafts. Published comments
- // are immutable, meaning in normal operation we only hit this path when
- // publishing a comment. It's exactly in that case that we have to delete
- // the draft.
- draftUpdate.deleteComment(c);
+ draftUpdate.markCommentPublished(c);
}
}
@@ -421,7 +409,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
}
public void setRevertOf(int revertOf) {
- int ownId = getChange().getId().get();
+ int ownId = getId().get();
checkArgument(ownId != revertOf, "A change cannot revert itself");
this.revertOf = revertOf;
rootOnly = true;
@@ -438,18 +426,18 @@ public class ChangeUpdate extends AbstractChangeUpdate {
RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
for (Comment c : comments) {
c.tag = tag;
- cache.get(new RevId(c.revId)).putComment(c);
+ cache.get(c.getCommitId()).putComment(c);
}
if (pushCert != null) {
checkState(commit != null);
- cache.get(new RevId(commit)).setPushCertificate(pushCert);
+ cache.get(ObjectId.fromString(commit)).setPushCertificate(pushCert);
}
- Map<RevId, RevisionNoteBuilder> builders = cache.getBuilders();
+ Map<ObjectId, RevisionNoteBuilder> builders = cache.getBuilders();
checkComments(rnm.revisionNotes, builders);
- for (Map.Entry<RevId, RevisionNoteBuilder> e : builders.entrySet()) {
+ for (Map.Entry<ObjectId, RevisionNoteBuilder> e : builders.entrySet()) {
ObjectId data = inserter.insert(OBJ_BLOB, e.getValue().build(noteUtil.getChangeNoteJson()));
- rnm.noteMap.set(ObjectId.fromString(e.getKey().get()), data);
+ rnm.noteMap.set(e.getKey(), data);
}
return rnm.noteMap.writeTree(inserter);
@@ -473,16 +461,12 @@ public class ChangeUpdate extends AbstractChangeUpdate {
// Even though reading from changes might not be enabled, we need to
// parse any existing revision notes so we can merge them.
return RevisionNoteMap.parse(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- getId(),
- rw.getObjectReader(),
- noteMap,
- PatchLineComment.Status.PUBLISHED);
+ noteUtil.getChangeNoteJson(), rw.getObjectReader(), noteMap, Comment.Status.PUBLISHED);
}
private void checkComments(
- Map<RevId, ChangeRevisionNote> existingNotes, Map<RevId, RevisionNoteBuilder> toUpdate) {
+ Map<ObjectId, ChangeRevisionNote> existingNotes,
+ Map<ObjectId, RevisionNoteBuilder> toUpdate) {
// Prohibit various kinds of illegal operations on comments.
Set<Comment.Key> existing = new HashSet<>();
for (ChangeRevisionNote rn : existingNotes.values()) {
@@ -504,7 +488,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
// separate commit. But note that we don't care much about the commit
// graph of the draft ref, particularly because the ref is completely
// deleted when all drafts are gone.
- draftUpdate.deleteComment(c.revId, c.key);
+ draftUpdate.deleteComment(c.getCommitId(), c.key);
}
}
}
@@ -524,6 +508,12 @@ public class ChangeUpdate extends AbstractChangeUpdate {
}
@Override
+ protected boolean bypassMaxUpdates() {
+ // Allow abandoning or submitting a change even if it would exceed the max update count.
+ return status != null && status.isClosed();
+ }
+
+ @Override
protected CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
throws IOException {
checkState(
diff --git a/java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java b/java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java
index 8fb28e1bc9..b555fdbf29 100644
--- a/java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java
+++ b/java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java
@@ -15,13 +15,13 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.parseCommitMessageRange;
+import static java.util.Objects.requireNonNull;
import static org.eclipse.jgit.util.RawParseUtils.decode;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.RefNames;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Optional;
@@ -46,7 +46,7 @@ public class DeleteChangeMessageRewriter implements NoteDbRewriter {
DeleteChangeMessageRewriter(Change.Id changeId, String targetMessageId, String newChangeMessage) {
this.changeId = changeId;
- this.targetMessageId = checkNotNull(targetMessageId);
+ this.targetMessageId = requireNonNull(targetMessageId);
this.newChangeMessage = newChangeMessage;
}
diff --git a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
index c100550f90..9c8b369ee8 100644
--- a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
+++ b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
@@ -15,16 +15,15 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.reviewdb.client.PatchLineComment.Status.PUBLISHED;
+import static com.google.gerrit.entities.Comment.Status;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.RefNames;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -96,13 +95,13 @@ public class DeleteCommentRewriter implements NoteDbRewriter {
ObjectReader reader = revWalk.getObjectReader();
RevCommit newTipCommit = revWalk.next(); // The first commit will not be rewritten.
Map<String, Comment> parentComments =
- getPublishedComments(noteUtil, changeId, reader, NoteMap.read(reader, newTipCommit));
+ getPublishedComments(noteUtil, reader, NoteMap.read(reader, newTipCommit));
boolean rewrite = false;
RevCommit originalCommit;
while ((originalCommit = revWalk.next()) != null) {
NoteMap noteMap = NoteMap.read(reader, originalCommit);
- Map<String, Comment> currComments = getPublishedComments(noteUtil, changeId, reader, noteMap);
+ Map<String, Comment> currComments = getPublishedComments(noteUtil, reader, noteMap);
if (!rewrite && currComments.containsKey(uuid)) {
rewrite = true;
@@ -132,28 +131,18 @@ public class DeleteCommentRewriter implements NoteDbRewriter {
*/
@VisibleForTesting
public static Map<String, Comment> getPublishedComments(
- ChangeNoteJson changeNoteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
- Change.Id changeId,
- ObjectReader reader,
- NoteMap noteMap)
+ ChangeNoteJson changeNoteJson, ObjectReader reader, NoteMap noteMap)
throws IOException, ConfigInvalidException {
- return RevisionNoteMap.parse(
- changeNoteJson, legacyChangeNoteRead, changeId, reader, noteMap, PUBLISHED)
- .revisionNotes.values().stream()
+ return RevisionNoteMap.parse(changeNoteJson, reader, noteMap, Status.PUBLISHED).revisionNotes
+ .values().stream()
.flatMap(n -> n.getEntities().stream())
.collect(toMap(c -> c.key.uuid, Function.identity()));
}
public static Map<String, Comment> getPublishedComments(
- ChangeNoteUtil noteUtil, Change.Id changeId, ObjectReader reader, NoteMap noteMap)
+ ChangeNoteUtil noteUtil, ObjectReader reader, NoteMap noteMap)
throws IOException, ConfigInvalidException {
- return getPublishedComments(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- changeId,
- reader,
- noteMap);
+ return getPublishedComments(noteUtil.getChangeNoteJson(), reader, noteMap);
}
/**
* Gets the comments put in by the current commit. The message of the target comment will be
@@ -216,24 +205,22 @@ public class DeleteCommentRewriter implements NoteDbRewriter {
RevisionNoteMap<ChangeRevisionNote> revNotesMap =
RevisionNoteMap.parse(
noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- changeId,
reader,
NoteMap.read(reader, parentCommit),
- PUBLISHED);
+ Status.PUBLISHED);
RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(revNotesMap);
for (Comment c : putInComments) {
- cache.get(new RevId(c.revId)).putComment(c);
+ cache.get(c.getCommitId()).putComment(c);
}
for (Comment c : deletedComments) {
- cache.get(new RevId(c.revId)).deleteComment(c.key);
+ cache.get(c.getCommitId()).deleteComment(c.key);
}
- Map<RevId, RevisionNoteBuilder> builders = cache.getBuilders();
- for (Map.Entry<RevId, RevisionNoteBuilder> entry : builders.entrySet()) {
- ObjectId objectId = ObjectId.fromString(entry.getKey().get());
+ Map<ObjectId, RevisionNoteBuilder> builders = cache.getBuilders();
+ for (Map.Entry<ObjectId, RevisionNoteBuilder> entry : builders.entrySet()) {
+ ObjectId objectId = entry.getKey();
byte[] data = entry.getValue().build(noteUtil.getChangeNoteJson());
if (data.length == 0) {
revNotesMap.noteMap.remove(objectId);
diff --git a/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java b/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
index 495ac659dd..128e185bb6 100644
--- a/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
+++ b/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
@@ -18,8 +18,8 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index 213613ec9f..3966396961 100644
--- a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.reviewdb.client.RefNames.refsDraftComments;
+import static com.google.gerrit.entities.RefNames.refsDraftComments;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
@@ -24,12 +24,10 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Project;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -52,7 +50,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
private final Account.Id author;
private final Ref ref;
- private ImmutableListMultimap<RevId, Comment> comments;
+ private ImmutableListMultimap<ObjectId, Comment> comments;
private RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
@AssistedInject
@@ -82,7 +80,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
return author;
}
- public ImmutableListMultimap<RevId, Comment> getComments() {
+ public ImmutableListMultimap<ObjectId, Comment> getComments() {
return comments;
}
@@ -122,16 +120,11 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
ObjectReader reader = handle.walk().getObjectReader();
revisionNoteMap =
RevisionNoteMap.parse(
- args.changeNoteJson,
- args.legacyChangeNoteRead,
- getChangeId(),
- reader,
- NoteMap.read(reader, tipCommit),
- PatchLineComment.Status.DRAFT);
- ListMultimap<RevId, Comment> cs = MultimapBuilder.hashKeys().arrayListValues().build();
+ args.changeNoteJson, reader, NoteMap.read(reader, tipCommit), Comment.Status.DRAFT);
+ ListMultimap<ObjectId, Comment> cs = MultimapBuilder.hashKeys().arrayListValues().build();
for (ChangeRevisionNote rn : revisionNoteMap.revisionNotes.values()) {
for (Comment c : rn.getEntities()) {
- cs.put(new RevId(c.revId), c);
+ cs.put(c.getCommitId(), c);
}
}
comments = ImmutableListMultimap.copyOf(cs);
diff --git a/java/com/google/gerrit/server/notedb/IntBlob.java b/java/com/google/gerrit/server/notedb/IntBlob.java
index cbc933cbc7..5efc2b0c5a 100644
--- a/java/com/google/gerrit/server/notedb/IntBlob.java
+++ b/java/com/google/gerrit/server/notedb/IntBlob.java
@@ -23,9 +23,9 @@ import com.google.common.base.CharMatcher;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import java.io.IOException;
import java.util.Optional;
diff --git a/java/com/google/gerrit/server/notedb/InvalidServerIdException.java b/java/com/google/gerrit/server/notedb/InvalidServerIdException.java
new file mode 100644
index 0000000000..f79e07c8cd
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/InvalidServerIdException.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.notedb;
+
+public class InvalidServerIdException extends IllegalStateException {
+ private static final long serialVersionUID = 5302751510361680907L;
+
+ public InvalidServerIdException(String expectedServerId, String actualServerId) {
+ super(
+ String.format(
+ "invalid server id, expected %s: actual: %s", expectedServerId, actualServerId));
+ }
+}
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java
deleted file mode 100644
index 916cc16d79..0000000000
--- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java
+++ /dev/null
@@ -1,401 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb;
-
-import static com.google.gerrit.server.notedb.ChangeNotes.parseException;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.CommentRange;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.inject.Inject;
-import java.sql.Timestamp;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.util.GitDateParser;
-import org.eclipse.jgit.util.MutableInteger;
-import org.eclipse.jgit.util.QuotedString;
-import org.eclipse.jgit.util.RawParseUtils;
-
-public class LegacyChangeNoteRead {
- private final String serverId;
-
- @Inject
- public LegacyChangeNoteRead(@GerritServerId String serverId) {
- this.serverId = serverId;
- }
-
- public Account.Id parseIdent(PersonIdent ident, Change.Id changeId)
- throws ConfigInvalidException {
- return NoteDbUtil.parseIdent(ident, serverId)
- .orElseThrow(
- () ->
- parseException(
- changeId,
- "invalid identity, expected <id>@%s: %s",
- serverId,
- ident.getEmailAddress()));
- }
-
- private static boolean match(byte[] note, MutableInteger p, byte[] expected) {
- int m = RawParseUtils.match(note, p.value, expected);
- return m == p.value + expected.length;
- }
-
- public List<Comment> parseNote(byte[] note, MutableInteger p, Change.Id changeId)
- throws ConfigInvalidException {
- if (p.value >= note.length) {
- return ImmutableList.of();
- }
- Set<Comment.Key> seen = new HashSet<>();
- List<Comment> result = new ArrayList<>();
- int sizeOfNote = note.length;
- byte[] psb = ChangeNoteUtil.PATCH_SET.getBytes(UTF_8);
- byte[] bpsb = ChangeNoteUtil.BASE_PATCH_SET.getBytes(UTF_8);
- byte[] bpn = ChangeNoteUtil.PARENT_NUMBER.getBytes(UTF_8);
-
- RevId revId = new RevId(parseStringField(note, p, changeId, ChangeNoteUtil.REVISION));
- String fileName = null;
- PatchSet.Id psId = null;
- boolean isForBase = false;
- Integer parentNumber = null;
-
- while (p.value < sizeOfNote) {
- boolean matchPs = match(note, p, psb);
- boolean matchBase = match(note, p, bpsb);
- if (matchPs) {
- fileName = null;
- psId = parsePsId(note, p, changeId, ChangeNoteUtil.PATCH_SET);
- isForBase = false;
- } else if (matchBase) {
- fileName = null;
- psId = parsePsId(note, p, changeId, ChangeNoteUtil.BASE_PATCH_SET);
- isForBase = true;
- if (match(note, p, bpn)) {
- parentNumber = parseParentNumber(note, p, changeId);
- }
- } else if (psId == null) {
- throw parseException(
- changeId,
- "missing %s or %s header",
- ChangeNoteUtil.PATCH_SET,
- ChangeNoteUtil.BASE_PATCH_SET);
- }
-
- Comment c = parseComment(note, p, fileName, psId, revId, isForBase, parentNumber);
- fileName = c.key.filename;
- if (!seen.add(c.key)) {
- throw parseException(changeId, "multiple comments for %s in note", c.key);
- }
- result.add(c);
- }
- return result;
- }
-
- private Comment parseComment(
- byte[] note,
- MutableInteger curr,
- String currentFileName,
- PatchSet.Id psId,
- RevId revId,
- boolean isForBase,
- Integer parentNumber)
- throws ConfigInvalidException {
- Change.Id changeId = psId.getParentKey();
-
- // Check if there is a new file.
- boolean newFile =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.FILE.getBytes(UTF_8))) != -1;
- if (newFile) {
- // If so, parse the new file name.
- currentFileName = parseFilename(note, curr, changeId);
- } else if (currentFileName == null) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.FILE);
- }
-
- CommentRange range = parseCommentRange(note, curr);
- if (range == null) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.COMMENT_RANGE);
- }
-
- Timestamp commentTime = parseTimestamp(note, curr, changeId);
- Account.Id aId = parseAuthor(note, curr, changeId, ChangeNoteUtil.AUTHOR);
- boolean hasRealAuthor =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.REAL_AUTHOR.getBytes(UTF_8))) != -1;
- Account.Id raId = null;
- if (hasRealAuthor) {
- raId = parseAuthor(note, curr, changeId, ChangeNoteUtil.REAL_AUTHOR);
- }
-
- boolean hasParent =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.PARENT.getBytes(UTF_8))) != -1;
- String parentUUID = null;
- boolean unresolved = false;
- if (hasParent) {
- parentUUID = parseStringField(note, curr, changeId, ChangeNoteUtil.PARENT);
- }
- boolean hasUnresolved =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.UNRESOLVED.getBytes(UTF_8))) != -1;
- if (hasUnresolved) {
- unresolved = parseBooleanField(note, curr, changeId, ChangeNoteUtil.UNRESOLVED);
- }
-
- String uuid = parseStringField(note, curr, changeId, ChangeNoteUtil.UUID);
-
- boolean hasTag =
- (RawParseUtils.match(note, curr.value, ChangeNoteUtil.TAG.getBytes(UTF_8))) != -1;
- String tag = null;
- if (hasTag) {
- tag = parseStringField(note, curr, changeId, ChangeNoteUtil.TAG);
- }
-
- int commentLength = parseCommentLength(note, curr, changeId);
-
- String message = RawParseUtils.decode(UTF_8, note, curr.value, curr.value + commentLength);
- checkResult(message, "message contents", changeId);
-
- Comment c =
- new Comment(
- new Comment.Key(uuid, currentFileName, psId.get()),
- aId,
- commentTime,
- isForBase ? (short) (parentNumber == null ? 0 : -parentNumber) : (short) 1,
- message,
- serverId,
- unresolved);
- c.lineNbr = range.getEndLine();
- c.parentUuid = parentUUID;
- c.tag = tag;
- c.setRevId(revId);
- if (raId != null) {
- c.setRealAuthor(raId);
- }
-
- if (range.getStartCharacter() != -1) {
- c.setRange(range);
- }
-
- curr.value = RawParseUtils.nextLF(note, curr.value + commentLength);
- curr.value = RawParseUtils.nextLF(note, curr.value);
- return c;
- }
-
- private static String parseStringField(
- byte[] note, MutableInteger curr, Change.Id changeId, String fieldName)
- throws ConfigInvalidException {
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- checkHeaderLineFormat(note, curr, fieldName, changeId);
- int startOfField = RawParseUtils.endOfFooterLineKey(note, curr.value) + 2;
- curr.value = endOfLine;
- return RawParseUtils.decode(UTF_8, note, startOfField, endOfLine - 1);
- }
-
- /**
- * @return a comment range. If the comment range line in the note only has one number, we return a
- * CommentRange with that one number as the end line and the other fields as -1. If the
- * comment range line in the note contains a whole comment range, then we return a
- * CommentRange with all fields set. If the line is not correctly formatted, return null.
- */
- private static CommentRange parseCommentRange(byte[] note, MutableInteger ptr) {
- CommentRange range = new CommentRange(-1, -1, -1, -1);
-
- int last = ptr.value;
- int startLine = RawParseUtils.parseBase10(note, ptr.value, ptr);
- if (ptr.value == last) {
- return null;
- } else if (note[ptr.value] == '\n') {
- range.setEndLine(startLine);
- ptr.value += 1;
- return range;
- } else if (note[ptr.value] == ':') {
- range.setStartLine(startLine);
- ptr.value += 1;
- } else {
- return null;
- }
-
- last = ptr.value;
- int startChar = RawParseUtils.parseBase10(note, ptr.value, ptr);
- if (ptr.value == last) {
- return null;
- } else if (note[ptr.value] == '-') {
- range.setStartCharacter(startChar);
- ptr.value += 1;
- } else {
- return null;
- }
-
- last = ptr.value;
- int endLine = RawParseUtils.parseBase10(note, ptr.value, ptr);
- if (ptr.value == last) {
- return null;
- } else if (note[ptr.value] == ':') {
- range.setEndLine(endLine);
- ptr.value += 1;
- } else {
- return null;
- }
-
- last = ptr.value;
- int endChar = RawParseUtils.parseBase10(note, ptr.value, ptr);
- if (ptr.value == last) {
- return null;
- } else if (note[ptr.value] == '\n') {
- range.setEndCharacter(endChar);
- ptr.value += 1;
- } else {
- return null;
- }
- return range;
- }
-
- private static PatchSet.Id parsePsId(
- byte[] note, MutableInteger curr, Change.Id changeId, String fieldName)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, fieldName, changeId);
- int startOfPsId = RawParseUtils.endOfFooterLineKey(note, curr.value) + 1;
- MutableInteger i = new MutableInteger();
- int patchSetId = RawParseUtils.parseBase10(note, startOfPsId, i);
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- if (i.value != endOfLine - 1) {
- throw parseException(changeId, "could not parse %s", fieldName);
- }
- checkResult(patchSetId, "patchset id", changeId);
- curr.value = endOfLine;
- return new PatchSet.Id(changeId, patchSetId);
- }
-
- private static Integer parseParentNumber(byte[] note, MutableInteger curr, Change.Id changeId)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, ChangeNoteUtil.PARENT_NUMBER, changeId);
-
- int start = RawParseUtils.endOfFooterLineKey(note, curr.value) + 1;
- MutableInteger i = new MutableInteger();
- int parentNumber = RawParseUtils.parseBase10(note, start, i);
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- if (i.value != endOfLine - 1) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.PARENT_NUMBER);
- }
- checkResult(parentNumber, "parent number", changeId);
- curr.value = endOfLine;
- return Integer.valueOf(parentNumber);
- }
-
- private static String parseFilename(byte[] note, MutableInteger curr, Change.Id changeId)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, ChangeNoteUtil.FILE, changeId);
- int startOfFileName = RawParseUtils.endOfFooterLineKey(note, curr.value) + 2;
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- curr.value = endOfLine;
- curr.value = RawParseUtils.nextLF(note, curr.value);
- return QuotedString.GIT_PATH.dequote(
- RawParseUtils.decode(UTF_8, note, startOfFileName, endOfLine - 1));
- }
-
- private static Timestamp parseTimestamp(byte[] note, MutableInteger curr, Change.Id changeId)
- throws ConfigInvalidException {
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- Timestamp commentTime;
- String dateString = RawParseUtils.decode(UTF_8, note, curr.value, endOfLine - 1);
- try {
- commentTime = new Timestamp(GitDateParser.parse(dateString, null, Locale.US).getTime());
- } catch (ParseException e) {
- throw new ConfigInvalidException("could not parse comment timestamp", e);
- }
- curr.value = endOfLine;
- return checkResult(commentTime, "comment timestamp", changeId);
- }
-
- private Account.Id parseAuthor(
- byte[] note, MutableInteger curr, Change.Id changeId, String fieldName)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, fieldName, changeId);
- int startOfAccountId = RawParseUtils.endOfFooterLineKey(note, curr.value) + 2;
- PersonIdent ident = RawParseUtils.parsePersonIdent(note, startOfAccountId);
- Account.Id aId = parseIdent(ident, changeId);
- curr.value = RawParseUtils.nextLF(note, curr.value);
- return checkResult(aId, fieldName, changeId);
- }
-
- private static int parseCommentLength(byte[] note, MutableInteger curr, Change.Id changeId)
- throws ConfigInvalidException {
- checkHeaderLineFormat(note, curr, ChangeNoteUtil.LENGTH, changeId);
- int startOfLength = RawParseUtils.endOfFooterLineKey(note, curr.value) + 1;
- MutableInteger i = new MutableInteger();
- i.value = startOfLength;
- int commentLength = RawParseUtils.parseBase10(note, startOfLength, i);
- if (i.value == startOfLength) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.LENGTH);
- }
- int endOfLine = RawParseUtils.nextLF(note, curr.value);
- if (i.value != endOfLine - 1) {
- throw parseException(changeId, "could not parse %s", ChangeNoteUtil.LENGTH);
- }
- curr.value = endOfLine;
- return checkResult(commentLength, "comment length", changeId);
- }
-
- private boolean parseBooleanField(
- byte[] note, MutableInteger curr, Change.Id changeId, String fieldName)
- throws ConfigInvalidException {
- String str = parseStringField(note, curr, changeId, fieldName);
- if ("true".equalsIgnoreCase(str)) {
- return true;
- } else if ("false".equalsIgnoreCase(str)) {
- return false;
- }
- throw parseException(changeId, "invalid boolean for %s: %s", fieldName, str);
- }
-
- private static <T> T checkResult(T o, String fieldName, Change.Id changeId)
- throws ConfigInvalidException {
- if (o == null) {
- throw parseException(changeId, "could not parse %s", fieldName);
- }
- return o;
- }
-
- private static int checkResult(int i, String fieldName, Change.Id changeId)
- throws ConfigInvalidException {
- if (i <= 0) {
- throw parseException(changeId, "could not parse %s", fieldName);
- }
- return i;
- }
-
- private static void checkHeaderLineFormat(
- byte[] note, MutableInteger curr, String fieldName, Change.Id changeId)
- throws ConfigInvalidException {
- boolean correct = RawParseUtils.match(note, curr.value, fieldName.getBytes(UTF_8)) != -1;
- int p = curr.value + fieldName.length();
- correct &= (p < note.length && note[p] == ':');
- p++;
- correct &= (p < note.length && note[p] == ' ');
- if (!correct) {
- throw parseException(changeId, "could not parse %s", fieldName);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
deleted file mode 100644
index 2d5293f0e2..0000000000
--- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ListMultimap;
-import com.google.gerrit.common.UsedAt;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.inject.Inject;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.sql.Timestamp;
-import java.util.Date;
-import java.util.List;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.util.QuotedString;
-
-public class LegacyChangeNoteWrite {
-
- private final PersonIdent serverIdent;
- private final String serverId;
-
- @Inject
- public LegacyChangeNoteWrite(
- @GerritPersonIdent PersonIdent serverIdent, @GerritServerId String serverId) {
- this.serverIdent = serverIdent;
- this.serverId = serverId;
- }
-
- public PersonIdent newIdent(Account.Id authorId, Date when, PersonIdent serverIdent) {
- return new PersonIdent(
- authorId.toString(), authorId.get() + "@" + serverId, when, serverIdent.getTimeZone());
- }
-
- public String getServerId() {
- return serverId;
- }
-
- private void appendHeaderField(PrintWriter writer, String field, String value) {
- writer.print(field);
- writer.print(": ");
- writer.print(value);
- writer.print('\n');
- }
-
- /**
- * Build a note that contains the metadata for and the contents of all of the comments in the
- * given comments.
- *
- * @param comments Comments to be written to the output stream, keyed by patch set ID; multiple
- * patch sets are allowed since base revisions may be shared across patch sets. All of the
- * comments must share the same RevId, and all the comments for a given patch set must have
- * the same side.
- * @param out output stream to write to.
- */
- @UsedAt(UsedAt.Project.GOOGLE)
- public void buildNote(ListMultimap<Integer, Comment> comments, OutputStream out) {
- if (comments.isEmpty()) {
- return;
- }
-
- ImmutableList<Integer> psIds = comments.keySet().stream().sorted().collect(toImmutableList());
-
- OutputStreamWriter streamWriter = new OutputStreamWriter(out, UTF_8);
- try (PrintWriter writer = new PrintWriter(streamWriter)) {
- String revId = comments.values().iterator().next().revId;
- appendHeaderField(writer, ChangeNoteUtil.REVISION, revId);
-
- for (int psId : psIds) {
- List<Comment> psComments = COMMENT_ORDER.sortedCopy(comments.get(psId));
- Comment first = psComments.get(0);
-
- short side = first.side;
- appendHeaderField(
- writer,
- side <= 0 ? ChangeNoteUtil.BASE_PATCH_SET : ChangeNoteUtil.PATCH_SET,
- Integer.toString(psId));
- if (side < 0) {
- appendHeaderField(writer, ChangeNoteUtil.PARENT_NUMBER, Integer.toString(-side));
- }
-
- String currentFilename = null;
-
- for (Comment c : psComments) {
- checkArgument(
- revId.equals(c.revId),
- "All comments being added must have all the same RevId. The "
- + "comment below does not have the same RevId as the others "
- + "(%s).\n%s",
- revId,
- c);
- checkArgument(
- side == c.side,
- "All comments being added must all have the same side. The "
- + "comment below does not have the same side as the others "
- + "(%s).\n%s",
- side,
- c);
- String commentFilename = QuotedString.GIT_PATH.quote(c.key.filename);
-
- if (!commentFilename.equals(currentFilename)) {
- currentFilename = commentFilename;
- writer.print("File: ");
- writer.print(commentFilename);
- writer.print("\n\n");
- }
-
- appendOneComment(writer, c);
- }
- }
- }
- }
-
- private void appendOneComment(PrintWriter writer, Comment c) {
- // The CommentRange field for a comment is allowed to be null. If it is
- // null, then in the first line, we simply use the line number field for a
- // comment instead. If it isn't null, we write the comment range itself.
- Comment.Range range = c.range;
- if (range != null) {
- writer.print(range.startLine);
- writer.print(':');
- writer.print(range.startChar);
- writer.print('-');
- writer.print(range.endLine);
- writer.print(':');
- writer.print(range.endChar);
- } else {
- writer.print(c.lineNbr);
- }
- writer.print("\n");
-
- writer.print(NoteDbUtil.formatTime(serverIdent, c.writtenOn));
- writer.print("\n");
-
- appendIdent(writer, ChangeNoteUtil.AUTHOR, c.author.getId(), c.writtenOn);
- if (!c.getRealAuthor().equals(c.author)) {
- appendIdent(writer, ChangeNoteUtil.REAL_AUTHOR, c.getRealAuthor().getId(), c.writtenOn);
- }
-
- String parent = c.parentUuid;
- if (parent != null) {
- appendHeaderField(writer, ChangeNoteUtil.PARENT, parent);
- }
-
- appendHeaderField(writer, ChangeNoteUtil.UNRESOLVED, Boolean.toString(c.unresolved));
- appendHeaderField(writer, ChangeNoteUtil.UUID, c.key.uuid);
-
- if (c.tag != null) {
- appendHeaderField(writer, ChangeNoteUtil.TAG, c.tag);
- }
-
- byte[] messageBytes = c.message.getBytes(UTF_8);
- appendHeaderField(writer, ChangeNoteUtil.LENGTH, Integer.toString(messageBytes.length));
-
- writer.print(c.message);
- writer.print("\n\n");
- }
-
- private void appendIdent(PrintWriter writer, String header, Account.Id id, Timestamp ts) {
- PersonIdent ident = newIdent(id, ts, serverIdent);
- StringBuilder name = new StringBuilder();
- PersonIdent.appendSanitized(name, ident.getName());
- name.append(" <");
- PersonIdent.appendSanitized(name, ident.getEmailAddress());
- name.append('>');
- appendHeaderField(writer, header, name.toString());
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbMetrics.java b/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
index 61f475f3a9..18ffd178f9 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
@@ -19,6 +19,7 @@ import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.server.logging.Metadata;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -45,7 +46,8 @@ class NoteDbMetrics {
@Inject
NoteDbMetrics(MetricMaker metrics) {
- Field<NoteDbTable> view = Field.ofEnum(NoteDbTable.class, "table");
+ Field<NoteDbTable> tableField =
+ Field.ofEnum(NoteDbTable.class, "table", Metadata.Builder::noteDbTable).build();
updateLatency =
metrics.newTimer(
@@ -53,7 +55,7 @@ class NoteDbMetrics {
new Description("NoteDb update latency by table")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- view);
+ tableField);
stageUpdateLatency =
metrics.newTimer(
@@ -61,7 +63,7 @@ class NoteDbMetrics {
new Description("Latency for staging updates to NoteDb by table")
.setCumulative()
.setUnit(Units.MICROSECONDS),
- view);
+ tableField);
readLatency =
metrics.newTimer(
@@ -69,7 +71,7 @@ class NoteDbMetrics {
new Description("NoteDb read latency by table")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- view);
+ tableField);
parseLatency =
metrics.newTimer(
@@ -77,6 +79,6 @@ class NoteDbMetrics {
new Description("NoteDb parse latency by table")
.setCumulative()
.setUnit(Units.MICROSECONDS),
- view);
+ tableField);
}
}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java b/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
index ea42a9dcd5..7022cdc29a 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
@@ -18,27 +18,21 @@ import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
-import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.git.RefUpdateUtil;
import com.google.gerrit.metrics.Timer1;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.InMemoryInserter;
-import com.google.gerrit.server.git.InsertedObject;
import com.google.gerrit.server.update.ChainedReceiveCommands;
-import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -50,9 +44,9 @@ import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -70,85 +64,16 @@ import org.eclipse.jgit.transport.ReceiveCommand;
* {@link #stage()}.
*/
public class NoteDbUpdateManager implements AutoCloseable {
- private static final ImmutableList<String> PACKAGE_PREFIXES =
- ImmutableList.of("com.google.gerrit.server.", "com.google.gerrit.");
- private static final ImmutableSet<String> SERVLET_NAMES =
- ImmutableSet.of(
- "com.google.gerrit.httpd.restapi.RestApiServlet", RetryingRestModifyView.class.getName());
-
public interface Factory {
NoteDbUpdateManager create(Project.NameKey projectName);
}
- public static class OpenRepo implements AutoCloseable {
- public final Repository repo;
- public final RevWalk rw;
- public final ChainedReceiveCommands cmds;
-
- private final InMemoryInserter inMemIns;
- private final ObjectInserter tempIns;
- @Nullable private final ObjectInserter finalIns;
-
- private final boolean close;
-
- private OpenRepo(
- Repository repo,
- RevWalk rw,
- @Nullable ObjectInserter ins,
- ChainedReceiveCommands cmds,
- boolean close) {
- ObjectReader reader = rw.getObjectReader();
- checkArgument(
- ins == null || reader.getCreatedFromInserter() == ins,
- "expected reader to be created from %s, but was %s",
- ins,
- reader.getCreatedFromInserter());
- this.repo = requireNonNull(repo);
-
- this.inMemIns = new InMemoryInserter(rw.getObjectReader());
- this.tempIns = inMemIns;
-
- this.rw = new RevWalk(tempIns.newReader());
- this.finalIns = ins;
- this.cmds = requireNonNull(cmds);
- this.close = close;
- }
-
- public Optional<ObjectId> getObjectId(String refName) throws IOException {
- return cmds.get(refName);
- }
-
- void flush() throws IOException {
- flushToFinalInserter();
- finalIns.flush();
- }
-
- void flushToFinalInserter() throws IOException {
- checkState(finalIns != null);
- for (InsertedObject obj : inMemIns.getInsertedObjects()) {
- finalIns.insert(obj.type(), obj.data().toByteArray());
- }
- inMemIns.clear();
- }
-
- @Override
- public void close() {
- rw.getObjectReader().close();
- rw.close();
- if (close) {
- if (finalIns != null) {
- finalIns.close();
- }
- repo.close();
- }
- }
- }
-
private final Provider<PersonIdent> serverIdent;
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
private final NoteDbMetrics metrics;
private final Project.NameKey projectName;
+ private final int maxUpdates;
private final ListMultimap<String, ChangeUpdate> changeUpdates;
private final ListMultimap<String, ChangeDraftUpdate> draftUpdates;
private final ListMultimap<String, RobotCommentUpdate> robotCommentUpdates;
@@ -157,6 +82,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
private OpenRepo changeRepo;
private OpenRepo allUsersRepo;
+ private AllUsersAsyncUpdate updateAllUsersAsync;
private boolean executed;
private String refLogMessage;
private PersonIdent refLogIdent;
@@ -164,16 +90,20 @@ public class NoteDbUpdateManager implements AutoCloseable {
@Inject
NoteDbUpdateManager(
+ @GerritServerConfig Config cfg,
@GerritPersonIdent Provider<PersonIdent> serverIdent,
GitRepositoryManager repoManager,
AllUsersName allUsersName,
NoteDbMetrics metrics,
+ AllUsersAsyncUpdate updateAllUsersAsync,
@Assisted Project.NameKey projectName) {
this.serverIdent = serverIdent;
this.repoManager = repoManager;
this.allUsersName = allUsersName;
this.metrics = metrics;
+ this.updateAllUsersAsync = updateAllUsersAsync;
this.projectName = projectName;
+ maxUpdates = cfg.getInt("change", null, "maxUpdates", 1000);
changeUpdates = MultimapBuilder.hashKeys().arrayListValues().build();
draftUpdates = MultimapBuilder.hashKeys().arrayListValues().build();
robotCommentUpdates = MultimapBuilder.hashKeys().arrayListValues().build();
@@ -236,28 +166,13 @@ public class NoteDbUpdateManager implements AutoCloseable {
private void initChangeRepo() throws IOException {
if (changeRepo == null) {
- changeRepo = openRepo(projectName);
+ changeRepo = OpenRepo.open(repoManager, projectName);
}
}
private void initAllUsersRepo() throws IOException {
if (allUsersRepo == null) {
- allUsersRepo = openRepo(allUsersName);
- }
- }
-
- private OpenRepo openRepo(Project.NameKey p) throws IOException {
- Repository repo = repoManager.openRepository(p); // Closed by OpenRepo#close.
- ObjectInserter ins = repo.newObjectInserter(); // Closed by OpenRepo#close.
- ObjectReader reader = ins.newReader(); // Not closed by OpenRepo#close.
- try (RevWalk rw = new RevWalk(reader)) { // Doesn't escape OpenRepo constructor.
- return new OpenRepo(repo, rw, ins, new ChainedReceiveCommands(repo), true) {
- @Override
- public void close() {
- reader.close();
- super.close();
- }
- };
+ allUsersRepo = OpenRepo.open(repoManager, allUsersName);
}
}
@@ -268,7 +183,8 @@ public class NoteDbUpdateManager implements AutoCloseable {
&& rewriters.isEmpty()
&& toDelete.isEmpty()
&& !hasCommands(changeRepo)
- && !hasCommands(allUsersRepo);
+ && !hasCommands(allUsersRepo)
+ && updateAllUsersAsync.isEmpty();
}
private static boolean hasCommands(@Nullable OpenRepo or) {
@@ -351,7 +267,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
* @throws IOException if a storage layer error occurs.
*/
private void stage() throws IOException {
- try (Timer1.Context timer = metrics.stageUpdateLatency.start(CHANGES)) {
+ try (Timer1.Context<NoteDbTable> timer = metrics.stageUpdateLatency.start(CHANGES)) {
if (isEmpty()) {
return;
}
@@ -364,16 +280,6 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
}
- public void flush() throws IOException {
- checkNotExecuted();
- if (changeRepo != null) {
- changeRepo.flush();
- }
- if (allUsersRepo != null) {
- allUsersRepo.flush();
- }
- }
-
@Nullable
public BatchRefUpdate execute() throws IOException {
return execute(false);
@@ -386,7 +292,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
executed = true;
return null;
}
- try (Timer1.Context timer = metrics.updateLatency.start(CHANGES)) {
+ try (Timer1.Context<NoteDbTable> timer = metrics.updateLatency.start(CHANGES)) {
stage();
// ChangeUpdates must execute before ChangeDraftUpdates.
//
@@ -398,6 +304,13 @@ public class NoteDbUpdateManager implements AutoCloseable {
// comments can only go from DRAFT to PUBLISHED, not vice versa.
BatchRefUpdate result = execute(changeRepo, dryrun, pushCert);
execute(allUsersRepo, dryrun, null);
+ if (!dryrun) {
+ // Only execute the asynchronous operation if we are not in dry-run mode: The dry run would
+ // have to run synchronous to be of any value at all. For the removal of draft comments from
+ // All-Users we don't care much of the operation succeeds, so we are skipping the dry run
+ // altogether.
+ updateAllUsersAsync.execute(refLogIdent, refLogMessage, pushCert);
+ }
executed = true;
return result;
} finally {
@@ -423,7 +336,8 @@ public class NoteDbUpdateManager implements AutoCloseable {
if (refLogMessage != null) {
bru.setRefLogMessage(refLogMessage, false);
} else {
- bru.setRefLogMessage(firstNonNull(guessRestApiHandler(), "Update NoteDb refs"), false);
+ bru.setRefLogMessage(
+ firstNonNull(NoteDbUtil.guessRestApiHandler(), "Update NoteDb refs"), false);
}
bru.setRefLogIdent(refLogIdent != null ? refLogIdent : serverIdent.get());
bru.setAtomic(true);
@@ -436,59 +350,18 @@ public class NoteDbUpdateManager implements AutoCloseable {
return bru;
}
- private static String guessRestApiHandler() {
- StackTraceElement[] trace = Thread.currentThread().getStackTrace();
- int i = findRestApiServlet(trace);
- if (i < 0) {
- return null;
- }
- try {
- for (i--; i >= 0; i--) {
- String cn = trace[i].getClassName();
- Class<?> cls = Class.forName(cn);
- if (RestModifyView.class.isAssignableFrom(cls) && cls != RetryingRestModifyView.class) {
- return viewName(cn);
- }
- }
- return null;
- } catch (ClassNotFoundException e) {
- return null;
- }
- }
-
- private static String viewName(String cn) {
- String impl = cn.replace('$', '.');
- for (String p : PACKAGE_PREFIXES) {
- if (impl.startsWith(p)) {
- return impl.substring(p.length());
- }
- }
- return impl;
- }
-
- private static int findRestApiServlet(StackTraceElement[] trace) {
- for (int i = 0; i < trace.length; i++) {
- if (SERVLET_NAMES.contains(trace[i].getClassName())) {
- return i;
- }
- }
- return -1;
- }
-
private void addCommands() throws IOException {
- if (isEmpty()) {
- return;
- }
- checkState(changeRepo != null, "must set change repo");
- if (!draftUpdates.isEmpty()) {
- checkState(allUsersRepo != null, "must set all users repo");
- }
- addUpdates(changeUpdates, changeRepo);
+ changeRepo.addUpdates(changeUpdates, Optional.of(maxUpdates));
if (!draftUpdates.isEmpty()) {
- addUpdates(draftUpdates, allUsersRepo);
+ boolean publishOnly = draftUpdates.values().stream().allMatch(ChangeDraftUpdate::canRunAsync);
+ if (publishOnly) {
+ updateAllUsersAsync.setDraftUpdates(draftUpdates);
+ } else {
+ allUsersRepo.addUpdates(draftUpdates);
+ }
}
if (!robotCommentUpdates.isEmpty()) {
- addUpdates(robotCommentUpdates, changeRepo);
+ changeRepo.addUpdates(robotCommentUpdates);
}
if (!rewriters.isEmpty()) {
addRewrites(rewriters, changeRepo);
@@ -520,36 +393,6 @@ public class NoteDbUpdateManager implements AutoCloseable {
checkState(!executed, "update has already been executed");
}
- private static <U extends AbstractChangeUpdate> void addUpdates(
- ListMultimap<String, U> all, OpenRepo or) throws IOException {
- for (Map.Entry<String, Collection<U>> e : all.asMap().entrySet()) {
- String refName = e.getKey();
- Collection<U> updates = e.getValue();
- ObjectId old = or.cmds.get(refName).orElse(ObjectId.zeroId());
- // Only actually write to the ref if one of the updates explicitly allows
- // us to do so, i.e. it is known to represent a new change. This avoids
- // writing partial change meta if the change hasn't been backfilled yet.
- if (!allowWrite(updates, old)) {
- continue;
- }
-
- ObjectId curr = old;
- for (U u : updates) {
- if (u.isRootOnly() && !old.equals(ObjectId.zeroId())) {
- throw new StorageException("Given ChangeUpdate is only allowed on initial commit");
- }
- ObjectId next = u.apply(or.rw, or.tempIns, curr);
- if (next == null) {
- continue;
- }
- curr = next;
- }
- if (!old.equals(curr)) {
- or.cmds.add(new ReceiveCommand(old, curr, refName));
- }
- }
- }
-
private static void addRewrites(ListMultimap<String, NoteDbRewriter> rewriters, OpenRepo openRepo)
throws IOException {
for (Map.Entry<String, Collection<NoteDbRewriter>> entry : rewriters.asMap().entrySet()) {
@@ -578,12 +421,4 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
}
}
-
- private static <U extends AbstractChangeUpdate> boolean allowWrite(
- Collection<U> updates, ObjectId old) {
- if (!old.equals(ObjectId.zeroId())) {
- return true;
- }
- return updates.iterator().next().allowWriteToNewRef();
- }
}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbUtil.java b/java/com/google/gerrit/server/notedb/NoteDbUtil.java
index 667ceaba4d..58a33c8501 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbUtil.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbUtil.java
@@ -15,8 +15,12 @@
package com.google.gerrit.server.notedb;
import com.google.common.base.CharMatcher;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.server.update.RetryingRestModifyView;
import java.sql.Timestamp;
import java.util.Optional;
import org.eclipse.jgit.lib.PersonIdent;
@@ -25,26 +29,35 @@ import org.eclipse.jgit.util.GitDateFormatter.Format;
public class NoteDbUtil {
- /**
- * Returns an AccountId for the given email address. Returns empty if the address isn't on this
- * server.
- */
- public static Optional<Account.Id> parseIdent(PersonIdent ident, String serverId) {
+ private static final CharMatcher INVALID_FOOTER_CHARS = CharMatcher.anyOf("\r\n\0");
+
+ private static final ImmutableList<String> PACKAGE_PREFIXES =
+ ImmutableList.of("com.google.gerrit.server.", "com.google.gerrit.");
+ private static final ImmutableSet<String> SERVLET_NAMES =
+ ImmutableSet.of(
+ "com.google.gerrit.httpd.restapi.RestApiServlet", RetryingRestModifyView.class.getName());
+
+ /** Returns an AccountId for the given email address. */
+ public static Optional<Account.Id> parseIdent(PersonIdent ident) {
String email = ident.getEmailAddress();
int at = email.indexOf('@');
if (at >= 0) {
- String host = email.substring(at + 1);
- if (host.equals(serverId)) {
- Integer id = Ints.tryParse(email.substring(0, at));
- if (id != null) {
- return Optional.of(new Account.Id(id));
- }
+ Integer id = Ints.tryParse(email.substring(0, at));
+ if (id != null) {
+ return Optional.of(Account.id(id));
}
}
return Optional.empty();
}
- private NoteDbUtil() {}
+ public static String extractHostPartFromPersonIdent(PersonIdent ident) {
+ String email = ident.getEmailAddress();
+ int at = email.indexOf('@');
+ if (at >= 0) {
+ return email.substring(at + 1);
+ }
+ throw new IllegalArgumentException("No host part found: " + email);
+ }
public static String formatTime(PersonIdent ident, Timestamp t) {
GitDateFormatter dateFormatter = new GitDateFormatter(Format.DEFAULT);
@@ -53,7 +66,29 @@ public class NoteDbUtil {
return dateFormatter.formatDate(newIdent);
}
- private static final CharMatcher INVALID_FOOTER_CHARS = CharMatcher.anyOf("\r\n\0");
+ /**
+ * Returns the name of the REST API handler that is in the stack trace of the caller of this
+ * method.
+ */
+ static String guessRestApiHandler() {
+ StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+ int i = findRestApiServlet(trace);
+ if (i < 0) {
+ return null;
+ }
+ try {
+ for (i--; i >= 0; i--) {
+ String cn = trace[i].getClassName();
+ Class<?> cls = Class.forName(cn);
+ if (RestModifyView.class.isAssignableFrom(cls) && cls != RetryingRestModifyView.class) {
+ return viewName(cn);
+ }
+ }
+ return null;
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
static String sanitizeFooter(String value) {
// Remove characters that would confuse JGit's footer parser if they were
@@ -65,4 +100,25 @@ public class NoteDbUtil {
// empty paragraph for the purposes of footer parsing.
return INVALID_FOOTER_CHARS.trimAndCollapseFrom(value, ' ');
}
+
+ private static int findRestApiServlet(StackTraceElement[] trace) {
+ for (int i = 0; i < trace.length; i++) {
+ if (SERVLET_NAMES.contains(trace[i].getClassName())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static String viewName(String cn) {
+ String impl = cn.replace('$', '.');
+ for (String p : PACKAGE_PREFIXES) {
+ if (impl.startsWith(p)) {
+ return impl.substring(p.length());
+ }
+ }
+ return impl;
+ }
+
+ private NoteDbUtil() {}
}
diff --git a/java/com/google/gerrit/server/notedb/OpenRepo.java b/java/com/google/gerrit/server/notedb/OpenRepo.java
new file mode 100644
index 0000000000..de88684610
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/OpenRepo.java
@@ -0,0 +1,176 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.notedb;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ListMultimap;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.InMemoryInserter;
+import com.google.gerrit.server.git.InsertedObject;
+import com.google.gerrit.server.update.ChainedReceiveCommands;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/**
+ * Wrapper around {@link Repository} that keeps track of related {@link ObjectInserter}s and other
+ * objects that are jointly closed when invoking {@link #close}.
+ */
+class OpenRepo implements AutoCloseable {
+ /** Returns a {@link OpenRepo} wrapping around an open {@link Repository}. */
+ static OpenRepo open(GitRepositoryManager repoManager, Project.NameKey project)
+ throws IOException {
+ Repository repo = repoManager.openRepository(project); // Closed by OpenRepo#close.
+ ObjectInserter ins = repo.newObjectInserter(); // Closed by OpenRepo#close.
+ ObjectReader reader = ins.newReader(); // Not closed by OpenRepo#close.
+ try (RevWalk rw = new RevWalk(reader)) { // Doesn't escape OpenRepo constructor.
+ return new OpenRepo(repo, rw, ins, new ChainedReceiveCommands(repo), true) {
+ @Override
+ public void close() {
+ reader.close();
+ super.close();
+ }
+ };
+ }
+ }
+
+ final Repository repo;
+ final RevWalk rw;
+ final ChainedReceiveCommands cmds;
+ final ObjectInserter tempIns;
+
+ private final InMemoryInserter inMemIns;
+ @Nullable private final ObjectInserter finalIns;
+ private final boolean close;
+
+ OpenRepo(
+ Repository repo,
+ RevWalk rw,
+ @Nullable ObjectInserter ins,
+ ChainedReceiveCommands cmds,
+ boolean close) {
+ ObjectReader reader = rw.getObjectReader();
+ checkArgument(
+ ins == null || reader.getCreatedFromInserter() == ins,
+ "expected reader to be created from %s, but was %s",
+ ins,
+ reader.getCreatedFromInserter());
+ this.repo = requireNonNull(repo);
+
+ this.inMemIns = new InMemoryInserter(rw.getObjectReader());
+ this.tempIns = inMemIns;
+
+ this.rw = new RevWalk(tempIns.newReader());
+ this.finalIns = ins;
+ this.cmds = requireNonNull(cmds);
+ this.close = close;
+ }
+
+ @Override
+ public void close() {
+ rw.getObjectReader().close();
+ rw.close();
+ if (close) {
+ if (finalIns != null) {
+ finalIns.close();
+ }
+ repo.close();
+ }
+ }
+
+ void flush() throws IOException {
+ flushToFinalInserter();
+ finalIns.flush();
+ }
+
+ void flushToFinalInserter() throws IOException {
+ checkState(finalIns != null);
+ for (InsertedObject obj : inMemIns.getInsertedObjects()) {
+ finalIns.insert(obj.type(), obj.data().toByteArray());
+ }
+ inMemIns.clear();
+ }
+
+ private static <U extends AbstractChangeUpdate> boolean allowWrite(
+ Collection<U> updates, ObjectId old) {
+ if (!old.equals(ObjectId.zeroId())) {
+ return true;
+ }
+ return updates.iterator().next().allowWriteToNewRef();
+ }
+
+ <U extends AbstractChangeUpdate> void addUpdates(ListMultimap<String, U> all) throws IOException {
+ addUpdates(all, Optional.empty());
+ }
+
+ <U extends AbstractChangeUpdate> void addUpdates(
+ ListMultimap<String, U> all, Optional<Integer> maxUpdates) throws IOException {
+ for (Map.Entry<String, Collection<U>> e : all.asMap().entrySet()) {
+ String refName = e.getKey();
+ Collection<U> updates = e.getValue();
+ ObjectId old = cmds.get(refName).orElse(ObjectId.zeroId());
+ // Only actually write to the ref if one of the updates explicitly allows
+ // us to do so, i.e. it is known to represent a new change. This avoids
+ // writing partial change meta if the change hasn't been backfilled yet.
+ if (!allowWrite(updates, old)) {
+ continue;
+ }
+
+ int updateCount;
+ U first = updates.iterator().next();
+ if (maxUpdates.isPresent()) {
+ checkState(first.getNotes() != null, "expected ChangeNotes on %s", first);
+ updateCount = first.getNotes().getUpdateCount();
+ } else {
+ updateCount = 0;
+ }
+
+ ObjectId curr = old;
+ for (U u : updates) {
+ if (u.isRootOnly() && !old.equals(ObjectId.zeroId())) {
+ throw new StorageException("Given ChangeUpdate is only allowed on initial commit");
+ }
+ ObjectId next = u.apply(rw, tempIns, curr);
+ if (next == null) {
+ continue;
+ }
+ if (maxUpdates.isPresent()
+ && !Objects.equals(next, curr)
+ && ++updateCount > maxUpdates.get()
+ && !u.bypassMaxUpdates()) {
+ throw new TooManyUpdatesException(u.getId(), maxUpdates.get());
+ }
+ curr = next;
+ }
+ if (!old.equals(curr)) {
+ cmds.add(new ReceiveCommand(old, curr, refName));
+ }
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/notedb/RepoSequence.java b/java/com/google/gerrit/server/notedb/RepoSequence.java
index 68b2b95399..e52b84c148 100644
--- a/java/com/google/gerrit/server/notedb/RepoSequence.java
+++ b/java/com/google/gerrit/server/notedb/RepoSequence.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_SEQUENCES;
+import static com.google.gerrit.entities.RefNames.REFS;
+import static com.google.gerrit.entities.RefNames.REFS_SEQUENCES;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -29,19 +29,20 @@ import com.github.rholder.retry.WaitStrategies;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.Runnables;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
-import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
@@ -72,9 +73,12 @@ public class RepoSequence {
}
@VisibleForTesting
- static RetryerBuilder<RefUpdate> retryerBuilder() {
- return RetryerBuilder.<RefUpdate>newBuilder()
- .retryIfResult(ru -> ru != null && RefUpdate.Result.LOCK_FAILURE.equals(ru.getResult()))
+ static RetryerBuilder<ImmutableList<Integer>> retryerBuilder() {
+ return RetryerBuilder.<ImmutableList<Integer>>newBuilder()
+ .retryIfException(
+ t ->
+ t instanceof StorageException
+ && ((StorageException) t).getCause() instanceof LockFailureException)
.withWaitStrategy(
WaitStrategies.join(
WaitStrategies.exponentialWait(5, TimeUnit.SECONDS),
@@ -82,7 +86,7 @@ public class RepoSequence {
.withStopStrategy(StopStrategies.stopAfterDelay(30, TimeUnit.SECONDS));
}
- private static final Retryer<RefUpdate> RETRYER = retryerBuilder().build();
+ private static final Retryer<ImmutableList<Integer>> RETRYER = retryerBuilder().build();
private final GitRepositoryManager repoManager;
private final GitReferenceUpdated gitRefUpdated;
@@ -92,7 +96,7 @@ public class RepoSequence {
private final int floor;
private final int batchSize;
private final Runnable afterReadRef;
- private final Retryer<RefUpdate> retryer;
+ private final Retryer<ImmutableList<Integer>> retryer;
// Protects all non-final fields.
private final Lock counterLock;
@@ -150,7 +154,7 @@ public class RepoSequence {
Seed seed,
int batchSize,
Runnable afterReadRef,
- Retryer<RefUpdate> retryer) {
+ Retryer<ImmutableList<Integer>> retryer) {
this(repoManager, gitRefUpdated, projectName, name, seed, batchSize, afterReadRef, retryer, 0);
}
@@ -162,7 +166,7 @@ public class RepoSequence {
Seed seed,
int batchSize,
Runnable afterReadRef,
- Retryer<RefUpdate> retryer,
+ Retryer<ImmutableList<Integer>> retryer,
int floor) {
this.repoManager = requireNonNull(repoManager, "repoManager");
this.gitRefUpdated = requireNonNull(gitRefUpdated, "gitRefUpdated");
@@ -188,79 +192,86 @@ public class RepoSequence {
counterLock = new ReentrantLock(true);
}
+ /**
+ * Retrieves the next available sequence number.
+ *
+ * <p>This method is thread-safe.
+ *
+ * @return the next available sequence number
+ */
public int next() {
- counterLock.lock();
- try {
- if (counter >= limit) {
- acquire(batchSize);
- }
- return counter++;
- } finally {
- counterLock.unlock();
- }
+ return Iterables.getOnlyElement(next(1));
}
+ /**
+ * Retrieves the next N available sequence number.
+ *
+ * <p>This method is thread-safe.
+ *
+ * @param count the number of sequence numbers which should be returned
+ * @return the next N available sequence numbers
+ */
public ImmutableList<Integer> next(int count) {
if (count == 0) {
return ImmutableList.of();
}
checkArgument(count > 0, "count is negative: %s", count);
- counterLock.lock();
+
try {
- List<Integer> ids = new ArrayList<>(count);
- while (counter < limit) {
- ids.add(counter++);
- if (ids.size() == count) {
- return ImmutableList.copyOf(ids);
- }
- }
- acquire(Math.max(count - ids.size(), batchSize));
- while (ids.size() < count) {
- ids.add(counter++);
- }
- return ImmutableList.copyOf(ids);
- } finally {
- counterLock.unlock();
- }
- }
+ return retryer.call(
+ () -> {
+ counterLock.lock();
+ try {
+ if (count == 1) {
+ if (counter >= limit) {
+ acquire(batchSize);
+ }
+ return ImmutableList.of(counter++);
+ }
- private void acquire(int count) {
- try (Repository repo = repoManager.openRepository(projectName);
- RevWalk rw = new RevWalk(repo)) {
- logger.atFine().log("acquire %d ids on %s in %s", count, refName, projectName);
- TryAcquire attempt = new TryAcquire(repo, rw, count);
- RefUpdateUtil.checkResult(retryer.call(attempt));
- counter = attempt.next;
- limit = counter + count;
- acquireCount++;
+ List<Integer> ids = new ArrayList<>(count);
+ while (counter < limit) {
+ ids.add(counter++);
+ if (ids.size() == count) {
+ return ImmutableList.copyOf(ids);
+ }
+ }
+ acquire(Math.max(count - ids.size(), batchSize));
+ while (ids.size() < count) {
+ ids.add(counter++);
+ }
+ return ImmutableList.copyOf(ids);
+ } finally {
+ counterLock.unlock();
+ }
+ });
} catch (ExecutionException | RetryException e) {
if (e.getCause() != null) {
Throwables.throwIfInstanceOf(e.getCause(), StorageException.class);
}
throw new StorageException(e);
- } catch (IOException e) {
- throw new StorageException(e);
}
}
- private class TryAcquire implements Callable<RefUpdate> {
- private final Repository repo;
- private final RevWalk rw;
- private final int count;
-
- private int next;
-
- private TryAcquire(Repository repo, RevWalk rw, int count) {
- this.repo = repo;
- this.rw = rw;
- this.count = count;
- }
-
- @Override
- public RefUpdate call() throws Exception {
+ /**
+ * Updates the next available sequence number in NoteDb in order to have a batch of sequence
+ * numbers available that can be handed out. {@link #counter} stores the next sequence number that
+ * can be handed out. When {@link #limit} is reached a new batch of sequence numbers needs to be
+ * retrieved by calling this method.
+ *
+ * <p><strong>Note:</strong> Callers are required to acquire the {@link #counterLock} before
+ * calling this method.
+ *
+ * @param count the number of sequence numbers which should be retrieved
+ */
+ private void acquire(int count) {
+ try (Repository repo = repoManager.openRepository(projectName);
+ RevWalk rw = new RevWalk(repo)) {
+ logger.atFine().log("acquire %d ids on %s in %s", count, refName, projectName);
Optional<IntBlob> blob = IntBlob.parse(repo, refName, rw);
afterReadRef.run();
ObjectId oldId;
+ int next;
if (!blob.isPresent()) {
oldId = ObjectId.zeroId();
next = seed.get();
@@ -269,7 +280,14 @@ public class RepoSequence {
next = blob.get().value();
}
next = Math.max(floor, next);
- return IntBlob.tryStore(repo, rw, projectName, refName, oldId, next + count, gitRefUpdated);
+ RefUpdate refUpdate =
+ IntBlob.tryStore(repo, rw, projectName, refName, oldId, next + count, gitRefUpdated);
+ RefUpdateUtil.checkResult(refUpdate);
+ counter = next;
+ limit = counter + count;
+ acquireCount++;
+ } catch (IOException e) {
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/notedb/ReviewerStateInternal.java b/java/com/google/gerrit/server/notedb/ReviewerStateInternal.java
index fad9832d90..d5a7259417 100644
--- a/java/com/google/gerrit/server/notedb/ReviewerStateInternal.java
+++ b/java/com/google/gerrit/server/notedb/ReviewerStateInternal.java
@@ -21,13 +21,13 @@ import org.eclipse.jgit.revwalk.FooterKey;
/** State of a reviewer on a change. */
public enum ReviewerStateInternal {
/** The user has contributed at least one nonzero vote on the change. */
- REVIEWER(new FooterKey("Reviewer"), ReviewerState.REVIEWER),
+ REVIEWER("Reviewer", ReviewerState.REVIEWER),
/** The reviewer was added to the change, but has not voted. */
- CC(new FooterKey("CC"), ReviewerState.CC),
+ CC("CC", ReviewerState.CC),
/** The user was previously a reviewer on the change, but was removed. */
- REMOVED(new FooterKey("Removed"), ReviewerState.REMOVED);
+ REMOVED("Removed", ReviewerState.REMOVED);
public static ReviewerStateInternal fromReviewerState(ReviewerState state) {
return ReviewerStateInternal.values()[state.ordinal()];
@@ -50,20 +50,20 @@ public enum ReviewerStateInternal {
}
}
- private final FooterKey footerKey;
+ private final String footer;
private final ReviewerState state;
- ReviewerStateInternal(FooterKey footerKey, ReviewerState state) {
- this.footerKey = footerKey;
+ ReviewerStateInternal(String footer, ReviewerState state) {
+ this.footer = footer;
this.state = state;
}
FooterKey getFooterKey() {
- return footerKey;
+ return new FooterKey(footer);
}
FooterKey getByEmailFooterKey() {
- return new FooterKey(footerKey.getName() + "-email");
+ return new FooterKey(footer + "-email");
}
public ReviewerState asReviewerState() {
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java b/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
index ac7a89d904..e63737c85a 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
@@ -21,8 +21,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.MultimapBuilder;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.entities.Comment;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -33,27 +32,29 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
class RevisionNoteBuilder {
static class Cache {
private final RevisionNoteMap<? extends RevisionNote<? extends Comment>> revisionNoteMap;
- private final Map<RevId, RevisionNoteBuilder> builders;
+ private final Map<ObjectId, RevisionNoteBuilder> builders;
Cache(RevisionNoteMap<? extends RevisionNote<? extends Comment>> revisionNoteMap) {
this.revisionNoteMap = revisionNoteMap;
this.builders = new HashMap<>();
}
- RevisionNoteBuilder get(RevId revId) {
- RevisionNoteBuilder b = builders.get(revId);
+ RevisionNoteBuilder get(AnyObjectId commitId) {
+ RevisionNoteBuilder b = builders.get(commitId);
if (b == null) {
- b = new RevisionNoteBuilder(revisionNoteMap.revisionNotes.get(revId));
- builders.put(revId, b);
+ b = new RevisionNoteBuilder(revisionNoteMap.revisionNotes.get(commitId));
+ builders.put(commitId.copy(), b);
}
return b;
}
- Map<RevId, RevisionNoteBuilder> getBuilders() {
+ Map<ObjectId, RevisionNoteBuilder> getBuilders() {
return Collections.unmodifiableMap(builders);
}
}
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteData.java b/java/com/google/gerrit/server/notedb/RevisionNoteData.java
index 1e16b2221b..c0e09ed343 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteData.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteData.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.notedb;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import java.util.List;
/**
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteMap.java b/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
index da790e248b..3e1bad1951 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
@@ -15,37 +15,28 @@
package com.google.gerrit.server.notedb;
import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.entities.Comment;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.notes.Note;
import org.eclipse.jgit.notes.NoteMap;
class RevisionNoteMap<T extends RevisionNote<? extends Comment>> {
final NoteMap noteMap;
- final ImmutableMap<RevId, T> revisionNotes;
+ final ImmutableMap<ObjectId, T> revisionNotes;
static RevisionNoteMap<ChangeRevisionNote> parse(
- ChangeNoteJson noteJson,
- LegacyChangeNoteRead legacyChangeNoteRead,
- Change.Id changeId,
- ObjectReader reader,
- NoteMap noteMap,
- PatchLineComment.Status status)
+ ChangeNoteJson noteJson, ObjectReader reader, NoteMap noteMap, Comment.Status status)
throws ConfigInvalidException, IOException {
- Map<RevId, ChangeRevisionNote> result = new HashMap<>();
+ Map<ObjectId, ChangeRevisionNote> result = new HashMap<>();
for (Note note : noteMap) {
- ChangeRevisionNote rn =
- new ChangeRevisionNote(
- noteJson, legacyChangeNoteRead, changeId, reader, note.getData(), status);
+ ChangeRevisionNote rn = new ChangeRevisionNote(noteJson, reader, note.getData(), status);
rn.parse();
- result.put(new RevId(note.name()), rn);
+ result.put(note.copy(), rn);
}
return new RevisionNoteMap<>(noteMap, ImmutableMap.copyOf(result));
}
@@ -53,12 +44,12 @@ class RevisionNoteMap<T extends RevisionNote<? extends Comment>> {
static RevisionNoteMap<RobotCommentsRevisionNote> parseRobotComments(
ChangeNoteJson changeNoteJson, ObjectReader reader, NoteMap noteMap)
throws ConfigInvalidException, IOException {
- Map<RevId, RobotCommentsRevisionNote> result = new HashMap<>();
+ Map<ObjectId, RobotCommentsRevisionNote> result = new HashMap<>();
for (Note note : noteMap) {
RobotCommentsRevisionNote rn =
new RobotCommentsRevisionNote(changeNoteJson, reader, note.getData());
rn.parse();
- result.put(new RevId(note.name()), rn);
+ result.put(note.copy(), rn);
}
return new RevisionNoteMap<>(noteMap, ImmutableMap.copyOf(result));
}
@@ -67,7 +58,7 @@ class RevisionNoteMap<T extends RevisionNote<? extends Comment>> {
return new RevisionNoteMap<>(NoteMap.newEmptyMap(), ImmutableMap.of());
}
- private RevisionNoteMap(NoteMap noteMap, ImmutableMap<RevId, T> revisionNotes) {
+ private RevisionNoteMap(NoteMap noteMap, ImmutableMap<ObjectId, T> revisionNotes) {
this.noteMap = noteMap;
this.revisionNotes = revisionNotes;
}
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
index 92dd7d8b7e..fe0564356e 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
@@ -19,11 +19,10 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.client.RobotComment;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.RobotComment;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -42,7 +41,7 @@ public class RobotCommentNotes extends AbstractChangeNotes<RobotCommentNotes> {
private final Change change;
- private ImmutableListMultimap<RevId, RobotComment> comments;
+ private ImmutableListMultimap<ObjectId, RobotComment> comments;
private RevisionNoteMap<RobotCommentsRevisionNote> revisionNoteMap;
private ObjectId metaId;
@@ -56,7 +55,7 @@ public class RobotCommentNotes extends AbstractChangeNotes<RobotCommentNotes> {
return revisionNoteMap;
}
- public ImmutableListMultimap<RevId, RobotComment> getComments() {
+ public ImmutableListMultimap<ObjectId, RobotComment> getComments() {
return comments;
}
@@ -95,10 +94,10 @@ public class RobotCommentNotes extends AbstractChangeNotes<RobotCommentNotes> {
revisionNoteMap =
RevisionNoteMap.parseRobotComments(
args.changeNoteJson, reader, NoteMap.read(reader, tipCommit));
- ListMultimap<RevId, RobotComment> cs = MultimapBuilder.hashKeys().arrayListValues().build();
+ ListMultimap<ObjectId, RobotComment> cs = MultimapBuilder.hashKeys().arrayListValues().build();
for (RobotCommentsRevisionNote rn : revisionNoteMap.revisionNotes.values()) {
for (RobotComment c : rn.getEntities()) {
- cs.put(new RevId(c.revId), c);
+ cs.put(c.getCommitId(), c);
}
}
comments = ImmutableListMultimap.copyOf(cs);
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java b/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
index 0304ab81d7..895f378452 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
@@ -15,16 +15,15 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.gerrit.reviewdb.client.RefNames.robotCommentsRef;
+import static com.google.gerrit.entities.RefNames.robotCommentsRef;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.common.collect.Sets;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -103,19 +102,19 @@ public class RobotCommentUpdate extends AbstractChangeUpdate {
RevWalk rw, ObjectInserter ins, ObjectId curr, CommitBuilder cb)
throws ConfigInvalidException, IOException {
RevisionNoteMap<RobotCommentsRevisionNote> rnm = getRevisionNoteMap(rw, curr);
- Set<RevId> updatedRevs = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
+ Set<ObjectId> updatedRevs = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
for (RobotComment c : put) {
- cache.get(new RevId(c.revId)).putComment(c);
+ cache.get(c.getCommitId()).putComment(c);
}
- Map<RevId, RevisionNoteBuilder> builders = cache.getBuilders();
+ Map<ObjectId, RevisionNoteBuilder> builders = cache.getBuilders();
boolean touchedAnyRevs = false;
boolean hasComments = false;
- for (Map.Entry<RevId, RevisionNoteBuilder> e : builders.entrySet()) {
- updatedRevs.add(e.getKey());
- ObjectId id = ObjectId.fromString(e.getKey().get());
+ for (Map.Entry<ObjectId, RevisionNoteBuilder> e : builders.entrySet()) {
+ ObjectId id = e.getKey();
+ updatedRevs.add(id);
byte[] data = e.getValue().build(noteUtil);
if (!Arrays.equals(data, e.getValue().baseRaw)) {
touchedAnyRevs = true;
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentsRevisionNote.java b/java/com/google/gerrit/server/notedb/RobotCommentsRevisionNote.java
index 6c3cc8627e..97a8ad4a4e 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentsRevisionNote.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentsRevisionNote.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.notedb;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.gerrit.reviewdb.client.RobotComment;
+import com.google.gerrit.entities.RobotComment;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentsRevisionNoteData.java b/java/com/google/gerrit/server/notedb/RobotCommentsRevisionNoteData.java
index 116b30e624..3619c432c1 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentsRevisionNoteData.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentsRevisionNoteData.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.notedb;
-import com.google.gerrit.reviewdb.client.RobotComment;
+import com.google.gerrit.entities.RobotComment;
import java.util.List;
public class RobotCommentsRevisionNoteData {
diff --git a/java/com/google/gerrit/server/notedb/Sequences.java b/java/com/google/gerrit/server/notedb/Sequences.java
index d4144cea06..9f1972535f 100644
--- a/java/com/google/gerrit/server/notedb/Sequences.java
+++ b/java/com/google/gerrit/server/notedb/Sequences.java
@@ -25,6 +25,7 @@ import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.logging.Metadata;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.Config;
@@ -110,30 +111,35 @@ public class Sequences {
new Description("Latency of requesting IDs from repo sequences")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- Field.ofEnum(SequenceType.class, "sequence"),
- Field.ofBoolean("multiple"));
+ Field.ofEnum(SequenceType.class, "sequence", Metadata.Builder::noteDbSequenceType)
+ .build(),
+ Field.ofBoolean("multiple", Metadata.Builder::multiple).build());
}
public int nextAccountId() {
- try (Timer2.Context timer = nextIdLatency.start(SequenceType.ACCOUNTS, false)) {
+ try (Timer2.Context<SequenceType, Boolean> timer =
+ nextIdLatency.start(SequenceType.ACCOUNTS, false)) {
return accountSeq.next();
}
}
public int nextChangeId() {
- try (Timer2.Context timer = nextIdLatency.start(SequenceType.CHANGES, false)) {
+ try (Timer2.Context<SequenceType, Boolean> timer =
+ nextIdLatency.start(SequenceType.CHANGES, false)) {
return changeSeq.next();
}
}
public ImmutableList<Integer> nextChangeIds(int count) {
- try (Timer2.Context timer = nextIdLatency.start(SequenceType.CHANGES, count > 1)) {
+ try (Timer2.Context<SequenceType, Boolean> timer =
+ nextIdLatency.start(SequenceType.CHANGES, count > 1)) {
return changeSeq.next(count);
}
}
public int nextGroupId() {
- try (Timer2.Context timer = nextIdLatency.start(SequenceType.GROUPS, false)) {
+ try (Timer2.Context<SequenceType, Boolean> timer =
+ nextIdLatency.start(SequenceType.GROUPS, false)) {
return groupSeq.next();
}
}
diff --git a/java/com/google/gerrit/server/notedb/TooManyUpdatesException.java b/java/com/google/gerrit/server/notedb/TooManyUpdatesException.java
new file mode 100644
index 0000000000..9c6faaf6c8
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/TooManyUpdatesException.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.notedb;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.exceptions.StorageException;
+
+/**
+ * Exception indicating that the change has received too many updates. Further actions apart from
+ * {@code abandon} or {@code submit} are blocked.
+ */
+public class TooManyUpdatesException extends StorageException {
+ @VisibleForTesting
+ public static String message(Change.Id id, int maxUpdates) {
+ return "Change "
+ + id
+ + " may not exceed "
+ + maxUpdates
+ + " updates. It may still be abandoned or submitted. To continue working on this "
+ + "change, recreate it with a new Change-Id, then abandon this one.";
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ TooManyUpdatesException(Change.Id id, int maxUpdates) {
+ super(message(id, maxUpdates));
+ }
+}
diff --git a/java/com/google/gerrit/server/patch/AutoMerger.java b/java/com/google/gerrit/server/patch/AutoMerger.java
index 18aa8b9fe0..a9cc9b5ac4 100644
--- a/java/com/google/gerrit/server/patch/AutoMerger.java
+++ b/java/com/google/gerrit/server/patch/AutoMerger.java
@@ -19,7 +19,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.InMemoryInserter;
diff --git a/java/com/google/gerrit/server/patch/DiffSummaryLoader.java b/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
index 9153638199..b61e0c7bb8 100644
--- a/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
+++ b/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.patch;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Project;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.ArrayList;
diff --git a/java/com/google/gerrit/server/patch/IntraLineDiff.java b/java/com/google/gerrit/server/patch/IntraLineDiff.java
index 1c3d78a91b..fb13207b75 100644
--- a/java/com/google/gerrit/server/patch/IntraLineDiff.java
+++ b/java/com/google/gerrit/server/patch/IntraLineDiff.java
@@ -21,8 +21,8 @@ import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.CodedEnum;
import com.google.gerrit.jgit.diff.ReplaceEdit;
-import com.google.gerrit.reviewdb.client.CodedEnum;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
diff --git a/java/com/google/gerrit/server/patch/IntraLineDiffArgs.java b/java/com/google/gerrit/server/patch/IntraLineDiffArgs.java
index 4661485ddf..2ebae02c7d 100644
--- a/java/com/google/gerrit/server/patch/IntraLineDiffArgs.java
+++ b/java/com/google/gerrit/server/patch/IntraLineDiffArgs.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.patch;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.diff.Edit;
diff --git a/java/com/google/gerrit/server/patch/PatchFile.java b/java/com/google/gerrit/server/patch/PatchFile.java
index 0e5bcc17a0..ca5223d971 100644
--- a/java/com/google/gerrit/server/patch/PatchFile.java
+++ b/java/com/google/gerrit/server/patch/PatchFile.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.patch;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.exceptions.NoSuchEntityException;
-import com.google.gerrit.reviewdb.client.Patch;
import java.io.IOException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -39,6 +39,15 @@ public class PatchFile {
private final RevTree aTree;
private final RevTree bTree;
+ // Full text of both sides of the file. For standard files, these are not directly reconstructable
+ // from the PatchListEntry, which comes from the PatchListCache and only contains the diff between
+ // the two blobs. This is intentional, to avoid storing entire large blobs in the cache. For
+ // regular files, the full text is initialized from the repo lazily only when necessary, e.g. in
+ // getLine. Although it's a safe assumption that any caller constructing a PatchSet will want to
+ // read some content, we don't know in advance which side they are interested in.
+ //
+ // For special files like COMMIT_MSG, the full text is loaded eagerly during the constructor.
+ // TODO(dborowitz): I see why the logic is different, but I don't see why it needs to be eager.
private Text a;
private Text b;
diff --git a/java/com/google/gerrit/server/patch/PatchList.java b/java/com/google/gerrit/server/patch/PatchList.java
index dd717ba37e..fee9088ce9 100644
--- a/java/com/google/gerrit/server/patch/PatchList.java
+++ b/java/com/google/gerrit/server/patch/PatchList.java
@@ -25,8 +25,9 @@ import static org.eclipse.jgit.lib.ObjectIdSerializer.writeWithoutMarker;
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.git.ObjectIds;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -78,7 +79,7 @@ public class PatchList implements Serializable {
boolean isMerge,
ComparisonType comparisonType,
PatchListEntry[] patches) {
- this.oldId = oldId != null ? oldId.copy() : null;
+ this.oldId = ObjectIds.copyOrNull(oldId);
this.newId = newId.copy();
this.isMerge = isMerge;
this.comparisonType = comparisonType;
diff --git a/java/com/google/gerrit/server/patch/PatchListCache.java b/java/com/google/gerrit/server/patch/PatchListCache.java
index 728d227123..63cac0e9ec 100644
--- a/java/com/google/gerrit/server/patch/PatchListCache.java
+++ b/java/com/google/gerrit/server/patch/PatchListCache.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.patch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import org.eclipse.jgit.lib.ObjectId;
/** Provides a cached list of {@link PatchListEntry}. */
diff --git a/java/com/google/gerrit/server/patch/PatchListCacheImpl.java b/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
index 20f0f720b8..15fa0f4ec9 100644
--- a/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
+++ b/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
@@ -18,10 +18,10 @@ package com.google.gerrit.server.patch;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.cache.CacheBackend;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -137,10 +137,7 @@ public class PatchListCacheImpl implements PatchListCache {
private PatchList get(Change change, PatchSet patchSet, Integer parentNum)
throws PatchListNotAvailableException {
Project.NameKey project = change.getProject();
- if (patchSet.getRevision() == null) {
- throw new PatchListNotAvailableException("revision is null for " + patchSet.getId());
- }
- ObjectId b = ObjectId.fromString(patchSet.getRevision().get());
+ ObjectId b = patchSet.commitId();
Whitespace ws = Whitespace.IGNORE_NONE;
if (parentNum != null) {
return get(PatchListKey.againstParentNum(parentNum, b, ws), project);
diff --git a/java/com/google/gerrit/server/patch/PatchListEntry.java b/java/com/google/gerrit/server/patch/PatchListEntry.java
index 6b1a153edc..625f56c253 100644
--- a/java/com/google/gerrit/server/patch/PatchListEntry.java
+++ b/java/com/google/gerrit/server/patch/PatchListEntry.java
@@ -30,10 +30,10 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
-import com.google.gerrit.reviewdb.client.Patch.PatchType;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Patch.ChangeType;
+import com.google.gerrit.entities.Patch.PatchType;
+import com.google.gerrit.entities.PatchSet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -229,7 +229,7 @@ public class PatchListEntry {
}
Patch toPatch(PatchSet.Id setId) {
- final Patch p = new Patch(new Patch.Key(setId, getNewName()));
+ final Patch p = new Patch(Patch.key(setId, getNewName()));
p.setChangeType(getChangeType());
p.setPatchType(getPatchType());
p.setSourceFileName(getOldName());
diff --git a/java/com/google/gerrit/server/patch/PatchListKey.java b/java/com/google/gerrit/server/patch/PatchListKey.java
index 2df6d66450..bf38029d00 100644
--- a/java/com/google/gerrit/server/patch/PatchListKey.java
+++ b/java/com/google/gerrit/server/patch/PatchListKey.java
@@ -23,6 +23,7 @@ import static org.eclipse.jgit.lib.ObjectIdSerializer.writeWithoutMarker;
import com.google.common.collect.ImmutableBiMap;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
+import com.google.gerrit.git.ObjectIds;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@@ -82,7 +83,7 @@ public class PatchListKey implements Serializable {
private transient Whitespace whitespace;
private PatchListKey(AnyObjectId a, AnyObjectId b, Whitespace ws) {
- oldId = a != null ? a.copy() : null;
+ oldId = ObjectIds.copyOrNull(a);
newId = b.copy();
whitespace = ws;
}
diff --git a/java/com/google/gerrit/server/patch/PatchListLoader.java b/java/com/google/gerrit/server/patch/PatchListLoader.java
index 08de537305..c3d9a1d3e8 100644
--- a/java/com/google/gerrit/server/patch/PatchListLoader.java
+++ b/java/com/google/gerrit/server/patch/PatchListLoader.java
@@ -28,9 +28,9 @@ import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -314,12 +314,12 @@ public class PatchListLoader implements Callable<PatchList> {
}
private static boolean areParentChild(RevCommit commitA, RevCommit commitB) {
- return ObjectId.equals(commitA.getParent(0), commitB)
- || ObjectId.equals(commitB.getParent(0), commitA);
+ return ObjectId.isEqual(commitA.getParent(0), commitB)
+ || ObjectId.isEqual(commitB.getParent(0), commitA);
}
private static boolean haveCommonParent(RevCommit commitA, RevCommit commitB) {
- return ObjectId.equals(commitA.getParent(0), commitB.getParent(0));
+ return ObjectId.isEqual(commitA.getParent(0), commitB.getParent(0));
}
private static Set<String> getTouchedFilePaths(PatchListEntry patchListEntry) {
diff --git a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index c2caa01b75..1c1c639c50 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -21,14 +21,14 @@ import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchScript.DisplayMethod;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.prettify.common.EditList;
import com.google.gerrit.prettify.common.SparseFileContent;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.mime.FileTypeRegistry;
import com.google.inject.Inject;
import eu.medsea.mimeutil.MimeType;
@@ -37,6 +37,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.errors.CorruptObjectException;
@@ -120,7 +121,6 @@ class PatchScriptBuilder {
private PatchScript build(PatchListEntry content, CommentDetail comments, List<Patch> history)
throws IOException {
- boolean intralineDifferenceIsPossible = true;
boolean intralineFailure = false;
boolean intralineTimeout = false;
@@ -146,21 +146,17 @@ class PatchScriptBuilder {
break;
case DISABLED:
- intralineDifferenceIsPossible = false;
break;
case ERROR:
- intralineDifferenceIsPossible = false;
intralineFailure = true;
break;
case TIMEOUT:
- intralineDifferenceIsPossible = false;
intralineTimeout = true;
break;
}
} else {
- intralineDifferenceIsPossible = false;
intralineFailure = true;
}
}
@@ -220,7 +216,6 @@ class PatchScriptBuilder {
comments,
history,
hugeFile,
- intralineDifferenceIsPossible,
intralineFailure,
intralineTimeout,
content.getPatchType() == Patch.PatchType.BINARY,
@@ -514,8 +509,7 @@ class PatchScriptBuilder {
try {
final boolean reuse;
if (Patch.COMMIT_MSG.equals(path)) {
- if (comparisonType.isAgainstParentOrAutoMerge()
- && (aId == within || within.equals(aId))) {
+ if (comparisonType.isAgainstParentOrAutoMerge() && Objects.equals(aId, within)) {
id = ObjectId.zeroId();
src = Text.EMPTY;
srcContent = Text.NO_BYTES;
@@ -534,8 +528,7 @@ class PatchScriptBuilder {
}
reuse = false;
} else if (Patch.MERGE_LIST.equals(path)) {
- if (comparisonType.isAgainstParentOrAutoMerge()
- && (aId == within || within.equals(aId))) {
+ if (comparisonType.isAgainstParentOrAutoMerge() && Objects.equals(aId, within)) {
id = ObjectId.zeroId();
src = Text.EMPTY;
srcContent = Text.NO_BYTES;
diff --git a/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index a7e77383ac..319a5bcbde 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -15,20 +15,21 @@
package com.google.gerrit.server.patch;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Patch.ChangeType;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
@@ -136,7 +137,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
this.psb = patchSetB;
this.diffPrefs = diffPrefs;
- changeId = patchSetB.getParentKey();
+ changeId = patchSetB.changeId();
}
@AssistedInject
@@ -172,7 +173,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
this.psb = patchSetB;
this.diffPrefs = diffPrefs;
- changeId = patchSetB.getParentKey();
+ changeId = patchSetB.changeId();
checkArgument(parentNum >= 0, "parentNum must be >= 0");
}
@@ -188,19 +189,28 @@ public class PatchScriptFactory implements Callable<PatchScript> {
public PatchScript call()
throws LargeObjectException, AuthException, InvalidChangeOperationException, IOException,
PermissionBackendException {
- if (parentNum < 0) {
- validatePatchSetId(psa);
- }
+ validatePatchSetId(psa);
validatePatchSetId(psb);
- PatchSet psEntityA = psa != null ? psUtil.get(notes, psa) : null;
- PatchSet psEntityB = psb.get() == 0 ? new PatchSet(psb) : psUtil.get(notes, psb);
- if (psEntityA != null || psEntityB != null) {
- try {
- permissionBackend.currentUser().change(notes).check(ChangePermission.READ);
- } catch (AuthException e) {
- throw new NoSuchChangeException(changeId, e);
- }
+ if (psa != null) {
+ checkState(parentNum < 0, "expected no parentNum when psa is present");
+ checkArgument(psa.get() != 0, "edit not supported for left side");
+ aId = getCommitId(psa);
+ } else {
+ aId = null;
+ }
+
+ if (psb.get() != 0) {
+ bId = getCommitId(psb);
+ } else {
+ // Change edit: create synthetic PatchSet corresponding to the edit.
+ bId = getEditRev();
+ }
+
+ try {
+ permissionBackend.currentUser().change(notes).check(ChangePermission.READ);
+ } catch (AuthException e) {
+ throw new NoSuchChangeException(changeId, e);
}
if (!projectCache.checkedGet(notes.getProjectName()).statePermitsRead()) {
@@ -208,11 +218,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
}
try (Repository git = repoManager.openRepository(notes.getProjectName())) {
- bId = toObjectId(psEntityB);
- if (parentNum < 0) {
- aId = psEntityA != null ? toObjectId(psEntityA) : null;
- }
-
try {
final PatchList list = listFor(keyFor(diffPrefs.ignoreWhitespace));
final PatchScriptBuilder b = newBuilder(list, git);
@@ -258,20 +263,12 @@ public class PatchScriptFactory implements Callable<PatchScript> {
return b;
}
- private ObjectId toObjectId(PatchSet ps) throws AuthException, IOException {
- if (ps.getId().get() == 0) {
- return getEditRev();
- }
- if (ps.getRevision() == null || ps.getRevision().get() == null) {
- throw new NoSuchChangeException(changeId);
- }
-
- try {
- return ObjectId.fromString(ps.getRevision().get());
- } catch (IllegalArgumentException e) {
- logger.atSevere().log("Patch set %s has invalid revision", ps.getId());
- throw new NoSuchChangeException(changeId, e);
+ private ObjectId getCommitId(PatchSet.Id psId) {
+ PatchSet ps = psUtil.get(notes, psId);
+ if (ps == null) {
+ throw new NoSuchChangeException(psId.changeId());
}
+ return ps.commitId();
}
private ObjectId getEditRev() throws AuthException, IOException {
@@ -284,7 +281,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
private void validatePatchSetId(PatchSet.Id psId) throws NoSuchChangeException {
if (psId == null) { // OK, means use base;
- } else if (changeId.equals(psId.getParentKey())) { // OK, same change;
+ } else if (changeId.equals(psId.changeId())) { // OK, same change;
} else {
throw new NoSuchChangeException(changeId);
}
@@ -306,7 +303,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
switch (changeType) {
case COPIED:
case RENAMED:
- if (ps.getId().equals(psa)) {
+ if (ps.id().equals(psa)) {
name = oldName;
}
break;
@@ -319,12 +316,12 @@ public class PatchScriptFactory implements Callable<PatchScript> {
}
}
- Patch p = new Patch(new Patch.Key(ps.getId(), name));
+ Patch p = new Patch(Patch.key(ps.id(), name));
history.add(p);
byKey.put(p.getKey(), p);
}
if (edit != null && edit.isPresent()) {
- Patch p = new Patch(new Patch.Key(new PatchSet.Id(psb.getParentKey(), 0), fileName));
+ Patch p = new Patch(Patch.key(PatchSet.id(psb.changeId(), 0), fileName));
history.add(p);
byKey.put(p.getKey(), p);
}
@@ -385,8 +382,8 @@ public class PatchScriptFactory implements Callable<PatchScript> {
private void loadPublished(Map<Patch.Key, Patch> byKey, String file) {
for (Comment c : commentsUtil.publishedByChangeFile(notes, file)) {
comments.include(notes.getChangeId(), c);
- PatchSet.Id psId = new PatchSet.Id(notes.getChangeId(), c.key.patchSetId);
- Patch.Key pKey = new Patch.Key(psId, c.key.filename);
+ PatchSet.Id psId = PatchSet.id(notes.getChangeId(), c.key.patchSetId);
+ Patch.Key pKey = Patch.key(psId, c.key.filename);
Patch p = byKey.get(pKey);
if (p != null) {
p.setCommentCount(p.getCommentCount() + 1);
@@ -397,8 +394,8 @@ public class PatchScriptFactory implements Callable<PatchScript> {
private void loadDrafts(Map<Patch.Key, Patch> byKey, Account.Id me, String file) {
for (Comment c : commentsUtil.draftByChangeFileAuthor(notes, file, me)) {
comments.include(notes.getChangeId(), c);
- PatchSet.Id psId = new PatchSet.Id(notes.getChangeId(), c.key.patchSetId);
- Patch.Key pKey = new Patch.Key(psId, c.key.filename);
+ PatchSet.Id psId = PatchSet.id(notes.getChangeId(), c.key.patchSetId);
+ Patch.Key pKey = Patch.key(psId, c.key.filename);
Patch p = byKey.get(pKey);
if (p != null) {
p.setDraftCount(p.getDraftCount() + 1);
diff --git a/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java b/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
index 0eb5588cb8..019fe1574a 100644
--- a/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
@@ -14,13 +14,10 @@
package com.google.gerrit.server.patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetInfo;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -28,13 +25,9 @@ import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -58,9 +51,9 @@ public class PatchSetInfoFactory {
PatchSetInfo info = new PatchSetInfo(psi);
info.setSubject(src.getShortMessage());
info.setMessage(src.getFullMessage());
- info.setAuthor(toUserIdentity(src.getAuthorIdent()));
- info.setCommitter(toUserIdentity(src.getCommitterIdent()));
- info.setRevId(src.getName());
+ info.setAuthor(emails.toUserIdentity(src.getAuthorIdent()));
+ info.setCommitter(emails.toUserIdentity(src.getCommitterIdent()));
+ info.setCommitId(src);
return info;
}
@@ -78,8 +71,8 @@ public class PatchSetInfoFactory {
throws PatchSetInfoNotAvailableException {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
- final RevCommit src = rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
- PatchSetInfo info = get(rw, src, patchSet.getId());
+ RevCommit src = rw.parseCommit(patchSet.commitId());
+ PatchSetInfo info = get(rw, src, patchSet.id());
info.setParents(toParentInfos(src.getParents(), rw));
return info;
} catch (IOException | StorageException e) {
@@ -87,33 +80,13 @@ public class PatchSetInfoFactory {
}
}
- // TODO: The same method exists in EventFactory, find a common place for it
- private UserIdentity toUserIdentity(PersonIdent who) throws IOException {
- final UserIdentity u = new UserIdentity();
- u.setName(who.getName());
- u.setEmail(who.getEmailAddress());
- u.setDate(new Timestamp(who.getWhen().getTime()));
- u.setTimeZone(who.getTimeZoneOffset());
-
- // If only one account has access to this email address, select it
- // as the identity of the user.
- //
- Set<Account.Id> a = emails.getAccountFor(u.getEmail());
- if (a.size() == 1) {
- u.setAccount(a.iterator().next());
- }
-
- return u;
- }
-
private List<PatchSetInfo.ParentInfo> toParentInfos(RevCommit[] parents, RevWalk walk)
throws IOException, MissingObjectException {
List<PatchSetInfo.ParentInfo> pInfos = new ArrayList<>(parents.length);
for (RevCommit parent : parents) {
walk.parseBody(parent);
- RevId rev = new RevId(parent.getId().name());
String msg = parent.getShortMessage();
- pInfos.add(new PatchSetInfo.ParentInfo(rev, msg));
+ pInfos.add(new PatchSetInfo.ParentInfo(parent, msg));
}
return pInfos;
}
diff --git a/java/com/google/gerrit/server/patch/Text.java b/java/com/google/gerrit/server/patch/Text.java
index 011185d2fc..cc0a5e40e4 100644
--- a/java/com/google/gerrit/server/patch/Text.java
+++ b/java/com/google/gerrit/server/patch/Text.java
@@ -18,6 +18,7 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.git.ObjectIds;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
@@ -62,7 +63,7 @@ public class Text extends RawText {
RevCommit p = c.getParent(0);
rw.parseBody(p);
b.append("Parent: ");
- b.append(reader.abbreviate(p, 8).name());
+ b.append(abbreviateName(p, reader));
b.append(" (");
b.append(p.getShortMessage());
b.append(")\n");
@@ -73,7 +74,7 @@ public class Text extends RawText {
RevCommit p = c.getParent(i);
rw.parseBody(p);
b.append(i == 0 ? "Merge Of: " : " ");
- b.append(reader.abbreviate(p, 8).name());
+ b.append(abbreviateName(p, reader));
b.append(" (");
b.append(p.getShortMessage());
b.append(")\n");
@@ -106,7 +107,7 @@ public class Text extends RawText {
b.append("Merge List:\n\n");
for (RevCommit commit : MergeListBuilder.build(rw, c, uniterestingParent)) {
b.append("* ");
- b.append(reader.abbreviate(commit, 8).name());
+ b.append(abbreviateName(commit, reader));
b.append(" ");
b.append(commit.getShortMessage());
b.append("\n");
@@ -116,6 +117,10 @@ public class Text extends RawText {
}
}
+ private static String abbreviateName(RevCommit p, ObjectReader reader) throws IOException {
+ return ObjectIds.abbreviateName(p, 8, reader);
+ }
+
private static void appendPersonIdent(StringBuilder b, String field, PersonIdent person) {
if (person != null) {
b.append(field).append(": ");
diff --git a/java/com/google/gerrit/server/permissions/ChangeControl.java b/java/com/google/gerrit/server/permissions/ChangeControl.java
index ee36200283..07cb50d34b 100644
--- a/java/com/google/gerrit/server/permissions/ChangeControl.java
+++ b/java/com/google/gerrit/server/permissions/ChangeControl.java
@@ -22,12 +22,12 @@ import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
index b23c85f246..6142bc0245 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
@@ -22,13 +22,13 @@ import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.api.access.PluginPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PeerDaemonUser;
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index 7fa712d5d4..67bfcb94dd 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.permissions;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CACHE_AUTOMERGE;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_CONFIG;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS_SELF;
+import static com.google.gerrit.entities.RefNames.REFS_CACHE_AUTOMERGE;
+import static com.google.gerrit.entities.RefNames.REFS_CONFIG;
+import static com.google.gerrit.entities.RefNames.REFS_USERS_SELF;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toMap;
@@ -30,17 +30,17 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.metrics.Counter0;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupCache;
@@ -49,6 +49,8 @@ import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.git.TagMatcher;
import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeNotes.Factory.ChangeNotesResult;
import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
@@ -66,7 +68,6 @@ import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.SymbolicRef;
class DefaultRefFilter {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -88,7 +89,7 @@ class DefaultRefFilter {
private final Counter0 skipFilterCount;
private final boolean skipFullRefEvaluationIfAllRefsAreVisible;
- private Map<Change.Id, Branch.NameKey> visibleChanges;
+ private Map<Change.Id, BranchNameKey> visibleChanges;
@Inject
DefaultRefFilter(
@@ -129,69 +130,100 @@ class DefaultRefFilter {
/** Filters given refs and tags by visibility. */
Map<String, Ref> filter(Map<String, Ref> refs, Repository repo, RefFilterOptions opts)
throws PermissionBackendException {
+ logger.atFinest().log(
+ "Filter refs for repository %s by visibility (options = %s, refs = %s)",
+ projectState.getNameKey(), opts, refs);
+ logger.atFinest().log("Calling user: %s", user.getLoggableName());
+ logger.atFinest().log("Groups: %s", user.getEffectiveGroups().getKnownGroups());
+ logger.atFinest().log(
+ "auth.skipFullRefEvaluationIfAllRefsAreVisible = %s",
+ skipFullRefEvaluationIfAllRefsAreVisible);
+ logger.atFinest().log(
+ "Project state %s permits read = %s",
+ projectState.getProject().getState(), projectState.statePermitsRead());
+
// See if we can get away with a single, cheap ref evaluation.
if (refs.size() == 1) {
String refName = Iterables.getOnlyElement(refs.values()).getName();
if (opts.filterMeta() && isMetadata(refName)) {
+ logger.atFinest().log("Filter out metadata ref %s", refName);
return ImmutableMap.of();
}
if (RefNames.isRefsChanges(refName)) {
- return canSeeSingleChangeRef(refName) ? refs : ImmutableMap.of();
+ boolean isChangeRefVisisble = canSeeSingleChangeRef(refName);
+ if (isChangeRefVisisble) {
+ logger.atFinest().log("Change ref %s is visible", refName);
+ return refs;
+ }
+ logger.atFinest().log("Filter out non-visible change ref %s", refName);
+ return ImmutableMap.of();
}
}
// Perform an initial ref filtering with all the refs the caller asked for. If we find tags that
- // we have to investigate (deferred tags) separately then perform a reachability check starting
+ // we have to investigate separately (deferred tags) then perform a reachability check starting
// from all visible branches (refs/heads/*).
Result initialRefFilter = filterRefs(refs, repo, opts);
Map<String, Ref> visibleRefs = initialRefFilter.visibleRefs();
if (!initialRefFilter.deferredTags().isEmpty()) {
- Result allVisibleBranches = filterRefs(getTaggableRefsMap(repo), repo, opts);
- checkState(
- allVisibleBranches.deferredTags().isEmpty(),
- "unexpected tags found when filtering refs/heads/* " + allVisibleBranches.deferredTags());
-
- TagMatcher tags =
- tagCache
- .get(projectState.getNameKey())
- .matcher(tagCache, repo, allVisibleBranches.visibleRefs().values());
- for (Ref tag : initialRefFilter.deferredTags()) {
- try {
- if (tags.isReachable(tag)) {
- visibleRefs.put(tag.getName(), tag);
+ try (TraceTimer traceTimer = TraceContext.newTimer("Check visibility of deferred tags")) {
+ Result allVisibleBranches = filterRefs(getTaggableRefsMap(repo), repo, opts);
+ checkState(
+ allVisibleBranches.deferredTags().isEmpty(),
+ "unexpected tags found when filtering refs/heads/* "
+ + allVisibleBranches.deferredTags());
+
+ TagMatcher tags =
+ tagCache
+ .get(projectState.getNameKey())
+ .matcher(tagCache, repo, allVisibleBranches.visibleRefs().values());
+ for (Ref tag : initialRefFilter.deferredTags()) {
+ try {
+ if (tags.isReachable(tag)) {
+ logger.atFinest().log("Include reachable tag %s", tag.getName());
+ visibleRefs.put(tag.getName(), tag);
+ } else {
+ logger.atFinest().log("Filter out non-reachable tag %s", tag.getName());
+ }
+ } catch (IOException e) {
+ throw new PermissionBackendException(e);
}
- } catch (IOException e) {
- throw new PermissionBackendException(e);
}
}
}
+
+ logger.atFinest().log("visible refs = %s", visibleRefs);
return visibleRefs;
}
/**
* Filters refs by visibility. Returns tags where visibility can't be trivially computed
- * separately for later ref-walk-based visibility computation. Tags where visibility is trivial to
+ * separately for later rev-walk-based visibility computation. Tags where visibility is trivial to
* compute will be returned as part of {@link Result#visibleRefs()}.
*/
Result filterRefs(Map<String, Ref> refs, Repository repo, RefFilterOptions opts)
throws PermissionBackendException {
- if (projectState.isAllUsers()) {
- refs = addUsersSelfSymref(refs);
- }
+ logger.atFinest().log("Filter refs (refs = %s)", refs);
// TODO(hiesel): Remove when optimization is done.
boolean hasReadOnRefsStar =
checkProjectPermission(permissionBackendForProject, ProjectPermission.READ);
+ logger.atFinest().log("User has READ on refs/* = %s", hasReadOnRefsStar);
if (skipFullRefEvaluationIfAllRefsAreVisible && !projectState.isAllUsers()) {
if (projectState.statePermitsRead() && hasReadOnRefsStar) {
skipFilterCount.increment();
+ logger.atFinest().log(
+ "Fast path, all refs are visible because user has READ on refs/*: %s", refs);
return new AutoValue_DefaultRefFilter_Result(refs, ImmutableList.of());
} else if (projectControl.allRefsAreVisible(ImmutableSet.of(RefNames.REFS_CONFIG))) {
skipFilterCount.increment();
- return new AutoValue_DefaultRefFilter_Result(
- fastHideRefsMetaConfig(refs), ImmutableList.of());
+ refs = fastHideRefsMetaConfig(refs);
+ logger.atFinest().log(
+ "Fast path, all refs except %s are visible: %s", RefNames.REFS_CONFIG, refs);
+ return new AutoValue_DefaultRefFilter_Result(refs, ImmutableList.of());
}
}
+ logger.atFinest().log("Doing full ref filtering");
fullFilterCount.increment();
boolean viewMetadata;
@@ -204,36 +236,52 @@ class DefaultRefFilter {
isAdmin = withUser.testOrFalse(GlobalPermission.ADMINISTRATE_SERVER);
identifiedUser = user.asIdentifiedUser();
userId = identifiedUser.getAccountId();
+ logger.atFinest().log(
+ "Account = %d; can view metadata = %s; is admin = %s",
+ userId.get(), viewMetadata, isAdmin);
} else {
+ logger.atFinest().log("User is anonymous");
viewMetadata = false;
isAdmin = false;
userId = null;
identifiedUser = null;
}
- Map<String, Ref> result = new HashMap<>();
+ Map<String, Ref> resultRefs = new HashMap<>();
List<Ref> deferredTags = new ArrayList<>();
for (Ref ref : refs.values()) {
String name = ref.getName();
Change.Id changeId;
Account.Id accountId;
AccountGroup.UUID accountGroupUuid;
- if (name.startsWith(REFS_CACHE_AUTOMERGE) || (opts.filterMeta() && isMetadata(name))) {
+ if (name.startsWith(REFS_CACHE_AUTOMERGE)) {
+ continue;
+ } else if (opts.filterMeta() && isMetadata(name)) {
+ logger.atFinest().log("Filter out metadata ref %s", name);
continue;
} else if (RefNames.isRefsEdit(name)) {
// Edits are visible only to the owning user, if change is visible.
if (viewMetadata || visibleEdit(repo, name)) {
- result.put(name, ref);
+ logger.atFinest().log("Include edit ref %s", name);
+ resultRefs.put(name, ref);
+ } else {
+ logger.atFinest().log("Filter out edit ref %s", name);
}
} else if ((changeId = Change.Id.fromRef(name)) != null) {
// Change ref is visible only if the change is visible.
if (viewMetadata || visible(repo, changeId)) {
- result.put(name, ref);
+ logger.atFinest().log("Include change ref %s", name);
+ resultRefs.put(name, ref);
+ } else {
+ logger.atFinest().log("Filter out change ref %s", name);
}
} else if ((accountId = Account.Id.fromRef(name)) != null) {
// Account ref is visible only to the corresponding account.
if (viewMetadata || (accountId.equals(userId) && canReadRef(name))) {
- result.put(name, ref);
+ logger.atFinest().log("Include user ref %s", name);
+ resultRefs.put(name, ref);
+ } else {
+ logger.atFinest().log("Filter out user ref %s", name);
}
} else if ((accountGroupUuid = AccountGroup.UUID.fromRef(name)) != null) {
// Group ref is visible only to the corresponding owner group.
@@ -242,7 +290,10 @@ class DefaultRefFilter {
|| (group != null
&& isGroupOwner(group, identifiedUser, isAdmin)
&& canReadRef(name))) {
- result.put(name, ref);
+ logger.atFinest().log("Include group ref %s", name);
+ resultRefs.put(name, ref);
+ } else {
+ logger.atFinest().log("Filter out group ref %s", name);
}
} else if (isTag(ref)) {
if (hasReadOnRefsStar) {
@@ -255,39 +306,56 @@ class DefaultRefFilter {
// (e.g. a private change ref) if a tag was attached to it. Tags are meant to be used on
// the regular Git tree that users interact with, not on any of the Gerrit trees, so this
// is a negligible risk.
- result.put(name, ref);
+ logger.atFinest().log("Include tag ref %s because user has read on refs/*", name);
+ resultRefs.put(name, ref);
} else {
// If its a tag, consider it later.
if (ref.getObjectId() != null) {
+ logger.atFinest().log("Defer tag ref %s", name);
deferredTags.add(ref);
+ } else {
+ logger.atFinest().log("Filter out tag ref %s that is not a tag", name);
}
}
} else if (name.startsWith(RefNames.REFS_SEQUENCES)) {
// Sequences are internal database implementation details.
if (viewMetadata) {
- result.put(name, ref);
+ logger.atFinest().log("Include sequence ref %s", name);
+ resultRefs.put(name, ref);
+ } else {
+ logger.atFinest().log("Filter out sequence ref %s", name);
}
} else if (projectState.isAllUsers()
&& (name.equals(RefNames.REFS_EXTERNAL_IDS) || name.equals(RefNames.REFS_GROUPNAMES))) {
// The notes branches with the external IDs / group names must not be exposed to normal
// users.
if (viewMetadata) {
- result.put(name, ref);
+ logger.atFinest().log("Include external IDs branch %s", name);
+ resultRefs.put(name, ref);
+ } else {
+ logger.atFinest().log("Filter out external IDs branch %s", name);
}
} else if (canReadRef(ref.getLeaf().getName())) {
// Use the leaf to lookup the control data. If the reference is
// symbolic we want the control around the final target. If its
// not symbolic then getLeaf() is a no-op returning ref itself.
- result.put(name, ref);
+ logger.atFinest().log(
+ "Include ref %s because its leaf %s is readable", name, ref.getLeaf().getName());
+ resultRefs.put(name, ref);
} else if (isRefsUsersSelf(ref)) {
// viewMetadata allows to see all account refs, hence refs/users/self should be included as
// well
if (viewMetadata) {
- result.put(name, ref);
+ logger.atFinest().log("Include ref %s", REFS_USERS_SELF);
+ resultRefs.put(name, ref);
}
+ } else {
+ logger.atFinest().log("Filter out ref %s", name);
}
}
- return new AutoValue_DefaultRefFilter_Result(result, deferredTags);
+ Result result = new AutoValue_DefaultRefFilter_Result(resultRefs, deferredTags);
+ logger.atFinest().log("Result of ref filtering = %s", result);
+ return result;
}
/**
@@ -322,18 +390,6 @@ class DefaultRefFilter {
return refs;
}
- private Map<String, Ref> addUsersSelfSymref(Map<String, Ref> refs) {
- if (user.isIdentifiedUser()) {
- Ref r = refs.get(RefNames.refsUsers(user.getAccountId()));
- if (r != null) {
- SymbolicRef s = new SymbolicRef(REFS_USERS_SELF, r);
- refs = new HashMap<>(refs);
- refs.put(s.getName(), s);
- }
- }
- return refs;
- }
-
private boolean visible(Repository repo, Change.Id changeId) throws PermissionBackendException {
if (visibleChanges == null) {
if (changeCache == null) {
@@ -341,44 +397,51 @@ class DefaultRefFilter {
} else {
visibleChanges = visibleChangesBySearch();
}
+ logger.atFinest().log("Visible changes: %s", visibleChanges.keySet());
}
return visibleChanges.containsKey(changeId);
}
private boolean visibleEdit(Repository repo, String name) throws PermissionBackendException {
Change.Id id = Change.Id.fromEditRefPart(name);
- // Initialize if it wasn't yet
- if (visibleChanges == null) {
- visible(repo, id);
- }
if (id == null) {
+ logger.atWarning().log("Couldn't extract change ID from edit ref %s", name);
return false;
}
if (user.isIdentifiedUser()
&& name.startsWith(RefNames.refsEditPrefix(user.asIdentifiedUser().getAccountId()))
&& visible(repo, id)) {
+ logger.atFinest().log("Own change edit ref is visible: %s", name);
return true;
}
+
+ // Initialize visibleChanges if it wasn't initialized yet.
+ if (visibleChanges == null) {
+ visible(repo, id);
+ }
if (visibleChanges.containsKey(id)) {
try {
// Default to READ_PRIVATE_CHANGES as there is no special permission for reading edits.
permissionBackendForProject
- .ref(visibleChanges.get(id).get())
+ .ref(visibleChanges.get(id).branch())
.check(RefPermission.READ_PRIVATE_CHANGES);
+ logger.atFinest().log("Foreign change edit ref is visible: %s", name);
return true;
} catch (AuthException e) {
+ logger.atFinest().log("Foreign change edit ref is not visible: %s", name);
return false;
}
}
+
+ logger.atFinest().log("Change %d of change edit ref %s is not visible", id.get(), name);
return false;
}
- private Map<Change.Id, Branch.NameKey> visibleChangesBySearch()
- throws PermissionBackendException {
+ private Map<Change.Id, BranchNameKey> visibleChangesBySearch() throws PermissionBackendException {
Project.NameKey project = projectState.getNameKey();
try {
- Map<Change.Id, Branch.NameKey> visibleChanges = new HashMap<>();
+ Map<Change.Id, BranchNameKey> visibleChanges = new HashMap<>();
for (ChangeData cd : changeCache.getChangeData(project)) {
ChangeNotes notes = changeNotesFactory.createFromIndexedChange(cd.change());
if (!projectState.statePermitsRead()) {
@@ -399,7 +462,7 @@ class DefaultRefFilter {
}
}
- private Map<Change.Id, Branch.NameKey> visibleChangesByScan(Repository repo)
+ private Map<Change.Id, BranchNameKey> visibleChangesByScan(Repository repo)
throws PermissionBackendException {
Project.NameKey p = projectState.getNameKey();
ImmutableList<ChangeNotesResult> changes;
@@ -411,7 +474,7 @@ class DefaultRefFilter {
return Collections.emptyMap();
}
- Map<Change.Id, Branch.NameKey> result = Maps.newHashMapWithExpectedSize(changes.size());
+ Map<Change.Id, BranchNameKey> result = Maps.newHashMapWithExpectedSize(changes.size());
for (ChangeNotesResult notesResult : changes) {
ChangeNotes notes = toNotes(notesResult);
if (notes != null) {
@@ -443,7 +506,9 @@ class DefaultRefFilter {
}
private boolean isMetadata(String name) {
- return RefNames.isRefsChanges(name) || RefNames.isRefsEdit(name);
+ boolean isMetaData = RefNames.isRefsChanges(name) || RefNames.isRefsEdit(name);
+ logger.atFinest().log("ref %s is " + (isMetaData ? "" : "not ") + "a metadata ref", name);
+ return isMetaData;
}
private static boolean isTag(Ref ref) {
@@ -479,8 +544,10 @@ class DefaultRefFilter {
requireNonNull(group);
// Keep this logic in sync with GroupControl#isOwner().
- return isAdmin
- || (user != null && user.getEffectiveGroups().contains(group.getOwnerGroupUUID()));
+ boolean isGroupOwner =
+ isAdmin || (user != null && user.getEffectiveGroups().contains(group.getOwnerGroupUUID()));
+ logger.atFinest().log("User is owner of group %s = %s", group.getGroupUUID(), isGroupOwner);
+ return isGroupOwner;
}
/**
diff --git a/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java b/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
index 5c7ee0d81d..0800d6b696 100644
--- a/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.permissions;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
import com.google.gerrit.server.permissions.PermissionBackend.ForProject;
diff --git a/java/com/google/gerrit/server/permissions/PermissionBackend.java b/java/com/google/gerrit/server/permissions/PermissionBackend.java
index 7960c65f1e..e0c5927f96 100644
--- a/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -23,15 +23,15 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.query.change.ChangeData;
@@ -157,8 +157,8 @@ public abstract class PermissionBackend {
public abstract ForProject project(Project.NameKey project);
/** Returns an instance scoped for the {@code ref}, and its parent project. */
- public ForRef ref(Branch.NameKey ref) {
- return project(ref.getParentKey()).ref(ref.get());
+ public ForRef ref(BranchNameKey ref) {
+ return project(ref.project()).ref(ref.branch());
}
/** Returns an instance scoped for the change, and its destination ref and project. */
@@ -280,7 +280,7 @@ public abstract class PermissionBackend {
/** Returns an instance scoped for the change, and its destination ref and project. */
public ForChange change(ChangeData cd) {
try {
- return ref(cd.change().getDest().get()).change(cd);
+ return ref(cd.change().getDest().branch()).change(cd);
} catch (StorageException e) {
return FailedPermissionBackend.change("unavailable", e);
}
@@ -288,7 +288,7 @@ public abstract class PermissionBackend {
/** Returns an instance scoped for the change, and its destination ref and project. */
public ForChange change(ChangeNotes notes) {
- return ref(notes.getChange().getDest().get()).change(notes);
+ return ref(notes.getChange().getDest().branch()).change(notes);
}
/**
@@ -297,7 +297,7 @@ public abstract class PermissionBackend {
* stale data from the index is acceptable.
*/
public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
- return ref(notes.getChange().getDest().get()).indexedChange(cd, notes);
+ return ref(notes.getChange().getDest().branch()).indexedChange(cd, notes);
}
/** Verify scoped user can {@code perm}, throwing if denied. */
diff --git a/java/com/google/gerrit/server/permissions/PermissionCollection.java b/java/com/google/gerrit/server/permissions/PermissionCollection.java
index 8ecb274be6..1f0370b74f 100644
--- a/java/com/google/gerrit/server/permissions/PermissionCollection.java
+++ b/java/com/google/gerrit/server/permissions/PermissionCollection.java
@@ -27,12 +27,12 @@ import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer0;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.project.RefPattern;
import com.google.gerrit.server.project.RefPatternMatcher.ExpandParameters;
diff --git a/java/com/google/gerrit/server/permissions/ProjectControl.java b/java/com/google/gerrit/server/permissions/ProjectControl.java
index dfc33395f2..cc3b6662db 100644
--- a/java/com/google/gerrit/server/permissions/ProjectControl.java
+++ b/java/com/google/gerrit/server/permissions/ProjectControl.java
@@ -17,23 +17,23 @@ package com.google.gerrit.server.permissions;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.common.data.AccessSection.ALL;
import static com.google.gerrit.common.data.AccessSection.REGEX_PREFIX;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_TAGS;
+import static com.google.gerrit.entities.RefNames.REFS_TAGS;
import static com.google.gerrit.server.util.MagicBranch.NEW_CHANGE;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
import com.google.gerrit.extensions.api.access.PluginProjectPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.config.GitReceivePackGroups;
@@ -111,8 +111,8 @@ class ProjectControl {
return changeControlFactory.create(controlForRef(notes.getChange().getDest()), notes);
}
- RefControl controlForRef(Branch.NameKey ref) {
- return controlForRef(ref.get());
+ RefControl controlForRef(BranchNameKey ref) {
+ return controlForRef(ref.branch());
}
public RefControl controlForRef(String refName) {
diff --git a/java/com/google/gerrit/server/permissions/ProjectPermission.java b/java/com/google/gerrit/server/permissions/ProjectPermission.java
index 653303a29c..fc31e964d9 100644
--- a/java/com/google/gerrit/server/permissions/ProjectPermission.java
+++ b/java/com/google/gerrit/server/permissions/ProjectPermission.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.permissions;
import static java.util.Objects.requireNonNull;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
import com.google.gerrit.extensions.api.access.GerritPermission;
-import com.google.gerrit.reviewdb.client.RefNames;
public enum ProjectPermission implements CoreOrPluginProjectPermission {
/**
diff --git a/java/com/google/gerrit/server/permissions/RefControl.java b/java/com/google/gerrit/server/permissions/RefControl.java
index 9a2ecdd17e..06fe4717ca 100644
--- a/java/com/google/gerrit/server/permissions/RefControl.java
+++ b/java/com/google/gerrit/server/permissions/RefControl.java
@@ -21,12 +21,12 @@ import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.logging.CallerFinder;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -395,7 +395,7 @@ class RefControl {
withForce,
projectControl.getProject().getName(),
refName,
- callerFinder.findCaller());
+ callerFinder.findCallerLazy());
return false;
}
@@ -408,7 +408,7 @@ class RefControl {
withForce,
projectControl.getProject().getName(),
refName,
- callerFinder.findCaller());
+ callerFinder.findCallerLazy());
return true;
}
}
@@ -420,7 +420,7 @@ class RefControl {
withForce,
projectControl.getProject().getName(),
refName,
- callerFinder.findCaller());
+ callerFinder.findCallerLazy());
return false;
}
diff --git a/java/com/google/gerrit/server/plugincontext/PluginContext.java b/java/com/google/gerrit/server/plugincontext/PluginContext.java
index 70b23e3b8b..90d56c8903 100644
--- a/java/com/google/gerrit/server/plugincontext/PluginContext.java
+++ b/java/com/google/gerrit/server/plugincontext/PluginContext.java
@@ -30,6 +30,7 @@ import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer3;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -118,25 +119,32 @@ public class PluginContext<T> {
@Inject
PluginMetrics(MetricMaker metricMaker) {
+ Field<String> pluginNameField =
+ Field.ofString("plugin_name", Metadata.Builder::pluginName).build();
+ Field<String> classNameField =
+ Field.ofString("class_name", Metadata.Builder::className).build();
+ Field<String> exportValueField =
+ Field.ofString("export_value", Metadata.Builder::exportValue).build();
+
this.latency =
metricMaker.newTimer(
"plugin/latency",
new Description("Latency for plugin invocation")
.setCumulative()
.setUnit(Units.MILLISECONDS),
- Field.ofString("plugin_name"),
- Field.ofString("class_name"),
- Field.ofString("export_name"));
+ pluginNameField,
+ classNameField,
+ exportValueField);
this.errorCount =
metricMaker.newCounter(
"plugin/error_count",
new Description("Number of plugin errors").setCumulative().setUnit("errors"),
- Field.ofString("plugin_name"),
- Field.ofString("class_name"),
- Field.ofString("export_name"));
+ pluginNameField,
+ classNameField,
+ exportValueField);
}
- Timer3.Context startLatency(Extension<?> extension) {
+ Timer3.Context<String, String, String> startLatency(Extension<?> extension) {
return latency.start(
extension.getPluginName(),
extension.get().getClass().getName(),
@@ -194,7 +202,7 @@ public class PluginContext<T> {
return;
}
try (TraceContext traceContext = newTrace(extension);
- Timer3.Context ctx = pluginMetrics.startLatency(extension)) {
+ Timer3.Context<String, String, String> ctx = pluginMetrics.startLatency(extension)) {
extensionImplConsumer.run(extensionImpl);
} catch (Throwable e) {
pluginMetrics.incrementErrorCount(extension);
@@ -223,7 +231,7 @@ public class PluginContext<T> {
}
try (TraceContext traceContext = newTrace(extension);
- Timer3.Context ctx = pluginMetrics.startLatency(extension)) {
+ Timer3.Context<String, String, String> ctx = pluginMetrics.startLatency(extension)) {
extensionConsumer.run(extension);
} catch (Throwable e) {
pluginMetrics.incrementErrorCount(extension);
@@ -257,14 +265,14 @@ public class PluginContext<T> {
}
try (TraceContext traceContext = newTrace(extension);
- Timer3.Context ctx = pluginMetrics.startLatency(extension)) {
+ Timer3.Context<String, String, String> ctx = pluginMetrics.startLatency(extension)) {
extensionImplConsumer.run(extensionImpl);
} catch (Throwable e) {
Throwables.throwIfInstanceOf(e, exceptionClass);
Throwables.throwIfUnchecked(e);
pluginMetrics.incrementErrorCount(extension);
logger.atWarning().withCause(e).log(
- "Failure in %s of plugin invoke%s", extensionImpl.getClass(), extension.getPluginName());
+ "Failure in %s of plugin %s", extensionImpl.getClass(), extension.getPluginName());
}
}
@@ -294,7 +302,7 @@ public class PluginContext<T> {
}
try (TraceContext traceContext = newTrace(extension);
- Timer3.Context ctx = pluginMetrics.startLatency(extension)) {
+ Timer3.Context<String, String, String> ctx = pluginMetrics.startLatency(extension)) {
extensionConsumer.run(extension);
} catch (Throwable e) {
Throwables.throwIfInstanceOf(e, exceptionClass);
@@ -320,7 +328,7 @@ public class PluginContext<T> {
Extension<T> extension,
ExtensionImplFunction<T, R> extensionImplFunction) {
try (TraceContext traceContext = newTrace(extension);
- Timer3.Context ctx = pluginMetrics.startLatency(extension)) {
+ Timer3.Context<String, String, String> ctx = pluginMetrics.startLatency(extension)) {
return extensionImplFunction.call(extension.get());
}
}
@@ -345,7 +353,7 @@ public class PluginContext<T> {
Class<X> exceptionClass)
throws X {
try (TraceContext traceContext = newTrace(extension);
- Timer3.Context ctx = pluginMetrics.startLatency(extension)) {
+ Timer3.Context<String, String, String> ctx = pluginMetrics.startLatency(extension)) {
try {
return checkedExtensionImplFunction.call(extension.get());
} catch (Exception e) {
@@ -374,7 +382,7 @@ public class PluginContext<T> {
Extension<T> extension,
ExtensionFunction<Extension<T>, R> extensionFunction) {
try (TraceContext traceContext = newTrace(extension);
- Timer3.Context ctx = pluginMetrics.startLatency(extension)) {
+ Timer3.Context<String, String, String> ctx = pluginMetrics.startLatency(extension)) {
return extensionFunction.call(extension);
}
}
@@ -400,7 +408,7 @@ public class PluginContext<T> {
Class<X> exceptionClass)
throws X {
try (TraceContext traceContext = newTrace(extension);
- Timer3.Context ctx = pluginMetrics.startLatency(extension)) {
+ Timer3.Context<String, String, String> ctx = pluginMetrics.startLatency(extension)) {
try {
return checkedExtensionFunction.call(extension);
} catch (Exception e) {
diff --git a/java/com/google/gerrit/server/plugins/CopyConfigModule.java b/java/com/google/gerrit/server/plugins/CopyConfigModule.java
index 090d257849..9b74341a9d 100644
--- a/java/com/google/gerrit/server/plugins/CopyConfigModule.java
+++ b/java/com/google/gerrit/server/plugins/CopyConfigModule.java
@@ -17,6 +17,8 @@ package com.google.gerrit.server.plugins;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.AnonymousCowardName;
+import com.google.gerrit.server.config.GerritIsReplica;
+import com.google.gerrit.server.config.GerritIsReplicaProvider;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
@@ -101,6 +103,14 @@ class CopyConfigModule extends AbstractModule {
return secureStore;
}
+ @Inject private GerritIsReplicaProvider isReplicaProvider;
+
+ @Provides
+ @GerritIsReplica
+ boolean getIsReplica() {
+ return isReplicaProvider.get();
+ }
+
@Inject
CopyConfigModule() {}
diff --git a/java/com/google/gerrit/server/plugins/DisablePlugin.java b/java/com/google/gerrit/server/plugins/DisablePlugin.java
index 62eb993118..8adae5223c 100644
--- a/java/com/google/gerrit/server/plugins/DisablePlugin.java
+++ b/java/com/google/gerrit/server/plugins/DisablePlugin.java
@@ -17,6 +17,8 @@ package com.google.gerrit.server.plugins;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.PluginInfo;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -30,15 +32,20 @@ public class DisablePlugin implements RestModifyView<PluginResource, Input> {
private final PluginLoader loader;
private final PermissionBackend permissionBackend;
+ private final MandatoryPluginsCollection mandatoryPluginsCollection;
@Inject
- DisablePlugin(PluginLoader loader, PermissionBackend permissionBackend) {
+ DisablePlugin(
+ PluginLoader loader,
+ PermissionBackend permissionBackend,
+ MandatoryPluginsCollection mandatoryPluginsCollection) {
this.loader = loader;
this.permissionBackend = permissionBackend;
+ this.mandatoryPluginsCollection = mandatoryPluginsCollection;
}
@Override
- public PluginInfo apply(PluginResource resource, Input input) throws RestApiException {
+ public Response<PluginInfo> apply(PluginResource resource, Input input) throws RestApiException {
try {
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
} catch (PermissionBackendException e) {
@@ -46,7 +53,10 @@ public class DisablePlugin implements RestModifyView<PluginResource, Input> {
}
loader.checkRemoteAdminEnabled();
String name = resource.getName();
+ if (mandatoryPluginsCollection.contains(name)) {
+ throw new MethodNotAllowedException("Plugin " + name + " is mandatory");
+ }
loader.disablePlugins(ImmutableSet.of(name));
- return ListPlugins.toPluginInfo(loader.get(name));
+ return Response.ok(ListPlugins.toPluginInfo(loader.get(name)));
}
}
diff --git a/java/com/google/gerrit/server/plugins/EnablePlugin.java b/java/com/google/gerrit/server/plugins/EnablePlugin.java
index 569bc39307..b45aaf1f69 100644
--- a/java/com/google/gerrit/server/plugins/EnablePlugin.java
+++ b/java/com/google/gerrit/server/plugins/EnablePlugin.java
@@ -20,6 +20,7 @@ import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.inject.Inject;
@@ -39,7 +40,7 @@ public class EnablePlugin implements RestModifyView<PluginResource, Input> {
}
@Override
- public PluginInfo apply(PluginResource resource, Input input) throws RestApiException {
+ public Response<PluginInfo> apply(PluginResource resource, Input input) throws RestApiException {
loader.checkRemoteAdminEnabled();
String name = resource.getName();
try {
@@ -52,6 +53,6 @@ public class EnablePlugin implements RestModifyView<PluginResource, Input> {
pw.flush();
throw new ResourceConflictException(buf.toString());
}
- return ListPlugins.toPluginInfo(loader.get(name));
+ return Response.ok(ListPlugins.toPluginInfo(loader.get(name)));
}
}
diff --git a/java/com/google/gerrit/server/plugins/GetStatus.java b/java/com/google/gerrit/server/plugins/GetStatus.java
index cbd864a2ce..5fcc96a526 100644
--- a/java/com/google/gerrit/server/plugins/GetStatus.java
+++ b/java/com/google/gerrit/server/plugins/GetStatus.java
@@ -15,13 +15,14 @@
package com.google.gerrit.server.plugins;
import com.google.gerrit.extensions.common.PluginInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.inject.Singleton;
@Singleton
public class GetStatus implements RestReadView<PluginResource> {
@Override
- public PluginInfo apply(PluginResource resource) {
- return ListPlugins.toPluginInfo(resource.getPlugin());
+ public Response<PluginInfo> apply(PluginResource resource) {
+ return Response.ok(ListPlugins.toPluginInfo(resource.getPlugin()));
}
}
diff --git a/java/com/google/gerrit/server/plugins/ListPlugins.java b/java/com/google/gerrit/server/plugins/ListPlugins.java
index 438a0c4a10..0408efc1b8 100644
--- a/java/com/google/gerrit/server/plugins/ListPlugins.java
+++ b/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -23,6 +23,7 @@ import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.plugins.Plugins;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.Url;
@@ -111,7 +112,8 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
}
@Override
- public SortedMap<String, PluginInfo> apply(TopLevelResource resource) throws BadRequestException {
+ public Response<SortedMap<String, PluginInfo>> apply(TopLevelResource resource)
+ throws BadRequestException {
Stream<Plugin> s = Streams.stream(pluginLoader.getPlugins(all));
if (matchPrefix != null) {
checkMatchOptions(matchSubstring == null && matchRegex == null);
@@ -132,7 +134,7 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
if (limit > 0) {
s = s.limit(limit);
}
- return new TreeMap<>(s.collect(toMap(Plugin::getName, ListPlugins::toPluginInfo)));
+ return Response.ok(new TreeMap<>(s.collect(toMap(Plugin::getName, ListPlugins::toPluginInfo))));
}
private void checkMatchOptions(boolean cond) throws BadRequestException {
diff --git a/java/com/google/gerrit/server/plugins/MandatoryPluginsCollection.java b/java/com/google/gerrit/server/plugins/MandatoryPluginsCollection.java
new file mode 100644
index 0000000000..70a0fff640
--- /dev/null
+++ b/java/com/google/gerrit/server/plugins/MandatoryPluginsCollection.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.plugins;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import org.eclipse.jgit.lib.Config;
+
+@Singleton
+public class MandatoryPluginsCollection {
+ private final CopyOnWriteArraySet<String> members;
+
+ @Inject
+ MandatoryPluginsCollection(@GerritServerConfig Config cfg) {
+ members = Sets.newCopyOnWriteArraySet();
+ members.addAll(Arrays.asList(cfg.getStringList("plugins", null, "mandatory")));
+ }
+
+ public boolean contains(String name) {
+ return members.contains(name);
+ }
+
+ public Set<String> asSet() {
+ return ImmutableSet.copyOf(members);
+ }
+
+ @VisibleForTesting
+ public void add(String name) {
+ members.add(name);
+ }
+}
diff --git a/java/com/google/gerrit/server/plugins/MissingMandatoryPluginsException.java b/java/com/google/gerrit/server/plugins/MissingMandatoryPluginsException.java
new file mode 100644
index 0000000000..1c23550261
--- /dev/null
+++ b/java/com/google/gerrit/server/plugins/MissingMandatoryPluginsException.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.plugins;
+
+import java.util.Collection;
+
+/** Raised when one or more mandatory plugins are missing. */
+public class MissingMandatoryPluginsException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public MissingMandatoryPluginsException(Collection<String> pluginNames) {
+ super(getMessage(pluginNames));
+ }
+
+ private static String getMessage(Collection<String> pluginNames) {
+ return String.format("Cannot find or load the following mandatory plugins: %s", pluginNames);
+ }
+}
diff --git a/java/com/google/gerrit/server/plugins/PluginLoader.java b/java/com/google/gerrit/server/plugins/PluginLoader.java
index 9279f0fee4..c4f4a1f22f 100644
--- a/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -52,6 +52,7 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -87,6 +88,7 @@ public class PluginLoader implements LifecycleListener {
private final Provider<String> urlProvider;
private final PersistentCacheFactory persistentCacheFactory;
private final boolean remoteAdmin;
+ private final MandatoryPluginsCollection mandatoryPlugins;
private final UniversalServerPluginProvider serverPluginFactory;
private final GerritRuntime gerritRuntime;
@@ -101,6 +103,7 @@ public class PluginLoader implements LifecycleListener {
@CanonicalWebUrl Provider<String> provider,
PersistentCacheFactory cacheFactory,
UniversalServerPluginProvider pluginFactory,
+ MandatoryPluginsCollection mpc,
GerritRuntime gerritRuntime) {
pluginsDir = sitePaths.plugins_dir;
dataDir = sitePaths.data_dir;
@@ -114,6 +117,7 @@ public class PluginLoader implements LifecycleListener {
serverPluginFactory = pluginFactory;
remoteAdmin = cfg.getBoolean("plugins", null, "allowRemoteAdmin", false);
+ mandatoryPlugins = mpc;
this.gerritRuntime = gerritRuntime;
long checkFrequency =
@@ -226,6 +230,11 @@ public class PluginLoader implements LifecycleListener {
continue;
}
+ if (mandatoryPlugins.contains(name)) {
+ logger.atWarning().log("Mandatory plugin %s cannot be disabled", name);
+ continue;
+ }
+
logger.atInfo().log("Disabling plugin %s", active.getName());
Path off =
active.getSrcFile().resolveSibling(active.getSrcFile().getFileName() + ".disabled");
@@ -381,52 +390,59 @@ public class PluginLoader implements LifecycleListener {
}
public synchronized void rescan() {
+ Set<String> loadedPlugins = new HashSet<>();
SetMultimap<String, Path> pluginsFiles = prunePlugins(pluginsDir);
- if (pluginsFiles.isEmpty()) {
- return;
- }
- syncDisabledPlugins(pluginsFiles);
-
- Map<String, Path> activePlugins = filterDisabled(pluginsFiles);
- for (Map.Entry<String, Path> entry : jarsFirstSortedPluginsSet(activePlugins)) {
- String name = entry.getKey();
- Path path = entry.getValue();
- String fileName = path.getFileName().toString();
- if (!isUiPlugin(fileName) && !serverPluginFactory.handles(path)) {
- logger.atWarning().log(
- "No Plugin provider was found that handles this file format: %s", fileName);
- continue;
- }
+ if (!pluginsFiles.isEmpty()) {
+ syncDisabledPlugins(pluginsFiles);
+
+ Map<String, Path> activePlugins = filterDisabled(pluginsFiles);
+ for (Map.Entry<String, Path> entry : jarsFirstSortedPluginsSet(activePlugins)) {
+ String name = entry.getKey();
+ Path path = entry.getValue();
+ String fileName = path.getFileName().toString();
+ if (!isUiPlugin(fileName) && !serverPluginFactory.handles(path)) {
+ logger.atWarning().log(
+ "No Plugin provider was found that handles this file format: %s", fileName);
+ continue;
+ }
- FileSnapshot brokenTime = broken.get(name);
- if (brokenTime != null && !brokenTime.isModified(path.toFile())) {
- continue;
- }
+ FileSnapshot brokenTime = broken.get(name);
+ if (brokenTime != null && !brokenTime.isModified(path.toFile())) {
+ continue;
+ }
- Plugin active = running.get(name);
- if (active != null && !active.isModified(path)) {
- continue;
- }
+ Plugin active = running.get(name);
+ if (active != null && !active.isModified(path)) {
+ loadedPlugins.add(name);
+ continue;
+ }
- if (active != null) {
- logger.atInfo().log("Reloading plugin %s", active.getName());
- }
+ if (active != null) {
+ logger.atInfo().log("Reloading plugin %s", active.getName());
+ }
- try {
- Plugin loadedPlugin = runPlugin(name, path, active);
- if (!loadedPlugin.isDisabled()) {
- logger.atInfo().log(
- "%s plugin %s, version %s",
- active == null ? "Loaded" : "Reloaded",
- loadedPlugin.getName(),
- loadedPlugin.getVersion());
+ try {
+ Plugin loadedPlugin = runPlugin(name, path, active);
+ if (!loadedPlugin.isDisabled()) {
+ loadedPlugins.add(name);
+ logger.atInfo().log(
+ "%s plugin %s, version %s",
+ active == null ? "Loaded" : "Reloaded",
+ loadedPlugin.getName(),
+ loadedPlugin.getVersion());
+ }
+ } catch (PluginInstallException e) {
+ logger.atWarning().withCause(e.getCause()).log("Cannot load plugin %s", name);
}
- } catch (PluginInstallException e) {
- logger.atWarning().withCause(e.getCause()).log("Cannot load plugin %s", name);
}
}
+ Set<String> missingMandatory = Sets.difference(mandatoryPlugins.asSet(), loadedPlugins);
+ if (!missingMandatory.isEmpty()) {
+ throw new MissingMandatoryPluginsException(missingMandatory);
+ }
+
cleanInBackground();
}
@@ -471,6 +487,12 @@ public class PluginLoader implements LifecycleListener {
throws PluginInstallException {
FileSnapshot snapshot = FileSnapshot.save(plugin.toFile());
try {
+ boolean restartRequired = oldPlugin != null && !oldPlugin.canReload();
+ if (restartRequired && mandatoryPlugins.contains(name)) {
+ logger.atWarning().log("Restarting mandatory plugin %s not allowed", name);
+ return oldPlugin;
+ }
+
Plugin newPlugin = loadPlugin(name, plugin, snapshot);
if (newPlugin.getCleanupHandle() != null) {
cleanupHandles.put(newPlugin, newPlugin.getCleanupHandle());
diff --git a/java/com/google/gerrit/server/plugins/PluginModule.java b/java/com/google/gerrit/server/plugins/PluginModule.java
index 6bc37bd792..71186e51e7 100644
--- a/java/com/google/gerrit/server/plugins/PluginModule.java
+++ b/java/com/google/gerrit/server/plugins/PluginModule.java
@@ -34,6 +34,7 @@ public class PluginModule extends LifecycleModule {
bind(PluginLoader.class);
bind(CopyConfigModule.class);
listener().to(PluginLoader.class);
+ bind(MandatoryPluginsCollection.class);
DynamicSet.setOf(binder(), ServerPluginProvider.class);
DynamicSet.bind(binder(), ServerPluginProvider.class).to(JarPluginProvider.class);
diff --git a/java/com/google/gerrit/server/plugins/ReloadPlugin.java b/java/com/google/gerrit/server/plugins/ReloadPlugin.java
index 1134f50800..490c4aab25 100644
--- a/java/com/google/gerrit/server/plugins/ReloadPlugin.java
+++ b/java/com/google/gerrit/server/plugins/ReloadPlugin.java
@@ -20,6 +20,7 @@ import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -38,7 +39,8 @@ public class ReloadPlugin implements RestModifyView<PluginResource, Input> {
}
@Override
- public PluginInfo apply(PluginResource resource, Input input) throws ResourceConflictException {
+ public Response<PluginInfo> apply(PluginResource resource, Input input)
+ throws ResourceConflictException {
String name = resource.getName();
try {
loader.reload(ImmutableList.of(name));
@@ -52,6 +54,6 @@ public class ReloadPlugin implements RestModifyView<PluginResource, Input> {
pw.flush();
throw new ResourceConflictException(buf.toString());
}
- return ListPlugins.toPluginInfo(loader.get(name));
+ return Response.ok(ListPlugins.toPluginInfo(loader.get(name)));
}
}
diff --git a/java/com/google/gerrit/server/project/AccessControlModule.java b/java/com/google/gerrit/server/project/AccessControlModule.java
index 6d772676f8..89ab8ee27d 100644
--- a/java/com/google/gerrit/server/project/AccessControlModule.java
+++ b/java/com/google/gerrit/server/project/AccessControlModule.java
@@ -18,8 +18,8 @@ import static com.google.inject.Scopes.SINGLETON;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.config.AdministrateServerGroups;
import com.google.gerrit.server.config.AdministrateServerGroupsProvider;
import com.google.gerrit.server.config.GitReceivePackGroups;
diff --git a/java/com/google/gerrit/server/project/BooleanProjectConfigTransformations.java b/java/com/google/gerrit/server/project/BooleanProjectConfigTransformations.java
index dc9cf9cafd..ae9828aaa8 100644
--- a/java/com/google/gerrit/server/project/BooleanProjectConfigTransformations.java
+++ b/java/com/google/gerrit/server/project/BooleanProjectConfigTransformations.java
@@ -16,11 +16,11 @@ package com.google.gerrit.server.project;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
+import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInfo.InheritedBooleanInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
import java.util.Arrays;
import java.util.HashSet;
diff --git a/java/com/google/gerrit/server/project/BranchResource.java b/java/com/google/gerrit/server/project/BranchResource.java
index 622b1ddad4..a8936ac220 100644
--- a/java/com/google/gerrit/server/project/BranchResource.java
+++ b/java/com/google/gerrit/server/project/BranchResource.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.TypeLiteral;
import org.eclipse.jgit.lib.Ref;
@@ -33,8 +33,8 @@ public class BranchResource extends RefResource {
this.revision = ref.getObjectId() != null ? ref.getObjectId().name() : null;
}
- public Branch.NameKey getBranchKey() {
- return new Branch.NameKey(getNameKey(), refName);
+ public BranchNameKey getBranchKey() {
+ return BranchNameKey.create(getNameKey(), refName);
}
@Override
diff --git a/java/com/google/gerrit/server/project/ChildProjects.java b/java/com/google/gerrit/server/project/ChildProjects.java
index ce9992eeba..2069a48185 100644
--- a/java/com/google/gerrit/server/project/ChildProjects.java
+++ b/java/com/google/gerrit/server/project/ChildProjects.java
@@ -18,8 +18,8 @@ import static java.util.stream.Collectors.toList;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.ProjectInfo;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java b/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
index b33fcb5c9c..a2de4ef704 100644
--- a/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
+++ b/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
@@ -20,15 +20,15 @@ import static java.util.Objects.requireNonNull;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.AccountGroup.UUID;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.metrics.Counter0;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.UrlFormatter;
@@ -117,7 +117,7 @@ public class ContributorAgreementsChecker {
if ((rule.getAction() == Action.ALLOW)
&& (rule.getGroup() != null)
&& (rule.getGroup().getUUID() != null)) {
- groupIds.add(new AccountGroup.UUID(rule.getGroup().getUUID().get()));
+ groupIds.add(AccountGroup.uuid(rule.getGroup().getUUID().get()));
}
}
}
diff --git a/java/com/google/gerrit/server/project/CreateProjectArgs.java b/java/com/google/gerrit/server/project/CreateProjectArgs.java
index df31c193d0..c1b7b86987 100644
--- a/java/com/google/gerrit/server/project/CreateProjectArgs.java
+++ b/java/com/google/gerrit/server/project/CreateProjectArgs.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import java.util.List;
public class CreateProjectArgs {
@@ -61,7 +61,7 @@ public class CreateProjectArgs {
}
public void setProjectName(String n) {
- projectName = n != null ? new Project.NameKey(n) : null;
+ projectName = n != null ? Project.nameKey(n) : null;
}
public void setProjectName(Project.NameKey n) {
diff --git a/java/com/google/gerrit/server/project/CreateRefControl.java b/java/com/google/gerrit/server/project/CreateRefControl.java
index 7a7598bf89..abbd9f6e64 100644
--- a/java/com/google/gerrit/server/project/CreateRefControl.java
+++ b/java/com/google/gerrit/server/project/CreateRefControl.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.project;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -65,15 +65,12 @@ public class CreateRefControl {
* @throws ResourceConflictException if the project state does not permit the operation
*/
public void checkCreateRef(
- Provider<? extends CurrentUser> user,
- Repository repo,
- Branch.NameKey branch,
- RevObject object)
+ Provider<? extends CurrentUser> user, Repository repo, BranchNameKey branch, RevObject object)
throws AuthException, PermissionBackendException, NoSuchProjectException, IOException,
ResourceConflictException {
- ProjectState ps = projectCache.checkedGet(branch.getParentKey());
+ ProjectState ps = projectCache.checkedGet(branch.project());
if (ps == null) {
- throw new NoSuchProjectException(branch.getParentKey());
+ throw new NoSuchProjectException(branch.project());
}
ps.checkStatePermitsWrite();
@@ -86,8 +83,7 @@ public class CreateRefControl {
try (RevWalk rw = new RevWalk(repo)) {
rw.parseBody(tag);
} catch (IOException e) {
- logger.atSevere().withCause(e).log(
- "RevWalk(%s) parsing %s:", branch.getParentKey(), tag.name());
+ logger.atSevere().withCause(e).log("RevWalk(%s) parsing %s:", branch.project(), tag.name());
throw e;
}
diff --git a/java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java b/java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java
index 3fb5d2af7c..000fb094d2 100644
--- a/java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java
+++ b/java/com/google/gerrit/server/project/DefaultProjectNameLockManager.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.project;
import com.google.common.util.concurrent.Striped;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import java.util.concurrent.locks.Lock;
diff --git a/java/com/google/gerrit/server/project/GroupList.java b/java/com/google/gerrit/server/project/GroupList.java
index fdb87406fb..fe59012889 100644
--- a/java/com/google/gerrit/server/project/GroupList.java
+++ b/java/com/google/gerrit/server/project/GroupList.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.project;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.git.meta.TabFile;
import java.io.IOException;
@@ -48,7 +48,7 @@ public class GroupList extends TabFile {
logger.atWarning().log("null field in group list for %s:\n%s", project, text);
continue;
}
- AccountGroup.UUID uuid = new AccountGroup.UUID(row.left);
+ AccountGroup.UUID uuid = AccountGroup.uuid(row.left);
String name = row.right;
GroupReference ref = new GroupReference(uuid, name);
diff --git a/java/com/google/gerrit/server/project/NoSuchChangeException.java b/java/com/google/gerrit/server/project/NoSuchChangeException.java
index 6f65659f04..93cf03a5aa 100644
--- a/java/com/google/gerrit/server/project/NoSuchChangeException.java
+++ b/java/com/google/gerrit/server/project/NoSuchChangeException.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Change;
/** Indicates the change does not exist. */
public class NoSuchChangeException extends StorageException {
diff --git a/java/com/google/gerrit/server/project/NoSuchProjectException.java b/java/com/google/gerrit/server/project/NoSuchProjectException.java
index 23d8d80b95..d4a8a5ca41 100644
--- a/java/com/google/gerrit/server/project/NoSuchProjectException.java
+++ b/java/com/google/gerrit/server/project/NoSuchProjectException.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.project;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
/** Indicates the project does not exist. */
public class NoSuchProjectException extends Exception {
diff --git a/java/com/google/gerrit/server/project/ProjectCache.java b/java/com/google/gerrit/server/project/ProjectCache.java
index 17250fa2de..0baaa11ecc 100644
--- a/java/com/google/gerrit/server/project/ProjectCache.java
+++ b/java/com/google/gerrit/server/project/ProjectCache.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.project;
import com.google.common.collect.ImmutableSortedSet;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import java.io.IOException;
import java.util.Set;
@@ -35,7 +35,7 @@ public interface ProjectCache {
* @param projectName name of the project.
* @return the cached data; null if no such project exists, projectName is null or an error
* occurred.
- * @see #checkedGet(com.google.gerrit.reviewdb.client.Project.NameKey)
+ * @see #checkedGet(com.google.gerrit.entities.Project.NameKey)
*/
ProjectState get(@Nullable Project.NameKey projectName);
@@ -57,7 +57,7 @@ public interface ProjectCache {
* errors.
* @return the cached data or null when strict = false
*/
- public ProjectState checkedGet(Project.NameKey projectName, boolean strict) throws Exception;
+ ProjectState checkedGet(Project.NameKey projectName, boolean strict) throws Exception;
/**
* Invalidate the cached information about the given project, and triggers reindexing for it
diff --git a/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index bd26a8fa56..e88bfc63ec 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -24,18 +24,19 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer0;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.inject.Inject;
@@ -262,8 +263,8 @@ public class ProjectCacheImpl implements ProjectCache {
@Override
public ImmutableSortedSet<Project.NameKey> byName(String pfx) {
- Project.NameKey start = new Project.NameKey(pfx);
- Project.NameKey end = new Project.NameKey(pfx + Character.MAX_VALUE);
+ Project.NameKey start = Project.nameKey(pfx);
+ Project.NameKey end = Project.nameKey(pfx + Character.MAX_VALUE);
try {
// Right endpoint is exclusive, but U+FFFF is a non-character so no project ends with it.
return list.get(ListKey.ALL).subSet(start, end);
@@ -293,9 +294,11 @@ public class ProjectCacheImpl implements ProjectCache {
@Override
public ProjectState load(String projectName) throws Exception {
- try (TraceTimer timer = TraceContext.newTimer("Loading project %s", projectName)) {
+ try (TraceTimer timer =
+ TraceContext.newTimer(
+ "Loading project", Metadata.builder().projectName(projectName).build())) {
long now = clock.read();
- Project.NameKey key = new Project.NameKey(projectName);
+ Project.NameKey key = Project.nameKey(projectName);
try (Repository git = mgr.openRepository(key)) {
ProjectConfig cfg = projectConfigFactory.create(key);
cfg.load(key, git);
diff --git a/java/com/google/gerrit/server/project/ProjectCacheWarmer.java b/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
index 10cf2de23a..d1f31a3729 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.project;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 09279fabb5..e6b8d44fa1 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.project;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.common.data.Permission.isPermission;
-import static com.google.gerrit.reviewdb.client.Project.DEFAULT_SUBMIT_TYPE;
+import static com.google.gerrit.entities.Project.DEFAULT_SUBMIT_TYPE;
import static com.google.gerrit.server.permissions.PluginPermissionsUtil.isValidPluginPermission;
import static java.util.stream.Collectors.toList;
@@ -26,6 +26,7 @@ import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.primitives.Shorts;
import com.google.gerrit.common.Nullable;
@@ -42,15 +43,15 @@ import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.InvalidNameException;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.config.AllProjectsName;
@@ -100,6 +101,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
public static final String KEY_COPY_MIN_SCORE = "copyMinScore";
public static final String KEY_ALLOW_POST_SUBMIT = "allowPostSubmit";
public static final String KEY_IGNORE_SELF_APPROVAL = "ignoreSelfApproval";
+ public static final String KEY_COPY_ANY_SCORE = "copyAnyScore";
public static final String KEY_COPY_MAX_SCORE = "copyMaxScore";
public static final String KEY_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE =
"copyAllScoresOnMergeFirstParentUpdate";
@@ -337,6 +339,10 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
return as;
}
+ public ImmutableSet<String> getAccessSectionNames() {
+ return ImmutableSet.copyOf(accessSections.keySet());
+ }
+
public Collection<AccessSection> getAccessSections() {
return sort(accessSections.values());
}
@@ -349,7 +355,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
return subscribeSections;
}
- public Collection<SubscribeSection> getSubscribeSections(Branch.NameKey branch) {
+ public Collection<SubscribeSection> getSubscribeSections(BranchNameKey branch) {
Collection<SubscribeSection> ret = new ArrayList<>();
for (SubscribeSection s : subscribeSections.values()) {
if (s.appliesTo(branch)) {
@@ -717,7 +723,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
if (groupName != null) {
GroupReference ref = groupsByName.get(groupName);
if (ref == null) {
- ref = new GroupReference(null, groupName);
+ ref = new GroupReference(groupName);
groupsByName.put(ref.getName(), ref);
}
if (ref.getUUID() != null) {
@@ -972,6 +978,8 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
rc.getBoolean(LABEL, name, KEY_ALLOW_POST_SUBMIT, LabelType.DEF_ALLOW_POST_SUBMIT));
label.setIgnoreSelfApproval(
rc.getBoolean(LABEL, name, KEY_IGNORE_SELF_APPROVAL, LabelType.DEF_IGNORE_SELF_APPROVAL));
+ label.setCopyAnyScore(
+ rc.getBoolean(LABEL, name, KEY_COPY_ANY_SCORE, LabelType.DEF_COPY_ANY_SCORE));
label.setCopyMinScore(
rc.getBoolean(LABEL, name, KEY_COPY_MIN_SCORE, LabelType.DEF_COPY_MIN_SCORE));
label.setCopyMaxScore(
@@ -1051,7 +1059,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
subscribeSections = new HashMap<>();
try {
for (String projectName : subsections) {
- Project.NameKey p = new Project.NameKey(projectName);
+ Project.NameKey p = Project.nameKey(projectName);
SubscribeSection ss = new SubscribeSection(p);
for (String s :
rc.getStringList(SUBSCRIBE_SECTION, projectName, SUBSCRIBE_MULTI_MATCH_REFS)) {
@@ -1203,6 +1211,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
private void saveAccountsSection(Config rc, Set<AccountGroup.UUID> keepGroups) {
+ unsetSection(rc, ACCOUNTS);
if (accountsSection != null) {
rc.setStringList(
ACCOUNTS,
@@ -1213,6 +1222,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
private void saveCommentLinkSections(Config rc) {
+ unsetSection(rc, COMMENTLINK);
if (commentLinkSections != null) {
for (CommentLinkInfoImpl cm : commentLinkSections.values()) {
rc.setString(COMMENTLINK, cm.name, KEY_MATCH, cm.match);
@@ -1230,6 +1240,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
private void saveContributorAgreements(Config rc, Set<AccountGroup.UUID> keepGroups) {
+ unsetSection(rc, CONTRIBUTOR_AGREEMENT);
for (ContributorAgreement ca : sort(contributorAgreements.values())) {
set(rc, CONTRIBUTOR_AGREEMENT, ca.getName(), KEY_DESCRIPTION, ca.getDescription());
set(rc, CONTRIBUTOR_AGREEMENT, ca.getName(), KEY_AGREEMENT_URL, ca.getAgreementUrl());
@@ -1263,6 +1274,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
private void saveNotifySections(Config rc, Set<AccountGroup.UUID> keepGroups) {
+ unsetSection(rc, NOTIFY);
for (NotifyConfig nc : sort(notifySections.values())) {
nc.getGroups().stream()
.map(GroupReference::getUUID)
@@ -1317,6 +1329,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
private void saveAccessSections(Config rc, Set<AccountGroup.UUID> keepGroups) {
+ unsetSection(rc, CAPABILITY);
AccessSection capability = accessSections.get(AccessSection.GLOBAL_CAPABILITIES);
if (capability != null) {
Set<String> have = new HashSet<>();
@@ -1399,9 +1412,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
List<String> existing = new ArrayList<>(rc.getSubsections(LABEL));
if (!new ArrayList<>(labelSections.keySet()).equals(existing)) {
// Order of sections changed, remove and rewrite them all.
- for (String name : existing) {
- rc.unsetSection(LABEL, name);
- }
+ unsetSection(rc, LABEL);
}
Set<String> toUnset = new HashSet<>(existing);
@@ -1430,6 +1441,13 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
rc,
LABEL,
name,
+ KEY_COPY_ANY_SCORE,
+ label.isCopyAnyScore(),
+ LabelType.DEF_COPY_ANY_SCORE);
+ setBooleanConfigKey(
+ rc,
+ LABEL,
+ name,
KEY_COPY_MIN_SCORE,
label.isCopyMinScore(),
LabelType.DEF_COPY_MIN_SCORE);
@@ -1497,11 +1515,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
private void savePluginSections(Config rc, Set<AccountGroup.UUID> keepGroups) {
- List<String> existing = new ArrayList<>(rc.getSubsections(PLUGIN));
- for (String name : existing) {
- rc.unsetSection(PLUGIN, name);
- }
-
+ unsetSection(rc, PLUGIN);
for (Map.Entry<String, Config> e : pluginConfigs.entrySet()) {
String plugin = e.getKey();
Config pluginConfig = e.getValue();
@@ -1542,6 +1556,13 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
}
+ private void unsetSection(Config rc, String sectionName) {
+ for (String subSectionName : rc.getSubsections(sectionName)) {
+ rc.unsetSection(sectionName, subSectionName);
+ }
+ rc.unsetSection(sectionName, null);
+ }
+
private <E extends Enum<?>> E getEnum(
Config rc, String section, String subsection, String name, E defaultValue) {
try {
diff --git a/java/com/google/gerrit/server/project/ProjectCreator.java b/java/com/google/gerrit/server/project/ProjectCreator.java
index 35ecd7cff8..c9eb73e266 100644
--- a/java/com/google/gerrit/server/project/ProjectCreator.java
+++ b/java/com/google/gerrit/server/project/ProjectCreator.java
@@ -21,13 +21,13 @@ import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.events.NewProjectCreatedListener;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupBackend;
diff --git a/java/com/google/gerrit/server/project/ProjectHierarchyIterator.java b/java/com/google/gerrit/server/project/ProjectHierarchyIterator.java
index 27bde7205a..694c5417f2 100644
--- a/java/com/google/gerrit/server/project/ProjectHierarchyIterator.java
+++ b/java/com/google/gerrit/server/project/ProjectHierarchyIterator.java
@@ -18,7 +18,7 @@ import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.config.AllProjectsName;
import java.util.Iterator;
import java.util.List;
diff --git a/java/com/google/gerrit/server/project/ProjectJson.java b/java/com/google/gerrit/server/project/ProjectJson.java
index 449f6073f2..fab6c11fa6 100644
--- a/java/com/google/gerrit/server/project/ProjectJson.java
+++ b/java/com/google/gerrit/server/project/ProjectJson.java
@@ -17,20 +17,20 @@ package com.google.gerrit.server.project;
import static java.util.stream.Collectors.toMap;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.LabelTypeInfo;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.HashMap;
-import java.util.List;
@Singleton
public class ProjectJson {
@@ -77,7 +77,7 @@ public class ProjectJson {
info.description = Strings.emptyToNull(p.getDescription());
info.state = p.getState();
info.id = Url.encode(info.name);
- List<WebLinkInfo> links = webLinks.getProjectLinks(p.getName());
+ ImmutableList<WebLinkInfo> links = webLinks.getProjectLinks(p.getName());
info.webLinks = links.isEmpty() ? null : links;
return info;
}
diff --git a/java/com/google/gerrit/server/project/ProjectLevelConfig.java b/java/com/google/gerrit/server/project/ProjectLevelConfig.java
index 961d1fcaf6..4e0261ccaf 100644
--- a/java/com/google/gerrit/server/project/ProjectLevelConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectLevelConfig.java
@@ -18,7 +18,7 @@ import static java.util.stream.Collectors.toList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import java.io.IOException;
import java.util.Arrays;
diff --git a/java/com/google/gerrit/server/project/ProjectNameLockManager.java b/java/com/google/gerrit/server/project/ProjectNameLockManager.java
index 4666c32fa0..72036a7456 100644
--- a/java/com/google/gerrit/server/project/ProjectNameLockManager.java
+++ b/java/com/google/gerrit/server/project/ProjectNameLockManager.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.project;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import java.util.concurrent.locks.Lock;
public interface ProjectNameLockManager {
diff --git a/java/com/google/gerrit/server/project/ProjectResource.java b/java/com/google/gerrit/server/project/ProjectResource.java
index 22b7bd989e..8802758949 100644
--- a/java/com/google/gerrit/server/project/ProjectResource.java
+++ b/java/com/google/gerrit/server/project/ProjectResource.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.TypeLiteral;
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 0e3a94027d..4b879a1865 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -28,6 +28,11 @@ import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -37,17 +42,13 @@ import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer1;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.CapabilityCollection;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.BranchOrderSection;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.TransferConfig;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -69,7 +70,7 @@ import org.eclipse.jgit.lib.Repository;
/**
* Cached information on a project. Must not contain any data derived from parents other than it's
- * immediate parent's {@link com.google.gerrit.reviewdb.client.Project.NameKey}.
+ * immediate parent's {@link com.google.gerrit.entities.Project.NameKey}.
*/
public class ProjectState {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -135,7 +136,7 @@ public class ProjectState {
new Description("Latency for access computations in ProjectState")
.setCumulative()
.setUnit(Units.NANOSECONDS),
- Field.ofString("method"));
+ Field.ofString("method", Metadata.Builder::methodName).build());
if (isAllProjects && !Permission.canBeOnAllProjects(AccessSection.ALL, Permission.OWNER)) {
localOwners = Collections.emptySet();
@@ -354,14 +355,15 @@ public class ProjectState {
* cached. Callers should try to cache this result per-request as much as possible.
*/
public List<SectionMatcher> getAllSections() {
- try (Timer1.Context ignored = computationLatency.start("getAllSections")) {
+ try (Timer1.Context<String> ignored = computationLatency.start("getAllSections")) {
if (isAllProjects) {
return getLocalAccessSections();
}
List<SectionMatcher> all = new ArrayList<>();
Iterable<ProjectState> tree = tree();
- try (Timer1.Context ignored2 = computationLatency.start("getAllSections-parsing-only")) {
+ try (Timer1.Context<String> ignored2 =
+ computationLatency.start("getAllSections-parsing-only")) {
for (ProjectState s : tree) {
all.addAll(s.getLocalAccessSections());
}
@@ -476,7 +478,7 @@ public class ProjectState {
}
/** All available label types for this branch. */
- public LabelTypes getLabelTypes(Branch.NameKey destination) {
+ public LabelTypes getLabelTypes(BranchNameKey destination) {
List<LabelType> all = getLabelTypes().getLabelTypes();
List<LabelType> r = Lists.newArrayListWithCapacity(all.size());
@@ -537,7 +539,7 @@ public class ProjectState {
return null;
}
- public Collection<SubscribeSection> getSubscribeSections(Branch.NameKey branch) {
+ public Collection<SubscribeSection> getSubscribeSections(BranchNameKey branch) {
Collection<SubscribeSection> ret = new ArrayList<>();
for (ProjectState s : tree()) {
ret.addAll(s.getConfig().getSubscribeSections(branch));
@@ -584,7 +586,7 @@ public class ProjectState {
return project;
}
- private boolean match(Branch.NameKey destination, String refPattern) {
- return RefPatternMatcher.getMatcher(refPattern).match(destination.get(), null);
+ private boolean match(BranchNameKey destination, String refPattern) {
+ return RefPatternMatcher.getMatcher(refPattern).match(destination.branch(), null);
}
}
diff --git a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
index 17fd69c4f8..a3b4126c55 100644
--- a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
@@ -24,6 +24,10 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.FixInput;
import com.google.gerrit.extensions.api.projects.CheckProjectInput;
@@ -38,9 +42,6 @@ import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.change.ChangeField;
@@ -225,7 +226,7 @@ public class ProjectsConsistencyChecker {
// important thing for callers is that auto-closable changes are closed. Which of the
// commits is used to auto-close a change if there are several candidates is of minor
// importance and hence can be non-deterministic.
- Change.Key changeKey = new Change.Key(changeId);
+ Change.Key changeKey = Change.key(changeId);
if (!changeIdToMergedSha1.containsKey(changeKey)) {
changeIdToMergedSha1.put(changeKey, commitId);
}
@@ -295,7 +296,7 @@ public class ProjectsConsistencyChecker {
// Auto-close by commit
for (ObjectId patchSetSha1 :
autoCloseableChange.patchSets().stream()
- .map(ps -> ObjectId.fromString(ps.getRevision().get()))
+ .map(PatchSet::commitId)
.collect(toSet())) {
if (mergedSha1s.contains(patchSetSha1)) {
autoCloseableChangesByBranch.add(
diff --git a/java/com/google/gerrit/server/project/Reachable.java b/java/com/google/gerrit/server/project/Reachable.java
index 93cbf4b1f7..b12f43b286 100644
--- a/java/com/google/gerrit/server/project/Reachable.java
+++ b/java/com/google/gerrit/server/project/Reachable.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.project;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.IncludedInResolver;
import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/project/RefFilter.java b/java/com/google/gerrit/server/project/RefFilter.java
index 76bafc0113..cdabcbe064 100644
--- a/java/com/google/gerrit/server/project/RefFilter.java
+++ b/java/com/google/gerrit/server/project/RefFilter.java
@@ -14,15 +14,17 @@
package com.google.gerrit.server.project;
-import com.google.common.base.Predicate;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
import com.google.common.base.Strings;
-import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.api.projects.RefInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
import java.util.List;
import java.util.Locale;
+import java.util.stream.Stream;
public class RefFilter<T extends RefInfo> {
private final String prefix;
@@ -55,15 +57,17 @@ public class RefFilter<T extends RefInfo> {
return this;
}
- public List<T> filter(List<T> refs) throws BadRequestException {
+ public ImmutableList<T> filter(List<T> refs) throws BadRequestException {
if (!Strings.isNullOrEmpty(matchSubstring) && !Strings.isNullOrEmpty(matchRegex)) {
throw new BadRequestException("specify exactly one of m/r");
}
- FluentIterable<T> results = FluentIterable.from(refs);
+ Stream<T> results = refs.stream();
if (!Strings.isNullOrEmpty(matchSubstring)) {
- results = results.filter(new SubstringPredicate(matchSubstring));
+ String lowercaseSubstring = matchSubstring.toLowerCase(Locale.US);
+ results = results.filter(refInfo -> matchesSubstring(prefix, lowercaseSubstring, refInfo));
} else if (!Strings.isNullOrEmpty(matchRegex)) {
- results = results.filter(new RegexPredicate(matchRegex));
+ RunAutomaton a = parseRegex(matchRegex);
+ results = results.filter(refInfo -> matchesRegex(prefix, a, refInfo));
}
if (start > 0) {
results = results.skip(start);
@@ -71,51 +75,39 @@ public class RefFilter<T extends RefInfo> {
if (limit > 0) {
results = results.limit(limit);
}
- return results.toList();
+ return results.collect(toImmutableList());
}
- private class SubstringPredicate implements Predicate<T> {
- private final String substring;
-
- private SubstringPredicate(String substring) {
- this.substring = substring.toLowerCase(Locale.US);
- }
-
- @Override
- public boolean apply(T in) {
- String ref = in.ref;
- if (ref.startsWith(prefix)) {
- ref = ref.substring(prefix.length());
- }
- ref = ref.toLowerCase(Locale.US);
- return ref.contains(substring);
+ private static <T extends RefInfo> boolean matchesSubstring(
+ String prefix, String lowercaseSubstring, T refInfo) {
+ String ref = refInfo.ref;
+ if (ref.startsWith(prefix)) {
+ ref = ref.substring(prefix.length());
}
+ ref = ref.toLowerCase(Locale.US);
+ return ref.contains(lowercaseSubstring);
}
- private class RegexPredicate implements Predicate<T> {
- private final RunAutomaton a;
-
- private RegexPredicate(String regex) throws BadRequestException {
- if (regex.startsWith("^")) {
- regex = regex.substring(1);
- if (regex.endsWith("$") && !regex.endsWith("\\$")) {
- regex = regex.substring(0, regex.length() - 1);
- }
- }
- try {
- a = new RunAutomaton(new RegExp(regex).toAutomaton());
- } catch (IllegalArgumentException e) {
- throw new BadRequestException(e.getMessage());
+ private static RunAutomaton parseRegex(String regex) throws BadRequestException {
+ if (regex.startsWith("^")) {
+ regex = regex.substring(1);
+ if (regex.endsWith("$") && !regex.endsWith("\\$")) {
+ regex = regex.substring(0, regex.length() - 1);
}
}
+ try {
+ return new RunAutomaton(new RegExp(regex).toAutomaton());
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException(e.getMessage());
+ }
+ }
- @Override
- public boolean apply(T in) {
- String ref = in.ref;
- if (ref.startsWith(prefix)) {
- ref = ref.substring(prefix.length());
- }
- return a.run(ref);
+ private static <T extends RefInfo> boolean matchesRegex(
+ String prefix, RunAutomaton a, T refInfo) {
+ String ref = refInfo.ref;
+ if (ref.startsWith(prefix)) {
+ ref = ref.substring(prefix.length());
}
+ return a.run(ref);
}
}
diff --git a/java/com/google/gerrit/server/project/RefPatternMatcher.java b/java/com/google/gerrit/server/project/RefPatternMatcher.java
index 4032524296..b9076b3db6 100644
--- a/java/com/google/gerrit/server/project/RefPatternMatcher.java
+++ b/java/com/google/gerrit/server/project/RefPatternMatcher.java
@@ -22,8 +22,8 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.gerrit.common.data.ParameterizedString;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.CurrentUser;
import dk.brics.automaton.Automaton;
import java.util.HashMap;
diff --git a/java/com/google/gerrit/server/project/RefUtil.java b/java/com/google/gerrit/server/project/RefUtil.java
index a9c964dd51..dc8cdc7864 100644
--- a/java/com/google/gerrit/server/project/RefUtil.java
+++ b/java/com/google/gerrit/server/project/RefUtil.java
@@ -20,9 +20,9 @@ import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import java.io.IOException;
import java.util.Collections;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
diff --git a/java/com/google/gerrit/server/project/RefValidationHelper.java b/java/com/google/gerrit/server/project/RefValidationHelper.java
index 0a5980ca97..9b297f91ab 100644
--- a/java/com/google/gerrit/server/project/RefValidationHelper.java
+++ b/java/com/google/gerrit/server/project/RefValidationHelper.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.validators.RefOperationValidators;
import com.google.gerrit.server.validators.ValidationException;
@@ -43,7 +43,7 @@ public class RefValidationHelper {
throws ResourceConflictException {
RefOperationValidators refValidators =
refValidatorsFactory.create(
- new Project(new Project.NameKey(projectName)),
+ new Project(Project.nameKey(projectName)),
user,
RefOperationValidators.getCommand(update, operationType));
try {
diff --git a/java/com/google/gerrit/server/project/RemoveReviewerControl.java b/java/com/google/gerrit/server/project/RemoveReviewerControl.java
index eeb2a65419..6bf3beb968 100644
--- a/java/com/google/gerrit/server/project/RemoveReviewerControl.java
+++ b/java/com/google/gerrit/server/project/RemoveReviewerControl.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -47,7 +47,7 @@ public class RemoveReviewerControl {
public void checkRemoveReviewer(
ChangeNotes notes, CurrentUser currentUser, PatchSetApproval approval)
throws PermissionBackendException, AuthException {
- checkRemoveReviewer(notes, currentUser, approval.getAccountId(), approval.getValue());
+ checkRemoveReviewer(notes, currentUser, approval.accountId(), approval.value());
}
/**
@@ -108,7 +108,7 @@ public class RemoveReviewerControl {
// owner and site admin can remove anyone
PermissionBackend.WithUser withUser = permissionBackend.user(currentUser);
PermissionBackend.ForProject forProject = withUser.project(change.getProject());
- if (check(forProject.ref(change.getDest().get()), RefPermission.WRITE_CONFIG)
+ if (check(forProject.ref(change.getDest().branch()), RefPermission.WRITE_CONFIG)
|| check(withUser, GlobalPermission.ADMINISTRATE_SERVER)) {
return true;
}
diff --git a/java/com/google/gerrit/server/project/SectionMatcher.java b/java/com/google/gerrit/server/project/SectionMatcher.java
index a8ebd98d62..6de8eec3f6 100644
--- a/java/com/google/gerrit/server/project/SectionMatcher.java
+++ b/java/com/google/gerrit/server/project/SectionMatcher.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.project;
import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
/**
diff --git a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 1b1869ca2e..cc7591c9dd 100644
--- a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -18,8 +18,12 @@ import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Description.Units;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer0;
import com.google.gerrit.server.index.OnlineReindexMode;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.query.change.ChangeData;
@@ -27,9 +31,9 @@ import com.google.gerrit.server.rules.PrologRule;
import com.google.gerrit.server.rules.SubmitRule;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -44,6 +48,8 @@ public class SubmitRuleEvaluator {
private final ProjectCache projectCache;
private final PrologRule prologRule;
private final PluginSetContext<SubmitRule> submitRules;
+ private final Timer0 submitRuleEvaluationLatency;
+ private final Timer0 submitTypeEvaluationLatency;
private final SubmitRuleOptions opts;
public interface Factory {
@@ -56,23 +62,36 @@ public class SubmitRuleEvaluator {
ProjectCache projectCache,
PrologRule prologRule,
PluginSetContext<SubmitRule> submitRules,
+ MetricMaker metricMaker,
@Assisted SubmitRuleOptions options) {
this.projectCache = projectCache;
this.prologRule = prologRule;
this.submitRules = submitRules;
+ this.submitRuleEvaluationLatency =
+ metricMaker.newTimer(
+ "change/submit_rule_evaluation",
+ new Description("Latency for evaluating submit rules on a change.")
+ .setCumulative()
+ .setUnit(Units.MILLISECONDS));
+ this.submitTypeEvaluationLatency =
+ metricMaker.newTimer(
+ "change/submit_type_evaluation",
+ new Description("Latency for evaluating the submit type on a change.")
+ .setCumulative()
+ .setUnit(Units.MILLISECONDS));
this.opts = options;
}
- public static List<SubmitRecord> defaultRuleError() {
+ public static SubmitRecord defaultRuleError() {
return createRuleError(DEFAULT_MSG);
}
- public static List<SubmitRecord> createRuleError(String err) {
+ public static SubmitRecord createRuleError(String err) {
SubmitRecord rec = new SubmitRecord();
rec.status = SubmitRecord.Status.RULE_ERROR;
rec.errorMessage = err;
- return Collections.singletonList(rec);
+ return rec;
}
public static SubmitTypeRecord defaultTypeError() {
@@ -87,46 +106,42 @@ public class SubmitRuleEvaluator {
* @param cd ChangeData to evaluate
*/
public List<SubmitRecord> evaluate(ChangeData cd) {
- Change change;
- ProjectState projectState;
- try {
- change = cd.change();
- if (change == null) {
- throw new StorageException("Change not found");
+ try (Timer0.Context ignored = submitRuleEvaluationLatency.start()) {
+ Change change;
+ ProjectState projectState;
+ try {
+ change = cd.change();
+ if (change == null) {
+ throw new StorageException("Change not found");
+ }
+
+ projectState = projectCache.get(cd.project());
+ if (projectState == null) {
+ throw new NoSuchProjectException(cd.project());
+ }
+ } catch (StorageException | NoSuchProjectException e) {
+ return Collections.singletonList(ruleError("Error looking up change " + cd.getId(), e));
}
- projectState = projectCache.get(cd.project());
- if (projectState == null) {
- throw new NoSuchProjectException(cd.project());
+ if ((!opts.allowClosed() || OnlineReindexMode.isActive()) && change.isClosed()) {
+ SubmitRecord rec = new SubmitRecord();
+ rec.status = SubmitRecord.Status.CLOSED;
+ return Collections.singletonList(rec);
}
- } catch (StorageException | NoSuchProjectException e) {
- return ruleError("Error looking up change " + cd.getId(), e);
- }
- if ((!opts.allowClosed() || OnlineReindexMode.isActive()) && change.isClosed()) {
- SubmitRecord rec = new SubmitRecord();
- rec.status = SubmitRecord.Status.CLOSED;
- return Collections.singletonList(rec);
+ // We evaluate all the plugin-defined evaluators,
+ // and then we collect the results in one list.
+ return Streams.stream(submitRules)
+ .map(c -> c.call(s -> s.evaluate(cd)))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(Collectors.toList());
}
-
- // We evaluate all the plugin-defined evaluators,
- // and then we collect the results in one list.
- return Streams.stream(submitRules)
- .map(c -> c.call(s -> s.evaluate(cd, opts)))
- .flatMap(Collection::stream)
- .collect(Collectors.toList());
}
- private List<SubmitRecord> ruleError(String err, Exception e) {
- if (opts.logErrors()) {
- if (e == null) {
- logger.atSevere().log(err);
- } else {
- logger.atSevere().withCause(e).log(err);
- }
- return defaultRuleError();
- }
- return createRuleError(err);
+ private SubmitRecord ruleError(String err, Exception e) {
+ logger.atSevere().withCause(e).log(err);
+ return defaultRuleError();
}
/**
@@ -136,24 +151,23 @@ public class SubmitRuleEvaluator {
* @param cd
*/
public SubmitTypeRecord getSubmitType(ChangeData cd) {
- ProjectState projectState;
- try {
- projectState = projectCache.get(cd.project());
- if (projectState == null) {
- throw new NoSuchProjectException(cd.project());
+ try (Timer0.Context ignored = submitTypeEvaluationLatency.start()) {
+ ProjectState projectState;
+ try {
+ projectState = projectCache.get(cd.project());
+ if (projectState == null) {
+ throw new NoSuchProjectException(cd.project());
+ }
+ } catch (NoSuchProjectException e) {
+ return typeError("Error looking up change " + cd.getId(), e);
}
- } catch (NoSuchProjectException e) {
- return typeError("Error looking up change " + cd.getId(), e);
- }
- return prologRule.getSubmitType(cd, opts);
+ return prologRule.getSubmitType(cd);
+ }
}
private SubmitTypeRecord typeError(String err, Exception e) {
- if (opts.logErrors()) {
- logger.atSevere().withCause(e).log(err);
- return defaultTypeError();
- }
- return SubmitTypeRecord.error(err);
+ logger.atSevere().withCause(e).log(err);
+ return defaultTypeError();
}
}
diff --git a/java/com/google/gerrit/server/project/SubmitRuleOptions.java b/java/com/google/gerrit/server/project/SubmitRuleOptions.java
index a4340b2bad..ad077c0aaa 100644
--- a/java/com/google/gerrit/server/project/SubmitRuleOptions.java
+++ b/java/com/google/gerrit/server/project/SubmitRuleOptions.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.project;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.common.Nullable;
/**
* Stable identifier for options passed to a particular submit rule evaluator.
@@ -26,12 +25,7 @@ import com.google.gerrit.common.Nullable;
@AutoValue
public abstract class SubmitRuleOptions {
private static final SubmitRuleOptions defaults =
- new AutoValue_SubmitRuleOptions.Builder()
- .allowClosed(false)
- .skipFilters(false)
- .logErrors(true)
- .rule(null)
- .build();
+ new AutoValue_SubmitRuleOptions.Builder().allowClosed(false).build();
public static SubmitRuleOptions defaults() {
return defaults;
@@ -43,25 +37,12 @@ public abstract class SubmitRuleOptions {
public abstract boolean allowClosed();
- public abstract boolean skipFilters();
-
- public abstract boolean logErrors();
-
- @Nullable
- public abstract String rule();
-
public abstract Builder toBuilder();
@AutoValue.Builder
public abstract static class Builder {
public abstract SubmitRuleOptions.Builder allowClosed(boolean allowClosed);
- public abstract SubmitRuleOptions.Builder skipFilters(boolean skipFilters);
-
- public abstract SubmitRuleOptions.Builder rule(@Nullable String rule);
-
- public abstract SubmitRuleOptions.Builder logErrors(boolean logErrors);
-
public abstract SubmitRuleOptions build();
}
}
diff --git a/java/com/google/gerrit/server/project/SuggestParentCandidates.java b/java/com/google/gerrit/server/project/SuggestParentCandidates.java
index d3dfdcdf04..fdc8b50765 100644
--- a/java/com/google/gerrit/server/project/SuggestParentCandidates.java
+++ b/java/com/google/gerrit/server/project/SuggestParentCandidates.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.project;
import static java.util.stream.Collectors.toList;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/project/testing/BUILD b/java/com/google/gerrit/server/project/testing/BUILD
index 968e3da744..988a89f1e3 100644
--- a/java/com/google/gerrit/server/project/testing/BUILD
+++ b/java/com/google/gerrit/server/project/testing/BUILD
@@ -5,9 +5,5 @@ java_library(
testonly = True,
srcs = glob(["*.java"]),
visibility = ["//visibility:public"],
- deps = [
- "//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/reviewdb:server",
- "//java/com/google/gerrit/server",
- ],
+ deps = ["//java/com/google/gerrit/common:server"],
)
diff --git a/java/com/google/gerrit/server/project/testing/TestLabels.java b/java/com/google/gerrit/server/project/testing/TestLabels.java
new file mode 100644
index 0000000000..6c2dddeb00
--- /dev/null
+++ b/java/com/google/gerrit/server/project/testing/TestLabels.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.project.testing;
+
+import com.google.gerrit.common.data.LabelFunction;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.LabelValue;
+import java.util.Arrays;
+
+public class TestLabels {
+ public static LabelType codeReview() {
+ return label(
+ "Code-Review",
+ value(2, "Looks good to me, approved"),
+ value(1, "Looks good to me, but someone else must approve"),
+ value(0, "No score"),
+ value(-1, "I would prefer this is not merged as is"),
+ value(-2, "This shall not be merged"));
+ }
+
+ public static LabelType verified() {
+ return label("Verified", value(1, "Verified"), value(0, "No score"), value(-1, "Fails"));
+ }
+
+ public static LabelType patchSetLock() {
+ LabelType label =
+ label("Patch-Set-Lock", value(1, "Patch Set Locked"), value(0, "Patch Set Unlocked"));
+ label.setFunction(LabelFunction.PATCH_SET_LOCK);
+ return label;
+ }
+
+ public static LabelValue value(int value, String text) {
+ return new LabelValue((short) value, text);
+ }
+
+ public static LabelType label(String name, LabelValue... values) {
+ return new LabelType(name, Arrays.asList(values));
+ }
+
+ private TestLabels() {}
+}
diff --git a/java/com/google/gerrit/server/project/testing/Util.java b/java/com/google/gerrit/server/project/testing/Util.java
deleted file mode 100644
index 204fa7b8f3..0000000000
--- a/java/com/google/gerrit/server/project/testing/Util.java
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.project.testing;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.data.LabelFunction;
-import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRange;
-import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.project.ProjectConfig;
-import java.util.Arrays;
-
-public class Util {
- public static final AccountGroup.UUID ADMIN = new AccountGroup.UUID("test.admin");
- public static final AccountGroup.UUID DEVS = new AccountGroup.UUID("test.devs");
-
- public static final LabelType codeReview() {
- return category(
- "Code-Review",
- value(2, "Looks good to me, approved"),
- value(1, "Looks good to me, but someone else must approve"),
- value(0, "No score"),
- value(-1, "I would prefer this is not merged as is"),
- value(-2, "This shall not be merged"));
- }
-
- public static final LabelType verified() {
- return category("Verified", value(1, "Verified"), value(0, "No score"), value(-1, "Fails"));
- }
-
- public static final LabelType patchSetLock() {
- LabelType label =
- category("Patch-Set-Lock", value(1, "Patch Set Locked"), value(0, "Patch Set Unlocked"));
- label.setFunction(LabelFunction.PATCH_SET_LOCK);
- return label;
- }
-
- public static LabelValue value(int value, String text) {
- return new LabelValue((short) value, text);
- }
-
- public static LabelType category(String name, LabelValue... values) {
- return new LabelType(name, Arrays.asList(values));
- }
-
- public static PermissionRule newRule(ProjectConfig project, AccountGroup.UUID groupUUID) {
- GroupReference group = new GroupReference(groupUUID, groupUUID.get());
- group = project.resolve(group);
-
- return new PermissionRule(group);
- }
-
- public static PermissionRule allow(
- ProjectConfig project,
- String permissionName,
- int min,
- int max,
- AccountGroup.UUID group,
- String ref) {
- PermissionRule rule = newRule(project, group);
- rule.setMin(min);
- rule.setMax(max);
- return grant(project, permissionName, rule, ref);
- }
-
- public static PermissionRule allowExclusive(
- ProjectConfig project,
- String permissionName,
- int min,
- int max,
- AccountGroup.UUID group,
- String ref) {
- PermissionRule rule = newRule(project, group);
- rule.setMin(min);
- rule.setMax(max);
- return grant(project, permissionName, rule, ref, true);
- }
-
- public static PermissionRule block(
- ProjectConfig project,
- String permissionName,
- int min,
- int max,
- AccountGroup.UUID group,
- String ref) {
- PermissionRule rule = newRule(project, group);
- rule.setMin(min);
- rule.setMax(max);
- PermissionRule r = grant(project, permissionName, rule, ref);
- r.setBlock();
- return r;
- }
-
- public static PermissionRule allow(
- ProjectConfig project, String permissionName, AccountGroup.UUID group, String ref) {
- return grant(project, permissionName, newRule(project, group), ref);
- }
-
- public static PermissionRule allow(
- ProjectConfig project,
- String permissionName,
- AccountGroup.UUID group,
- String ref,
- boolean exclusive) {
- return grant(project, permissionName, newRule(project, group), ref, exclusive);
- }
-
- public static PermissionRule allow(
- ProjectConfig project, String capabilityName, AccountGroup.UUID group) {
- return allow(project, capabilityName, group, (PermissionRange) null);
- }
-
- public static PermissionRule allow(
- ProjectConfig project,
- String capabilityName,
- AccountGroup.UUID group,
- PermissionRange customRange) {
- PermissionRule rule = newRule(project, group);
- project
- .getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true)
- .getPermission(capabilityName, true)
- .add(rule);
- if (GlobalCapability.hasRange(capabilityName)) {
- if (customRange == null) {
- PermissionRange.WithDefaults range = GlobalCapability.getRange(capabilityName);
- if (range != null) {
- rule.setRange(range.getDefaultMin(), range.getDefaultMax());
- }
- return rule;
- }
- rule.setRange(customRange.getMin(), customRange.getMax());
- }
- return rule;
- }
-
- public static PermissionRule remove(
- ProjectConfig project, String capabilityName, AccountGroup.UUID group) {
- PermissionRule rule = newRule(project, group);
- project
- .getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true)
- .getPermission(capabilityName, true)
- .remove(rule);
- return rule;
- }
-
- public static PermissionRule remove(
- ProjectConfig project, String permissionName, AccountGroup.UUID group, String ref) {
- PermissionRule rule = newRule(project, group);
- project.getAccessSection(ref, true).getPermission(permissionName, true).remove(rule);
- return rule;
- }
-
- public static PermissionRule block(
- ProjectConfig project, String capabilityName, AccountGroup.UUID group) {
- PermissionRule rule = newRule(project, group);
- project
- .getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true)
- .getPermission(capabilityName, true)
- .add(rule);
- return rule;
- }
-
- public static PermissionRule block(
- ProjectConfig project, String permissionName, AccountGroup.UUID group, String ref) {
- PermissionRule r = grant(project, permissionName, newRule(project, group), ref);
- r.setBlock();
- return r;
- }
-
- public static PermissionRule blockLabel(
- ProjectConfig project, String labelName, AccountGroup.UUID group, String ref) {
- return blockLabel(project, labelName, -1, 1, group, ref);
- }
-
- public static PermissionRule blockLabel(
- ProjectConfig project,
- String labelName,
- int min,
- int max,
- AccountGroup.UUID group,
- String ref) {
- PermissionRule r = grant(project, Permission.LABEL + labelName, newRule(project, group), ref);
- r.setBlock();
- r.setRange(min, max);
- return r;
- }
-
- public static PermissionRule deny(
- ProjectConfig project, String permissionName, AccountGroup.UUID group, String ref) {
- PermissionRule r = grant(project, permissionName, newRule(project, group), ref);
- r.setDeny();
- return r;
- }
-
- public static void doNotInherit(ProjectConfig project, String permissionName, String ref) {
- project
- .getAccessSection(ref, true) //
- .getPermission(permissionName, true) //
- .setExclusiveGroup(true);
- }
-
- private static PermissionRule grant(
- ProjectConfig project, String permissionName, PermissionRule rule, String ref) {
- return grant(project, permissionName, rule, ref, false);
- }
-
- private static PermissionRule grant(
- ProjectConfig project,
- String permissionName,
- PermissionRule rule,
- String ref,
- boolean exclusive) {
- Permission permission = project.getAccessSection(ref, true).getPermission(permissionName, true);
- if (exclusive) {
- permission.setExclusiveGroup(exclusive);
- }
- permission.add(rule);
- return rule;
- }
-
- private Util() {}
-}
diff --git a/java/com/google/gerrit/server/query/account/AccountPredicates.java b/java/com/google/gerrit/server/query/account/AccountPredicates.java
index cb96bc5eaa..1eed7ea29e 100644
--- a/java/com/google/gerrit/server/query/account/AccountPredicates.java
+++ b/java/com/google/gerrit/server/query/account/AccountPredicates.java
@@ -16,14 +16,14 @@ package com.google.gerrit.server.query.account;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.IndexPredicate;
import com.google.gerrit.index.query.Matchable;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.index.account.AccountField;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -44,7 +44,7 @@ public class AccountPredicates {
List<Predicate<AccountState>> preds = Lists.newArrayListWithCapacity(3);
Integer id = Ints.tryParse(query);
if (id != null) {
- preds.add(id(new Account.Id(id)));
+ preds.add(id(schema, Account.id(id)));
}
if (canSeeSecondaryEmails) {
preds.add(equalsNameIncludingSecondaryEmails(query));
@@ -64,9 +64,11 @@ public class AccountPredicates {
return Predicate.or(preds);
}
- public static Predicate<AccountState> id(Account.Id accountId) {
+ public static Predicate<AccountState> id(Schema<AccountState> schema, Account.Id accountId) {
return new AccountPredicate(
- AccountField.ID, AccountQueryBuilder.FIELD_ACCOUNT, accountId.toString());
+ schema.useLegacyNumericFields() ? AccountField.ID : AccountField.ID_STR,
+ AccountQueryBuilder.FIELD_ACCOUNT,
+ accountId.toString());
}
public static Predicate<AccountState> emailIncludingSecondaryEmails(String email) {
diff --git a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index 38336c1c17..42a83106e6 100644
--- a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -17,6 +17,7 @@ import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.NotSignedInException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -26,7 +27,6 @@ import com.google.gerrit.index.query.LimitPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountState;
@@ -204,7 +204,7 @@ public class AccountQueryBuilder extends QueryBuilder<AccountState, AccountQuery
if ("self".equalsIgnoreCase(query) || "me".equalsIgnoreCase(query)) {
try {
- return Predicate.or(defaultPredicate, AccountPredicates.id(self()));
+ return Predicate.or(defaultPredicate, AccountPredicates.id(args.schema(), self()));
} catch (QueryParseException e) {
// Skip.
}
diff --git a/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java b/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
index 7a7381d6d9..2e29bbd4f6 100644
--- a/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
+++ b/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
@@ -77,6 +77,6 @@ public class AccountQueryProcessor extends QueryProcessor<AccountState> {
@Override
protected String formatForLogging(AccountState accountState) {
- return accountState.getAccount().getId().toString();
+ return accountState.account().id().toString();
}
}
diff --git a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
index 8bbeb24018..0252a0689c 100644
--- a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
+++ b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
@@ -37,7 +37,7 @@ public class CanSeeChangePredicate extends PostFilterPredicate<AccountState> {
public boolean match(AccountState accountState) {
try {
permissionBackend
- .absentUser(accountState.getAccount().getId())
+ .absentUser(accountState.account().id())
.change(changeNotes)
.check(ChangePermission.READ);
return true;
diff --git a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index f2385ec789..091edca081 100644
--- a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -24,17 +24,16 @@ import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.InternalQuery;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.index.account.AccountField;
import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.inject.Inject;
-import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -77,7 +76,7 @@ public class InternalAccountQuery extends InternalQuery<AccountState, InternalAc
msg.append("Ambiguous external ID ").append(externalId).append(" for accounts: ");
Joiner.on(", ")
.appendTo(
- msg, accountStates.stream().map(AccountState.ACCOUNT_ID_FUNCTION).collect(toList()));
+ msg, accountStates.stream().map(a -> a.account().id().toString()).collect(toList()));
logger.atWarning().log(msg.toString());
}
return null;
@@ -103,7 +102,7 @@ public class InternalAccountQuery extends InternalQuery<AccountState, InternalAc
}
return query(AccountPredicates.preferredEmail(email)).stream()
- .filter(a -> a.getAccount().getPreferredEmail().equals(email))
+ .filter(a -> a.account().preferredEmail().equals(email))
.collect(toList());
}
@@ -114,15 +113,13 @@ public class InternalAccountQuery extends InternalQuery<AccountState, InternalAc
* @return multimap of the given emails to accounts that have a preferred email that exactly
* matches this email
*/
- public Multimap<String, AccountState> byPreferredEmail(String... emails) {
- List<String> emailList = Arrays.asList(emails);
-
+ public Multimap<String, AccountState> byPreferredEmail(List<String> emails) {
if (hasPreferredEmailExact()) {
List<List<AccountState>> r =
- query(emailList.stream().map(AccountPredicates::preferredEmailExact).collect(toList()));
+ query(emails.stream().map(AccountPredicates::preferredEmailExact).collect(toList()));
Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
- for (int i = 0; i < emailList.size(); i++) {
- accountsByEmail.putAll(emailList.get(i), r.get(i));
+ for (int i = 0; i < emails.size(); i++) {
+ accountsByEmail.putAll(emails.get(i), r.get(i));
}
return accountsByEmail;
}
@@ -132,13 +129,13 @@ public class InternalAccountQuery extends InternalQuery<AccountState, InternalAc
}
List<List<AccountState>> r =
- query(emailList.stream().map(AccountPredicates::preferredEmail).collect(toList()));
+ query(emails.stream().map(AccountPredicates::preferredEmail).collect(toList()));
Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
- for (int i = 0; i < emailList.size(); i++) {
- String email = emailList.get(i);
+ for (int i = 0; i < emails.size(); i++) {
+ String email = emails.get(i);
Set<AccountState> matchingAccounts =
r.get(i).stream()
- .filter(a -> a.getAccount().getPreferredEmail().equals(email))
+ .filter(a -> a.account().preferredEmail().equals(email))
.collect(toSet());
accountsByEmail.putAll(email, matchingAccounts);
}
diff --git a/java/com/google/gerrit/server/query/change/AgePredicate.java b/java/com/google/gerrit/server/query/change/AgePredicate.java
index 1cf2c2f9e6..36eb5b7c06 100644
--- a/java/com/google/gerrit/server/query/change/AgePredicate.java
+++ b/java/com/google/gerrit/server/query/change/AgePredicate.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.query.change;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.util.time.TimeUtil;
diff --git a/java/com/google/gerrit/server/query/change/AssigneePredicate.java b/java/com/google/gerrit/server/query/change/AssigneePredicate.java
index fb19e850bb..35a91c98ef 100644
--- a/java/com/google/gerrit/server/query/change/AssigneePredicate.java
+++ b/java/com/google/gerrit/server/query/change/AssigneePredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.index.change.ChangeField;
public class AssigneePredicate extends ChangeIndexPredicate {
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 3cd64b3a6d..c6beac4302 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -19,6 +19,7 @@ import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import com.google.auto.value.AutoValue;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
@@ -34,19 +35,19 @@ import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
@@ -74,6 +75,7 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.project.SubmitRuleOptions;
+import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -222,12 +224,18 @@ public class ChangeData {
* @return instance for testing.
*/
public static ChangeData createForTest(
- Project.NameKey project, Change.Id id, int currentPatchSetId) {
+ Project.NameKey project, Change.Id id, int currentPatchSetId, ObjectId commitId) {
ChangeData cd =
new ChangeData(
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, project, id, null, null);
- cd.currentPatchSet = new PatchSet(new PatchSet.Id(id, currentPatchSetId));
+ cd.currentPatchSet =
+ PatchSet.builder()
+ .id(PatchSet.id(id, currentPatchSetId))
+ .commitId(commitId)
+ .uploader(Account.id(1000))
+ .createdOn(TimeUtil.nowTs())
+ .build();
return cd;
}
@@ -357,6 +365,7 @@ public class ChangeData {
return allUsersName;
}
+ @VisibleForTesting
public void setCurrentFilePaths(List<String> filePaths) {
PatchSet ps = currentPatchSet();
if (ps != null) {
@@ -387,7 +396,7 @@ public class ChangeData {
return Optional.empty();
}
- ObjectId id = ObjectId.fromString(ps.getRevision().get());
+ ObjectId id = ps.commitId();
Whitespace ws = Whitespace.IGNORE_NONE;
PatchListKey pk =
parentCount > 1
@@ -497,7 +506,7 @@ public class ChangeData {
return null;
}
for (PatchSet p : patchSets()) {
- if (p.getId().equals(c.currentPatchSetId())) {
+ if (p.id().equals(c.currentPatchSetId())) {
currentPatchSet = p;
return p;
}
@@ -580,10 +589,9 @@ public class ChangeData {
if (ps == null) {
return false;
}
- String sha1 = ps.getRevision().get();
try (Repository repo = repoManager.openRepository(project());
RevWalk walk = new RevWalk(repo)) {
- RevCommit c = walk.parseCommit(ObjectId.fromString(sha1));
+ RevCommit c = walk.parseCommit(ps.commitId());
commitMessage = c.getFullMessage();
commitFooters = c.getFooterLines();
author = c.getAuthorIdent();
@@ -610,11 +618,11 @@ public class ChangeData {
/** @return patch with the given ID, or null if it does not exist. */
public PatchSet patchSet(PatchSet.Id psId) {
- if (currentPatchSet != null && currentPatchSet.getId().equals(psId)) {
+ if (currentPatchSet != null && currentPatchSet.id().equals(psId)) {
return currentPatchSet;
}
for (PatchSet ps : patchSets()) {
- if (ps.getId().equals(psId)) {
+ if (ps.id().equals(psId)) {
return ps;
}
}
@@ -901,7 +909,7 @@ public class ChangeData {
}
try (Repository repo = repoManager.openRepository(project())) {
- Ref ref = repo.getRefDatabase().exactRef(c.getDest().get());
+ Ref ref = repo.getRefDatabase().exactRef(c.getDest().branch());
SubmitTypeRecord str = submitTypeRecord();
if (!str.isOk()) {
// If submit type rules are broken, it's definitely not mergeable.
@@ -911,13 +919,7 @@ public class ChangeData {
String mergeStrategy =
mergeUtilFactory.create(projectCache.get(project())).mergeStrategyName();
mergeable =
- mergeabilityCache.get(
- ObjectId.fromString(ps.getRevision().get()),
- ref,
- str.type,
- mergeStrategy,
- c.getDest(),
- repo);
+ mergeabilityCache.get(ps.commitId(), ref, str.type, mergeStrategy, c.getDest(), repo);
} catch (IOException e) {
throw new StorageException(e);
}
@@ -995,11 +997,11 @@ public class ChangeData {
PatchSet ps = currentPatchSet();
if (ps != null) {
- if (stars.contains(StarredChangesUtil.REVIEWED_LABEL + "/" + ps.getPatchSetId())) {
+ if (stars.contains(StarredChangesUtil.REVIEWED_LABEL + "/" + ps.number())) {
return true;
}
- if (stars.contains(StarredChangesUtil.UNREVIEWED_LABEL + "/" + ps.getPatchSetId())) {
+ if (stars.contains(StarredChangesUtil.UNREVIEWED_LABEL + "/" + ps.number())) {
return false;
}
}
diff --git a/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
index 74ad0ef52d..05cc6ca1e2 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.index.change.ChangeField;
/** Predicate over Change-Id strings (aka Change.Key). */
diff --git a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
index 8015a33af7..819fc2b65b 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.index.query.IsVisibleToPredicate;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.InternalUser;
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index f998ad3fc6..df729cb63c 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import static com.google.gerrit.reviewdb.client.Change.CHANGE_ID_PATTERN;
+import static com.google.gerrit.entities.Change.CHANGE_ID_PATTERN;
import static com.google.gerrit.server.account.AccountResolver.isSelf;
import static com.google.gerrit.server.query.change.ChangeData.asChanges;
import static java.util.stream.Collectors.toList;
@@ -29,6 +29,11 @@ import com.google.common.primitives.Ints;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.NotSignedInException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicMap;
@@ -41,11 +46,6 @@ import com.google.gerrit.index.query.QueryBuilder;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.QueryRequiresAuthException;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -62,10 +62,7 @@ import com.google.gerrit.server.account.VersionedAccountQueries;
import com.google.gerrit.server.change.ChangeTriplet;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
@@ -94,7 +91,6 @@ import java.util.function.Function;
import java.util.regex.Pattern;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
/** Parses a query string meant to be applied to change objects. */
@@ -189,7 +185,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuil
public static final String ARG_ID_USER = "user";
public static final String ARG_ID_GROUP = "group";
public static final String ARG_ID_OWNER = "owner";
- public static final Account.Id OWNER_ACCOUNT_ID = new Account.Id(0);
+ public static final Account.Id OWNER_ACCOUNT_ID = Account.id(0);
private static final QueryBuilder.Definition<ChangeData, ChangeQueryBuilder> mydef =
new QueryBuilder.Definition<>(ChangeQueryBuilder.class);
@@ -400,8 +396,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuil
private final Arguments args;
- private @Inject @GerritServerConfig Config cfg;
-
@Inject
ChangeQueryBuilder(Arguments args) {
this(mydef, args);
@@ -452,13 +446,15 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuil
if (triplet.isPresent()) {
return Predicate.and(
project(triplet.get().project().get()),
- branch(triplet.get().branch().get()),
+ branch(triplet.get().branch().branch()),
new ChangeIdPredicate(parseChangeId(triplet.get().id().get())));
}
if (PAT_LEGACY_ID.matcher(query).matches()) {
Integer id = Ints.tryParse(query);
if (id != null) {
- return new LegacyChangeIdPredicate(new Change.Id(id));
+ return args.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(Change.id(id))
+ : new LegacyChangeIdStrPredicate(Change.id(id));
}
} else if (PAT_CHANGE_ID.matcher(query).matches()) {
return new ChangeIdPredicate(parseChangeId(query));
@@ -566,11 +562,11 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuil
}
if ("assigned".equalsIgnoreCase(value)) {
- return Predicate.not(new AssigneePredicate(new Account.Id(ChangeField.NO_ASSIGNEE)));
+ return Predicate.not(new AssigneePredicate(Account.id(ChangeField.NO_ASSIGNEE)));
}
if ("unassigned".equalsIgnoreCase(value)) {
- return new AssigneePredicate(new Account.Id(ChangeField.NO_ASSIGNEE));
+ return new AssigneePredicate(Account.id(ChangeField.NO_ASSIGNEE));
}
if ("submittable".equalsIgnoreCase(value)) {
@@ -733,10 +729,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuil
@Operator
public Predicate<ChangeData> extension(String ext) throws QueryParseException {
if (args.getSchema().hasField(ChangeField.EXTENSION)) {
- if (ext.isEmpty()
- && IndexModule.getIndexType(cfg).equalsIgnoreCase(IndexType.ELASTICSEARCH.name())) {
- return new FileWithNoExtensionInElasticPredicate();
- }
return new FileExtensionPredicate(ext);
}
throw new QueryParseException("'extension' operator is not supported by change index version");
@@ -775,11 +767,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuil
if (directory.startsWith("^")) {
return new RegexDirectoryPredicate(directory);
}
-
- if (IndexModule.getIndexType(cfg).equalsIgnoreCase(IndexType.ELASTICSEARCH.name())
- && (directory.isEmpty() || directory.equals("/"))) {
- return Predicate.any();
- }
return new DirectoryPredicate(directory);
}
throw new QueryParseException("'directory' operator is not supported by change index version");
@@ -1184,7 +1171,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuil
try (Repository git = args.repoManager.openRepository(args.allUsersName)) {
VersionedAccountDestinations d = VersionedAccountDestinations.forUser(self());
d.load(args.allUsersName, git);
- Set<Branch.NameKey> destinations = d.getDestinationList().getDestinations(name);
+ Set<BranchNameKey> destinations = d.getDestinationList().getDestinations(name);
if (destinations != null && !destinations.isEmpty()) {
return new DestinationPredicate(destinations, name);
}
@@ -1321,7 +1308,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuil
private Set<Account.Id> getMembers(AccountGroup.UUID g) throws IOException {
Set<Account.Id> accounts;
Set<Account.Id> allMembers =
- args.groupMembers.listAccounts(g).stream().map(Account::getId).collect(toSet());
+ args.groupMembers.listAccounts(g).stream().map(Account::id).collect(toSet());
int maxTerms = args.indexConfig.maxTerms();
if (allMembers.size() > maxTerms) {
// limit the number of query terms otherwise Gerrit will barf
diff --git a/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java b/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
index 66790e7579..88e93d9d25 100644
--- a/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
@@ -18,9 +18,9 @@ import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Change.Status;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Status;
import com.google.gerrit.server.index.change.ChangeField;
import java.util.ArrayList;
import java.util.List;
diff --git a/java/com/google/gerrit/server/query/change/CommentByPredicate.java b/java/com/google/gerrit/server/query/change/CommentByPredicate.java
index 0747bb2784..bd7981c107 100644
--- a/java/com/google/gerrit/server/query/change/CommentByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommentByPredicate.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
import com.google.gerrit.server.index.change.ChangeField;
import java.util.Objects;
diff --git a/java/com/google/gerrit/server/query/change/CommentPredicate.java b/java/com/google/gerrit/server/query/change/CommentPredicate.java
index d193bb68bc..4b14f081a0 100644
--- a/java/com/google/gerrit/server/query/change/CommentPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommentPredicate.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.query.change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
@@ -32,9 +33,15 @@ public class CommentPredicate extends ChangeIndexPredicate {
@Override
public boolean match(ChangeData object) {
try {
- Predicate<ChangeData> p = Predicate.and(new LegacyChangeIdPredicate(object.getId()), this);
+ Change.Id id = object.getId();
+ Predicate<ChangeData> p =
+ Predicate.and(
+ index.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(id)
+ : new LegacyChangeIdStrPredicate(id),
+ this);
for (ChangeData cData : index.getSource(p, IndexedChangeQuery.oneResult()).read()) {
- if (cData.getId().equals(object.getId())) {
+ if (cData.getId().equals(id)) {
return true;
}
}
diff --git a/java/com/google/gerrit/server/query/change/CommitPredicate.java b/java/com/google/gerrit/server/query/change/CommitPredicate.java
index 567f58d59a..b54ee64441 100644
--- a/java/com/google/gerrit/server/query/change/CommitPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommitPredicate.java
@@ -14,16 +14,17 @@
package com.google.gerrit.server.query.change;
+import static com.google.gerrit.git.ObjectIds.matchesAbbreviation;
import static com.google.gerrit.server.index.change.ChangeField.COMMIT;
import static com.google.gerrit.server.index.change.ChangeField.EXACT_COMMIT;
-import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.index.FieldDef;
-import com.google.gerrit.reviewdb.client.PatchSet;
public class CommitPredicate extends ChangeIndexPredicate {
static FieldDef<ChangeData, ?> commitField(String id) {
- if (id.length() == OBJECT_ID_STRING_LENGTH) {
+ if (id.length() == ObjectIds.STR_LEN) {
return EXACT_COMMIT;
}
return COMMIT;
@@ -45,9 +46,10 @@ public class CommitPredicate extends ChangeIndexPredicate {
}
protected boolean equals(PatchSet p, String id) {
- boolean exact = getField() == EXACT_COMMIT;
- String rev = p.getRevision() != null ? p.getRevision().get() : null;
- return (exact && id.equals(rev)) || (!exact && rev != null && rev.startsWith(id));
+ if (getField() == EXACT_COMMIT) {
+ return p.commitId().name().equals(id);
+ }
+ return matchesAbbreviation(p.commitId(), id);
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
index d415f71627..17e4a59647 100644
--- a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -20,14 +20,14 @@ import static java.util.concurrent.TimeUnit.MINUTES;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.query.PostFilterPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.project.NoSuchProjectException;
@@ -86,8 +86,12 @@ public class ConflictsPredicate {
List<Predicate<ChangeData>> and = new ArrayList<>(5);
and.add(new ProjectPredicate(c.getProject().get()));
- and.add(new RefPredicate(c.getDest().get()));
- and.add(Predicate.not(new LegacyChangeIdPredicate(c.getId())));
+ and.add(new RefPredicate(c.getDest().branch()));
+ and.add(
+ Predicate.not(
+ args.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(c.getId())
+ : new LegacyChangeIdStrPredicate(c.getId())));
and.add(Predicate.or(filePredicates));
ChangeDataCache changeDataCache = new ChangeDataCache(cd, args.projectCache);
@@ -97,7 +101,7 @@ public class ConflictsPredicate {
private static final class CheckConflict extends PostFilterPredicate<ChangeData> {
private final Arguments args;
- private final Branch.NameKey dest;
+ private final BranchNameKey dest;
private final ChangeDataCache changeDataCache;
CheckConflict(String value, Arguments args, Change c, ChangeDataCache changeDataCache) {
@@ -131,7 +135,7 @@ public class ConflictsPredicate {
return false;
}
- other = ObjectId.fromString(object.currentPatchSet().getRevision().get());
+ other = object.currentPatchSet().commitId();
ConflictKey conflictsKey =
ConflictKey.create(
changeDataCache.getTestAgainst(),
@@ -207,7 +211,7 @@ public class ConflictsPredicate {
ObjectId getTestAgainst() {
if (testAgainst == null) {
- testAgainst = ObjectId.fromString(cd.currentPatchSet().getRevision().get());
+ testAgainst = cd.currentPatchSet().commitId();
}
return testAgainst;
}
diff --git a/java/com/google/gerrit/server/query/change/DestinationPredicate.java b/java/com/google/gerrit/server/query/change/DestinationPredicate.java
index 702745e9b4..3c3d70f872 100644
--- a/java/com/google/gerrit/server/query/change/DestinationPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DestinationPredicate.java
@@ -14,15 +14,15 @@
package com.google.gerrit.server.query.change;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.query.PostFilterPredicate;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
import java.util.Set;
public class DestinationPredicate extends PostFilterPredicate<ChangeData> {
- protected Set<Branch.NameKey> destinations;
+ protected Set<BranchNameKey> destinations;
- public DestinationPredicate(Set<Branch.NameKey> destinations, String value) {
+ public DestinationPredicate(Set<BranchNameKey> destinations, String value) {
super(ChangeQueryBuilder.FIELD_DESTINATION, value);
this.destinations = destinations;
}
diff --git a/java/com/google/gerrit/server/query/change/EditByPredicate.java b/java/com/google/gerrit/server/query/change/EditByPredicate.java
index dfe73105e2..0fd66f8b9a 100644
--- a/java/com/google/gerrit/server/query/change/EditByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EditByPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.index.change.ChangeField;
public class EditByPredicate extends ChangeIndexPredicate {
diff --git a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index acf2e25d03..62b1144184 100644
--- a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -16,11 +16,11 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -60,7 +60,7 @@ public class EqualsLabelPredicate extends ChangeIndexPredicate {
return false;
}
- ProjectState project = projectCache.get(c.getDest().getParentKey());
+ ProjectState project = projectCache.get(c.getDest().project());
if (project == null) {
// The project has disappeared.
//
@@ -76,7 +76,7 @@ public class EqualsLabelPredicate extends ChangeIndexPredicate {
for (PatchSetApproval p : object.currentApprovals()) {
if (labelType.matches(p)) {
hasVote = true;
- if (match(object, p.getValue(), p.getAccountId())) {
+ if (match(object, p.value(), p.accountId())) {
return true;
}
}
diff --git a/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java b/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
index c6ade75e94..6683c91a64 100644
--- a/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.query.change;
import static com.google.gerrit.server.index.change.ChangeField.EXACT_TOPIC;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
public class ExactTopicPredicate extends ChangeIndexPredicate {
public ExactTopicPredicate(String topic) {
diff --git a/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java b/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
index 140f26b165..d558b0f1c8 100644
--- a/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
@@ -17,10 +17,10 @@ package com.google.gerrit.server.query.change;
import static com.google.gerrit.server.index.change.ChangeField.FUZZY_TOPIC;
import com.google.common.collect.Iterables;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
@@ -43,7 +43,10 @@ public class FuzzyTopicPredicate extends ChangeIndexPredicate {
return false;
}
try {
- Predicate<ChangeData> thisId = new LegacyChangeIdPredicate(cd.getId());
+ Predicate<ChangeData> thisId =
+ index.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(cd.getId())
+ : new LegacyChangeIdStrPredicate(cd.getId());
Iterable<ChangeData> results =
index.getSource(and(thisId, this), IndexedChangeQuery.oneResult()).read();
return !Iterables.isEmpty(results);
diff --git a/java/com/google/gerrit/server/query/change/GroupPredicate.java b/java/com/google/gerrit/server/query/change/GroupPredicate.java
index 7f7bcff528..99f37d629f 100644
--- a/java/com/google/gerrit/server/query/change/GroupPredicate.java
+++ b/java/com/google/gerrit/server/query/change/GroupPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.server.index.change.ChangeField;
import java.util.List;
@@ -26,7 +26,7 @@ public class GroupPredicate extends ChangeIndexPredicate {
@Override
public boolean match(ChangeData cd) {
for (PatchSet ps : cd.patchSets()) {
- List<String> groups = ps.getGroups();
+ List<String> groups = ps.groups();
if (groups != null && groups.contains(getValue())) {
return true;
}
diff --git a/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java b/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
index e57a8b39d0..6d1576fdd1 100644
--- a/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.index.change.ChangeField;
public class HasDraftByPredicate extends ChangeIndexPredicate {
diff --git a/java/com/google/gerrit/server/query/change/HasStarsPredicate.java b/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
index 0c99cdf0bc..2fbd1e81ff 100644
--- a/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
+++ b/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.index.change.ChangeField;
public class HasStarsPredicate extends ChangeIndexPredicate {
diff --git a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index 74a0f71d1a..6605c23da2 100644
--- a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -25,13 +25,13 @@ import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.query.InternalQuery;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.inject.Inject;
@@ -55,8 +55,13 @@ import org.eclipse.jgit.lib.Repository;
* holding on to a single instance.
*/
public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChangeQuery> {
- private static Predicate<ChangeData> ref(Branch.NameKey branch) {
- return new RefPredicate(branch.get());
+ @FunctionalInterface
+ static interface ChangeIdPredicateFactory {
+ Predicate<ChangeData> create(Change.Id id);
+ }
+
+ private static Predicate<ChangeData> ref(BranchNameKey branch) {
+ return new RefPredicate(branch.branch());
}
private static Predicate<ChangeData> change(Change.Key key) {
@@ -78,6 +83,9 @@ public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChang
private final ChangeData.Factory changeDataFactory;
private final ChangeNotes.Factory notesFactory;
+ // TODO(davido): Remove the below fields when support for legacy numeric fields is removed.
+ private final ChangeIdPredicateFactory predicateFactory;
+
@Inject
InternalChangeQuery(
ChangeQueryProcessor queryProcessor,
@@ -88,6 +96,11 @@ public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChang
super(queryProcessor, indexes, indexConfig);
this.changeDataFactory = changeDataFactory;
this.notesFactory = notesFactory;
+ predicateFactory =
+ (id) ->
+ schema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(id)
+ : new LegacyChangeIdStrPredicate(id);
}
public List<ChangeData> byKey(Change.Key key) {
@@ -99,48 +112,48 @@ public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChang
}
public List<ChangeData> byLegacyChangeId(Change.Id id) {
- return query(new LegacyChangeIdPredicate(id));
+ return query(predicateFactory.create(id));
}
public List<ChangeData> byLegacyChangeIds(Collection<Change.Id> ids) {
List<Predicate<ChangeData>> preds = new ArrayList<>(ids.size());
for (Change.Id id : ids) {
- preds.add(new LegacyChangeIdPredicate(id));
+ preds.add(predicateFactory.create(id));
}
return query(or(preds));
}
- public List<ChangeData> byBranchKey(Branch.NameKey branch, Change.Key key) {
+ public List<ChangeData> byBranchKey(BranchNameKey branch, Change.Key key) {
return query(byBranchKeyPred(branch, key));
}
public List<ChangeData> byBranchKeyOpen(Project.NameKey project, String branch, Change.Key key) {
- return query(and(byBranchKeyPred(new Branch.NameKey(project, branch), key), open()));
+ return query(and(byBranchKeyPred(BranchNameKey.create(project, branch), key), open()));
}
public static Predicate<ChangeData> byBranchKeyOpenPred(
Project.NameKey project, String branch, Change.Key key) {
- return and(byBranchKeyPred(new Branch.NameKey(project, branch), key), open());
+ return and(byBranchKeyPred(BranchNameKey.create(project, branch), key), open());
}
- private static Predicate<ChangeData> byBranchKeyPred(Branch.NameKey branch, Change.Key key) {
- return and(ref(branch), project(branch.getParentKey()), change(key));
+ private static Predicate<ChangeData> byBranchKeyPred(BranchNameKey branch, Change.Key key) {
+ return and(ref(branch), project(branch.project()), change(key));
}
public List<ChangeData> byProject(Project.NameKey project) {
return query(project(project));
}
- public List<ChangeData> byBranchOpen(Branch.NameKey branch) {
- return query(and(ref(branch), project(branch.getParentKey()), open()));
+ public List<ChangeData> byBranchOpen(BranchNameKey branch) {
+ return query(and(ref(branch), project(branch.project()), open()));
}
- public List<ChangeData> byBranchNew(Branch.NameKey branch) {
- return query(and(ref(branch), project(branch.getParentKey()), status(Change.Status.NEW)));
+ public List<ChangeData> byBranchNew(BranchNameKey branch) {
+ return query(and(ref(branch), project(branch.project()), status(Change.Status.NEW)));
}
public Iterable<ChangeData> byCommitsOnBranchNotMerged(
- Repository repo, Branch.NameKey branch, Collection<String> hashes) throws IOException {
+ Repository repo, BranchNameKey branch, Collection<String> hashes) throws IOException {
return byCommitsOnBranchNotMerged(
repo,
branch,
@@ -151,7 +164,7 @@ public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChang
@VisibleForTesting
Iterable<ChangeData> byCommitsOnBranchNotMerged(
- Repository repo, Branch.NameKey branch, Collection<String> hashes, int indexLimit)
+ Repository repo, BranchNameKey branch, Collection<String> hashes, int indexLimit)
throws IOException {
if (hashes.size() > indexLimit) {
return byCommitsOnBranchNotMergedFromDatabase(repo, branch, hashes);
@@ -160,7 +173,7 @@ public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChang
}
private Iterable<ChangeData> byCommitsOnBranchNotMergedFromDatabase(
- Repository repo, Branch.NameKey branch, Collection<String> hashes) throws IOException {
+ Repository repo, BranchNameKey branch, Collection<String> hashes) throws IOException {
Set<Change.Id> changeIds = Sets.newHashSetWithExpectedSize(hashes.size());
String lastPrefix = null;
for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_CHANGES)) {
@@ -180,7 +193,7 @@ public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChang
List<ChangeNotes> notes =
notesFactory.create(
- branch.getParentKey(),
+ branch.project(),
changeIds,
cn -> {
Change c = cn.getChange();
@@ -190,11 +203,11 @@ public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChang
}
private Iterable<ChangeData> byCommitsOnBranchNotMergedFromIndex(
- Branch.NameKey branch, Collection<String> hashes) {
+ BranchNameKey branch, Collection<String> hashes) {
return query(
and(
ref(branch),
- project(branch.getParentKey()),
+ project(branch.project()),
not(status(Change.Status.MERGED)),
or(commits(hashes))));
}
@@ -241,8 +254,8 @@ public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChang
return query(byBranchCommitPred(project, branch, hash));
}
- public List<ChangeData> byBranchCommit(Branch.NameKey branch, String hash) {
- return byBranchCommit(branch.getParentKey().get(), branch.get(), hash);
+ public List<ChangeData> byBranchCommit(BranchNameKey branch, String hash) {
+ return byBranchCommit(branch.project().get(), branch.branch(), hash);
}
public List<ChangeData> byBranchCommitOpen(String project, String branch, String hash) {
diff --git a/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java b/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
index ddb6f329b9..4b32b066f0 100644
--- a/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.query.change;
import static com.google.gerrit.server.index.change.ChangeField.REVIEWEDBY;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.change.ChangeField;
import java.util.ArrayList;
import java.util.Collection;
@@ -25,7 +25,7 @@ import java.util.List;
import java.util.Set;
public class IsReviewedPredicate extends ChangeIndexPredicate {
- protected static final Account.Id NOT_REVIEWED = new Account.Id(ChangeField.NOT_REVIEWED);
+ protected static final Account.Id NOT_REVIEWED = Account.id(ChangeField.NOT_REVIEWED);
public static Predicate<ChangeData> create() {
return Predicate.not(new IsReviewedPredicate(NOT_REVIEWED));
diff --git a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
index c52d2a925c..3a43fd3669 100644
--- a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
@@ -93,7 +93,7 @@ public class IsWatchedByPredicate extends AndPredicate<ChangeData> {
throws QueryParseException {
CurrentUser user = args.getUser();
if (user.isIdentifiedUser()) {
- return user.asIdentifiedUser().state().getProjectWatches().keySet();
+ return user.asIdentifiedUser().state().projectWatches().keySet();
}
return Collections.emptySet();
}
diff --git a/java/com/google/gerrit/server/query/change/LabelPredicate.java b/java/com/google/gerrit/server/query/change/LabelPredicate.java
index b5d375cd64..38d1dbed8b 100644
--- a/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.query.change;
import com.google.common.collect.Lists;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.query.OrPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.RangeUtil;
import com.google.gerrit.index.query.RangeUtil.Range;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectCache;
diff --git a/java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java b/java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java
index fe4d4e10ca..d531236286 100644
--- a/java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/LegacyChangeIdPredicate.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.query.change;
import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
/** Predicate over change number (aka legacy ID or Change.Id). */
public class LegacyChangeIdPredicate extends ChangeIndexPredicate {
diff --git a/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java b/java/com/google/gerrit/server/query/change/LegacyChangeIdStrPredicate.java
index d886bafa0c..bae9c0d1fd 100644
--- a/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java
+++ b/java/com/google/gerrit/server/query/change/LegacyChangeIdStrPredicate.java
@@ -14,20 +14,22 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.index.query.PostFilterPredicate;
-import com.google.gerrit.server.index.change.ChangeField;
+import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID_STR;
-public class FileWithNoExtensionInElasticPredicate extends PostFilterPredicate<ChangeData> {
+import com.google.gerrit.entities.Change;
- private static final String NO_EXT = "";
+/** Predicate over change number (aka legacy ID or Change.Id). */
+public class LegacyChangeIdStrPredicate extends ChangeIndexPredicate {
+ protected final Change.Id id;
- public FileWithNoExtensionInElasticPredicate() {
- super(ChangeField.EXTENSION.getName(), NO_EXT);
+ public LegacyChangeIdStrPredicate(Change.Id id) {
+ super(LEGACY_ID_STR, ChangeQueryBuilder.FIELD_CHANGE, id.toString());
+ this.id = id;
}
@Override
- public boolean match(ChangeData cd) {
- return ChangeField.getExtensions(cd).contains(NO_EXT);
+ public boolean match(ChangeData object) {
+ return id.equals(object.getId());
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/MessagePredicate.java b/java/com/google/gerrit/server/query/change/MessagePredicate.java
index 0bd8c881c4..44cbd8eed3 100644
--- a/java/com/google/gerrit/server/query/change/MessagePredicate.java
+++ b/java/com/google/gerrit/server/query/change/MessagePredicate.java
@@ -33,7 +33,12 @@ public class MessagePredicate extends ChangeIndexPredicate {
@Override
public boolean match(ChangeData object) {
try {
- Predicate<ChangeData> p = Predicate.and(new LegacyChangeIdPredicate(object.getId()), this);
+ Predicate<ChangeData> p =
+ Predicate.and(
+ index.getSchema().useLegacyNumericFields()
+ ? new LegacyChangeIdPredicate(object.getId())
+ : new LegacyChangeIdStrPredicate(object.getId()),
+ this);
for (ChangeData cData : index.getSource(p, IndexedChangeQuery.oneResult()).read()) {
if (cData.getId().equals(object.getId())) {
return true;
diff --git a/java/com/google/gerrit/server/query/change/OrSource.java b/java/com/google/gerrit/server/query/change/OrSource.java
index ba06b89f15..983d9b4cd4 100644
--- a/java/com/google/gerrit/server/query/change/OrSource.java
+++ b/java/com/google/gerrit/server/query/change/OrSource.java
@@ -14,17 +14,21 @@
package com.google.gerrit.server.query.change;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.query.FieldBundle;
-import com.google.gerrit.index.query.ListResultSet;
+import com.google.gerrit.index.query.LazyResultSet;
import com.google.gerrit.index.query.OrPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.ResultSet;
-import com.google.gerrit.reviewdb.client.Change;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
public class OrSource extends OrPredicate<ChangeData> implements ChangeDataSource {
@@ -36,22 +40,29 @@ public class OrSource extends OrPredicate<ChangeData> implements ChangeDataSourc
@Override
public ResultSet<ChangeData> read() {
- // TODO(spearce) This probably should be more lazy.
- //
- List<ChangeData> r = new ArrayList<>();
- Set<Change.Id> have = new HashSet<>();
- for (Predicate<ChangeData> p : getChildren()) {
- if (p instanceof ChangeDataSource) {
- for (ChangeData cd : ((ChangeDataSource) p).read()) {
- if (have.add(cd.getId())) {
- r.add(cd);
- }
- }
- } else {
- throw new StorageException("No ChangeDataSource: " + p);
- }
+ Optional<Predicate<ChangeData>> nonChangeDataSource =
+ getChildren().stream().filter(p -> !(p instanceof ChangeDataSource)).findAny();
+ if (nonChangeDataSource.isPresent()) {
+ throw new StorageException("No ChangeDataSource: " + nonChangeDataSource.get());
}
- return new ListResultSet<>(r);
+
+ // ResultSets are lazy. Calling #read here first and then dealing with ResultSets only when
+ // requested allows the index to run asynchronous queries.
+ List<ResultSet<ChangeData>> results =
+ getChildren().stream().map(p -> ((ChangeDataSource) p).read()).collect(toImmutableList());
+ return new LazyResultSet<>(
+ () -> {
+ List<ChangeData> r = new ArrayList<>();
+ Set<Change.Id> have = new HashSet<>();
+ for (ResultSet<ChangeData> resultSet : results) {
+ for (ChangeData result : resultSet) {
+ if (have.add(result.getId())) {
+ r.add(result);
+ }
+ }
+ }
+ return ImmutableList.copyOf(r);
+ });
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 08e6f3339b..e8754995d5 100644
--- a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -19,11 +19,11 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.QueryResult;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.DynamicOptions;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.data.ChangeAttribute;
diff --git a/java/com/google/gerrit/server/query/change/OwnerPredicate.java b/java/com/google/gerrit/server/query/change/OwnerPredicate.java
index 100a66c7a7..923a9ca924 100644
--- a/java/com/google/gerrit/server/query/change/OwnerPredicate.java
+++ b/java/com/google/gerrit/server/query/change/OwnerPredicate.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.index.change.ChangeField;
public class OwnerPredicate extends ChangeIndexPredicate {
diff --git a/java/com/google/gerrit/server/query/change/OwnerinPredicate.java b/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
index 41b32040aa..c2bc5d9514 100644
--- a/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
+++ b/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.query.change;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.query.PostFilterPredicate;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.IdentifiedUser;
public class OwnerinPredicate extends PostFilterPredicate<ChangeData> {
diff --git a/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java b/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java
index 17d6448915..5deb7f5368 100644
--- a/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java
@@ -15,10 +15,10 @@
package com.google.gerrit.server.query.change;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.index.query.OrPredicate;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChildProjects;
import com.google.gerrit.server.project.ProjectCache;
@@ -40,7 +40,7 @@ public class ParentProjectPredicate extends OrPredicate<ChangeData> {
protected static List<Predicate<ChangeData>> predicates(
ProjectCache projectCache, ChildProjects childProjects, String value) {
- ProjectState projectState = projectCache.get(new Project.NameKey(value));
+ ProjectState projectState = projectCache.get(Project.nameKey(value));
if (projectState == null) {
return Collections.emptyList();
}
diff --git a/java/com/google/gerrit/server/query/change/ProjectPredicate.java b/java/com/google/gerrit/server/query/change/ProjectPredicate.java
index febd6c64f5..db5a9327f0 100644
--- a/java/com/google/gerrit/server/query/change/ProjectPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ProjectPredicate.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.index.change.ChangeField;
public class ProjectPredicate extends ChangeIndexPredicate {
@@ -24,7 +24,7 @@ public class ProjectPredicate extends ChangeIndexPredicate {
}
protected Project.NameKey getValueKey() {
- return new Project.NameKey(getValue());
+ return Project.nameKey(getValue());
}
@Override
@@ -34,7 +34,7 @@ public class ProjectPredicate extends ChangeIndexPredicate {
return false;
}
- Project.NameKey p = change.getDest().getParentKey();
+ Project.NameKey p = change.getDest().project();
return p.equals(getValueKey());
}
diff --git a/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java b/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
index 744f4d27ef..c23a17570c 100644
--- a/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.index.change.ChangeField;
public class ProjectPrefixPredicate extends ChangeIndexPredicate {
@@ -25,7 +25,7 @@ public class ProjectPrefixPredicate extends ChangeIndexPredicate {
@Override
public boolean match(ChangeData object) {
Change c = object.change();
- return c != null && c.getDest().getParentKey().get().startsWith(getValue());
+ return c != null && c.getDest().project().get().startsWith(getValue());
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/RefPredicate.java b/java/com/google/gerrit/server/query/change/RefPredicate.java
index 2ed4c99dd4..1c70a62d9f 100644
--- a/java/com/google/gerrit/server/query/change/RefPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RefPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.index.change.ChangeField;
public class RefPredicate extends ChangeIndexPredicate {
@@ -28,7 +28,7 @@ public class RefPredicate extends ChangeIndexPredicate {
if (change == null) {
return false;
}
- return getValue().equals(change.getDest().get());
+ return getValue().equals(change.getDest().branch());
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java b/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
index 46a17f6798..bbdfc663f0 100644
--- a/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.index.change.ChangeField;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
@@ -44,7 +44,7 @@ public class RegexProjectPredicate extends ChangeRegexPredicate {
return false;
}
- Project.NameKey p = change.getDest().getParentKey();
+ Project.NameKey p = change.getDest().project();
return pattern.run(p.get());
}
diff --git a/java/com/google/gerrit/server/query/change/RegexRefPredicate.java b/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
index af211e630a..6d404b4631 100644
--- a/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.index.change.ChangeField;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
@@ -42,7 +42,7 @@ public class RegexRefPredicate extends ChangeRegexPredicate {
if (change == null) {
return false;
}
- return pattern.run(change.getDest().get());
+ return pattern.run(change.getDest().branch());
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java b/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
index 0441afa4b5..7713f24223 100644
--- a/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.query.change;
import static com.google.gerrit.server.index.change.ChangeField.EXACT_TOPIC;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
diff --git a/java/com/google/gerrit/server/query/change/ReviewerPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
index 19104d31ef..d783f76aa5 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.query.change;
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
diff --git a/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
index 542a357804..e5c6647803 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.query.change;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.query.PostFilterPredicate;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
diff --git a/java/com/google/gerrit/server/query/change/SingleGroupUser.java b/java/com/google/gerrit/server/query/change/SingleGroupUser.java
index a49e8c5642..c451d46d29 100644
--- a/java/com/google/gerrit/server/query/change/SingleGroupUser.java
+++ b/java/com/google/gerrit/server/query/change/SingleGroupUser.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.query.change;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.account.ListGroupMembership;
diff --git a/java/com/google/gerrit/server/query/change/StarPredicate.java b/java/com/google/gerrit/server/query/change/StarPredicate.java
index 6c5fd78798..788f1a32f6 100644
--- a/java/com/google/gerrit/server/query/change/StarPredicate.java
+++ b/java/com/google/gerrit/server/query/change/StarPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.index.change.ChangeField;
diff --git a/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java b/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
index 0995a590f6..093447e095 100644
--- a/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.index.change.ChangeField;
public class SubmissionIdPredicate extends ChangeIndexPredicate {
diff --git a/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java b/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
index e59ae436bf..bc65422f27 100644
--- a/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
+++ b/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.query.change;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.change.ChangeField;
import java.util.Set;
diff --git a/java/com/google/gerrit/server/query/group/GroupPredicates.java b/java/com/google/gerrit/server/query/group/GroupPredicates.java
index d02f6a46fa..17a7000348 100644
--- a/java/com/google/gerrit/server/query/group/GroupPredicates.java
+++ b/java/com/google/gerrit/server/query/group/GroupPredicates.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.query.group;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.query.IndexPredicate;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.index.group.GroupField;
import java.util.Locale;
diff --git a/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java b/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
index 215e36f6d0..4e60db5dbe 100644
--- a/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
@@ -20,13 +20,13 @@ import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.query.LimitPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.QueryRequiresAuthException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
import com.google.gerrit.server.account.GroupBackend;
@@ -75,7 +75,7 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup, GroupQueryBui
@Operator
public Predicate<InternalGroup> uuid(String uuid) {
- return GroupPredicates.uuid(new AccountGroup.UUID(uuid));
+ return GroupPredicates.uuid(AccountGroup.uuid(uuid));
}
@Operator
@@ -169,7 +169,7 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup, GroupQueryBui
}
private AccountGroup.UUID parseGroup(String groupNameOrUuid) throws QueryParseException {
- Optional<InternalGroup> group = args.groupCache.get(new AccountGroup.UUID(groupNameOrUuid));
+ Optional<InternalGroup> group = args.groupCache.get(AccountGroup.uuid(groupNameOrUuid));
if (group.isPresent()) {
return group.get().getGroupUUID();
}
diff --git a/java/com/google/gerrit/server/query/group/InternalGroupQuery.java b/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
index 57498099b2..57328736b0 100644
--- a/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
+++ b/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
@@ -19,11 +19,11 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.query.InternalQuery;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/query/project/ProjectPredicates.java b/java/com/google/gerrit/server/query/project/ProjectPredicates.java
index 5f13236fd3..4e56fac76d 100644
--- a/java/com/google/gerrit/server/query/project/ProjectPredicates.java
+++ b/java/com/google/gerrit/server/query/project/ProjectPredicates.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.query.project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectField;
import com.google.gerrit.index.project.ProjectPredicate;
import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.reviewdb.client.Project;
import java.util.Locale;
public class ProjectPredicates {
diff --git a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
index 8b4cb538ae..d23454644e 100644
--- a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
@@ -17,13 +17,13 @@ package com.google.gerrit.server.query.project;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.query.LimitPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import java.util.List;
@@ -41,12 +41,12 @@ public class ProjectQueryBuilder extends QueryBuilder<ProjectData, ProjectQueryB
@Operator
public Predicate<ProjectData> name(String name) {
- return ProjectPredicates.name(new Project.NameKey(name));
+ return ProjectPredicates.name(Project.nameKey(name));
}
@Operator
public Predicate<ProjectData> parent(String parentName) {
- return ProjectPredicates.parent(new Project.NameKey(parentName));
+ return ProjectPredicates.parent(Project.nameKey(parentName));
}
@Operator
diff --git a/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java b/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
index d39e55ca80..c6599759ed 100644
--- a/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
+++ b/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
@@ -18,9 +18,9 @@ import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.plugincontext.PluginSetEntryContext;
diff --git a/java/com/google/gerrit/server/quota/QuotaBackend.java b/java/com/google/gerrit/server/quota/QuotaBackend.java
index 11ce61f74e..694f4f1070 100644
--- a/java/com/google/gerrit/server/quota/QuotaBackend.java
+++ b/java/com/google/gerrit/server/quota/QuotaBackend.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.quota;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.ImplementedBy;
diff --git a/java/com/google/gerrit/server/quota/QuotaRequestContext.java b/java/com/google/gerrit/server/quota/QuotaRequestContext.java
index 90b501c39f..bfa7a35419 100644
--- a/java/com/google/gerrit/server/quota/QuotaRequestContext.java
+++ b/java/com/google/gerrit/server/quota/QuotaRequestContext.java
@@ -15,9 +15,9 @@
package com.google.gerrit.server.quota;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import java.util.Optional;
diff --git a/java/com/google/gerrit/server/restapi/BUILD b/java/com/google/gerrit/server/restapi/BUILD
index dfb95940ff..fd341e9dfd 100644
--- a/java/com/google/gerrit/server/restapi/BUILD
+++ b/java/com/google/gerrit/server/restapi/BUILD
@@ -10,8 +10,10 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
+ "//java/com/google/gerrit/git",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
"//java/com/google/gerrit/index/project",
@@ -20,7 +22,6 @@ java_library(
"//java/com/google/gerrit/mail",
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/prettify:server",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/ioutil",
"//java/com/google/gerrit/server/util/time",
@@ -29,6 +30,7 @@ java_library(
"//lib:blame-cache",
"//lib:gson",
"//lib:guava",
+ "//lib:jgit",
"//lib:servlet-api",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
@@ -38,6 +40,5 @@ java_library(
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/server/restapi/access/ListAccess.java b/java/com/google/gerrit/server/restapi/access/ListAccess.java
index a3e95301b4..2520821ad1 100644
--- a/java/com/google/gerrit/server/restapi/access/ListAccess.java
+++ b/java/com/google/gerrit/server/restapi/access/ListAccess.java
@@ -14,16 +14,13 @@
package com.google.gerrit.server.restapi.access;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.project.GetAccess;
import com.google.inject.Inject;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -47,13 +44,12 @@ public class ListAccess implements RestReadView<TopLevelResource> {
}
@Override
- public Map<String, ProjectAccessInfo> apply(TopLevelResource resource)
- throws ResourceNotFoundException, ResourceConflictException, IOException,
- PermissionBackendException {
+ public Response<Map<String, ProjectAccessInfo>> apply(TopLevelResource resource)
+ throws Exception {
Map<String, ProjectAccessInfo> access = new TreeMap<>();
for (String p : projects) {
- access.put(p, getAccess.apply(new Project.NameKey(p)));
+ access.put(p, getAccess.apply(Project.nameKey(p)));
}
- return access;
+ return Response.ok(access);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/AddSshKey.java b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
index 7c05f4e07f..1fcf0bd1cd 100644
--- a/java/com/google/gerrit/server/restapi/account/AddSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
@@ -104,7 +104,7 @@ public class AddSshKey
addKeyFactory.create(user, sshKey).send();
} catch (EmailException e) {
logger.atSevere().withCause(e).log(
- "Cannot send SSH key added message to %s", user.getAccount().getPreferredEmail());
+ "Cannot send SSH key added message to %s", user.getAccount().preferredEmail());
}
user.getUserName().ifPresent(sshKeyCache::evict);
diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
index bf50e10e76..c110194335 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -22,6 +22,8 @@ import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.InvalidSshKeyException;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -34,8 +36,6 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountExternalIdCreator;
import com.google.gerrit.server.account.AccountLoader;
@@ -119,7 +119,7 @@ public class CreateAccount
Set<AccountGroup.UUID> groups = parseGroups(input.groups);
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
List<ExternalId> extIds = new ArrayList<>();
if (input.email != null) {
diff --git a/java/com/google/gerrit/server/restapi/account/CreateEmail.java b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
index cc4cf21af7..ae45b68b02 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
@@ -97,11 +97,11 @@ public class CreateEmail
throw new MethodNotAllowedException("realm does not allow adding emails");
}
- return apply(rsrc.getUser(), id, input);
+ return Response.created(apply(rsrc.getUser(), id, input));
}
/** To be used from plugins that want to create emails without permission checks. */
- public Response<EmailInfo> apply(IdentifiedUser user, IdString id, EmailInput input)
+ public EmailInfo apply(IdentifiedUser user, IdString id, EmailInput input)
throws RestApiException, EmailException, MethodNotAllowedException, IOException,
ConfigInvalidException, PermissionBackendException {
String email = id.get().trim();
@@ -146,6 +146,6 @@ public class CreateEmail
throw e;
}
}
- return Response.created(info);
+ return info;
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
index 478830143e..a6fbb10e17 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
@@ -15,26 +15,27 @@
package com.google.gerrit.server.restapi.account;
import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentCommitId;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
import com.google.gerrit.extensions.api.accounts.DeletedDraftCommentInfo;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
@@ -107,7 +108,7 @@ public class DeleteDraftComments
}
@Override
- public ImmutableList<DeletedDraftCommentInfo> apply(
+ public Response<ImmutableList<DeletedDraftCommentInfo>> apply(
AccountResource rsrc, DeleteDraftCommentsInput input)
throws RestApiException, UpdateException {
CurrentUser user = userProvider.get();
@@ -147,7 +148,8 @@ public class DeleteDraftComments
// allowing partial failure would have little value.
BatchUpdate.execute(updates.values(), BatchUpdateListener.NONE, false);
- return ops.stream().map(Op::getResult).filter(Objects::nonNull).collect(toImmutableList());
+ return Response.ok(
+ ops.stream().map(Op::getResult).filter(Objects::nonNull).collect(toImmutableList()));
}
private Predicate<ChangeData> predicate(Account.Id accountId, DeleteDraftCommentsInput input)
@@ -180,8 +182,8 @@ public class DeleteDraftComments
boolean dirty = false;
for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getNotes(), accountId)) {
dirty = true;
- PatchSet.Id psId = new PatchSet.Id(ctx.getChange().getId(), c.key.patchSetId);
- setCommentRevId(c, patchListCache, ctx.getChange(), psUtil.get(ctx.getNotes(), psId));
+ PatchSet.Id psId = PatchSet.id(ctx.getChange().getId(), c.key.patchSetId);
+ setCommentCommitId(c, patchListCache, ctx.getChange(), psUtil.get(ctx.getNotes(), psId));
commentsUtil.deleteComments(ctx.getUpdate(psId), Collections.singleton(c));
comments.add(commentFormatter.format(c));
}
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
index 72a67db858..5cdf4ae9b6 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.account;
import static java.util.stream.Collectors.toSet;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.extensions.common.Input;
@@ -25,7 +26,6 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountException;
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
index 054f4bc984..b470be8e75 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
@@ -74,7 +74,7 @@ public class DeleteSshKey implements RestModifyView<AccountResource.SshKey, Inpu
deleteKeySenderFactory.create(user, rsrc.getSshKey()).send();
} catch (EmailException e) {
logger.atSevere().withCause(e).log(
- "Cannot send SSH key deletion message to %s", user.getAccount().getPreferredEmail());
+ "Cannot send SSH key deletion message to %s", user.getAccount().preferredEmail());
}
user.getUserName().ifPresent(sshKeyCache::evict);
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
index 798aad17b3..9f38b97dcc 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
@@ -16,13 +16,13 @@ package com.google.gerrit.server.restapi.account;
import static java.util.stream.Collectors.toList;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountResource;
@@ -77,7 +77,7 @@ public class DeleteWatchedProjects
u.deleteProjectWatches(
input.stream()
.filter(Objects::nonNull)
- .map(w -> ProjectWatchKey.create(new Project.NameKey(w.project), w.filter))
+ .map(w -> ProjectWatchKey.create(Project.nameKey(w.project), w.filter))
.collect(toList())));
return Response.none();
}
diff --git a/java/com/google/gerrit/server/restapi/account/EmailsCollection.java b/java/com/google/gerrit/server/restapi/account/EmailsCollection.java
index 434b9d6174..7a498f46ac 100644
--- a/java/com/google/gerrit/server/restapi/account/EmailsCollection.java
+++ b/java/com/google/gerrit/server/restapi/account/EmailsCollection.java
@@ -63,7 +63,7 @@ public class EmailsCollection implements ChildCollection<AccountResource, Accoun
}
if ("preferred".equals(id.get())) {
- String email = rsrc.getUser().getAccount().getPreferredEmail();
+ String email = rsrc.getUser().getAccount().preferredEmail();
if (Strings.isNullOrEmpty(email)) {
throw new ResourceNotFoundException(id);
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetAccount.java b/java/com/google/gerrit/server/restapi/account/GetAccount.java
index 6544f8dced..898b0bbfbc 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAccount.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.AccountResource;
@@ -32,10 +33,10 @@ public class GetAccount implements RestReadView<AccountResource> {
}
@Override
- public AccountInfo apply(AccountResource rsrc) throws PermissionBackendException {
+ public Response<AccountInfo> apply(AccountResource rsrc) throws PermissionBackendException {
AccountLoader loader = infoFactory.create(true);
AccountInfo info = loader.get(rsrc.getUser().getAccountId());
loader.fill();
- return info;
+ return Response.ok(info);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetAgreements.java b/java/com/google/gerrit/server/restapi/account/GetAgreements.java
index edcbc3560f..5feca6696c 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAgreements.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAgreements.java
@@ -18,12 +18,13 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.common.AgreementInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
@@ -61,7 +62,7 @@ public class GetAgreements implements RestReadView<AccountResource> {
}
@Override
- public List<AgreementInfo> apply(AccountResource resource)
+ public Response<List<AgreementInfo>> apply(AccountResource resource)
throws RestApiException, PermissionBackendException {
if (!agreementsEnabled) {
throw new MethodNotAllowedException("contributor agreements disabled");
@@ -97,6 +98,6 @@ public class GetAgreements implements RestReadView<AccountResource> {
results.add(agreementJson.format(ca));
}
}
- return results;
+ return Response.ok(results);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java b/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
index 904b15fc68..e97e0a0e7e 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAvatarChangeUrl.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.avatar.AvatarProvider;
@@ -33,7 +34,7 @@ public class GetAvatarChangeUrl implements RestReadView<AccountResource> {
}
@Override
- public String apply(AccountResource rsrc) throws ResourceNotFoundException {
+ public Response<String> apply(AccountResource rsrc) throws ResourceNotFoundException {
AvatarProvider impl = avatarProvider.get();
if (impl == null) {
throw new ResourceNotFoundException();
@@ -43,6 +44,6 @@ public class GetAvatarChangeUrl implements RestReadView<AccountResource> {
if (Strings.isNullOrEmpty(url)) {
throw new ResourceNotFoundException();
}
- return url;
+ return Response.ok(url);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
index 77f166899b..fa9ab18bd2 100644
--- a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
@@ -28,9 +28,9 @@ import com.google.gerrit.extensions.config.CapabilityDefinition;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.OptionUtil;
import com.google.gerrit.server.account.AccountLimits;
@@ -40,7 +40,6 @@ import com.google.gerrit.server.git.QueueProvider;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -79,7 +78,7 @@ public class GetCapabilities implements RestReadView<AccountResource> {
}
@Override
- public Object apply(AccountResource resource)
+ public Response<Map<String, Object>> apply(AccountResource resource)
throws RestApiException, PermissionBackendException {
permissionBackend.checkUsesDefaultCapabilities();
PermissionBackend.WithUser perm = permissionBackend.currentUser();
@@ -97,9 +96,7 @@ public class GetCapabilities implements RestReadView<AccountResource> {
addRanges(have, limits);
addPriority(have, limits);
- return OutputFormat.JSON
- .newGson()
- .toJsonTree(have, new TypeToken<Map<String, Object>>() {}.getType());
+ return Response.ok(have);
}
private Set<GlobalOrPluginPermission> permissionsToTest() {
@@ -172,9 +169,9 @@ public class GetCapabilities implements RestReadView<AccountResource> {
}
@Override
- public BinaryResult apply(Capability resource) throws ResourceNotFoundException {
+ public Response<BinaryResult> apply(Capability resource) throws ResourceNotFoundException {
permissionBackend.checkUsesDefaultCapabilities();
- return BinaryResult.create("ok\n");
+ return Response.ok(BinaryResult.create("ok\n"));
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetDetail.java b/java/com/google/gerrit/server/restapi/account/GetDetail.java
index 72044c40d1..b19559e3e3 100644
--- a/java/com/google/gerrit/server/restapi/account/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDetail.java
@@ -14,9 +14,10 @@
package com.google.gerrit.server.restapi.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.AccountDetailInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountDirectory.FillOptions;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.InternalAccountDirectory;
@@ -36,12 +37,12 @@ public class GetDetail implements RestReadView<AccountResource> {
}
@Override
- public AccountDetailInfo apply(AccountResource rsrc) throws PermissionBackendException {
+ public Response<AccountDetailInfo> apply(AccountResource rsrc) throws PermissionBackendException {
Account a = rsrc.getUser().getAccount();
- AccountDetailInfo info = new AccountDetailInfo(a.getId().get());
- info.registeredOn = a.getRegisteredOn();
+ AccountDetailInfo info = new AccountDetailInfo(a.id().get());
+ info.registeredOn = a.registeredOn();
info.inactive = !a.isActive() ? true : null;
directory.fillAccountInfo(Collections.singleton(info), EnumSet.allOf(FillOptions.class));
- return info;
+ return Response.ok(info);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
index 40201a879a..670ef3b83d 100644
--- a/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
@@ -14,12 +14,13 @@
package com.google.gerrit.server.restapi.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountResource;
@@ -48,16 +49,17 @@ public class GetDiffPreferences implements RestReadView<AccountResource> {
}
@Override
- public DiffPreferencesInfo apply(AccountResource rsrc)
+ public Response<DiffPreferencesInfo> apply(AccountResource rsrc)
throws RestApiException, ConfigInvalidException, IOException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
Account.Id id = rsrc.getUser().getAccountId();
- return accountCache
- .get(id)
- .map(AccountState::getDiffPreferences)
- .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+ return Response.ok(
+ accountCache
+ .get(id)
+ .map(AccountState::diffPreferences)
+ .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
index 0ecd6eaddd..409209ee41 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
@@ -14,12 +14,13 @@
package com.google.gerrit.server.restapi.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountResource;
@@ -48,16 +49,17 @@ public class GetEditPreferences implements RestReadView<AccountResource> {
}
@Override
- public EditPreferencesInfo apply(AccountResource rsrc)
+ public Response<EditPreferencesInfo> apply(AccountResource rsrc)
throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
Account.Id id = rsrc.getUser().getAccountId();
- return accountCache
- .get(id)
- .map(AccountState::getEditPreferences)
- .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+ return Response.ok(
+ accountCache
+ .get(id)
+ .map(AccountState::editPreferences)
+ .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetEmail.java b/java/com/google/gerrit/server/restapi/account/GetEmail.java
index 31183805a3..afcdac2e50 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmail.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.gerrit.extensions.common.EmailInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountResource;
import com.google.inject.Inject;
@@ -26,10 +27,10 @@ public class GetEmail implements RestReadView<AccountResource.Email> {
public GetEmail() {}
@Override
- public EmailInfo apply(AccountResource.Email rsrc) {
+ public Response<EmailInfo> apply(AccountResource.Email rsrc) {
EmailInfo e = new EmailInfo();
e.email = rsrc.getEmail();
- e.preferred(rsrc.getUser().getAccount().getPreferredEmail());
- return e;
+ e.preferred(rsrc.getUser().getAccount().preferredEmail());
+ return Response.ok(e);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetEmails.java b/java/com/google/gerrit/server/restapi/account/GetEmails.java
index 8c21536ed3..9db9f05cc2 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEmails.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmails.java
@@ -19,6 +19,7 @@ import static java.util.stream.Collectors.toList;
import com.google.gerrit.extensions.common.EmailInfo;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountResource;
@@ -43,22 +44,23 @@ public class GetEmails implements RestReadView<AccountResource> {
}
@Override
- public List<EmailInfo> apply(AccountResource rsrc)
+ public Response<List<EmailInfo>> apply(AccountResource rsrc)
throws AuthException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
- return rsrc.getUser().getEmailAddresses().stream()
- .filter(Objects::nonNull)
- .map(e -> toEmailInfo(rsrc, e))
- .sorted(comparing((EmailInfo e) -> e.email))
- .collect(toList());
+ return Response.ok(
+ rsrc.getUser().getEmailAddresses().stream()
+ .filter(Objects::nonNull)
+ .map(e -> toEmailInfo(rsrc, e))
+ .sorted(comparing((EmailInfo e) -> e.email))
+ .collect(toList()));
}
private static EmailInfo toEmailInfo(AccountResource rsrc, String email) {
EmailInfo e = new EmailInfo();
e.email = email;
- e.preferred(rsrc.getUser().getAccount().getPreferredEmail());
+ e.preferred(rsrc.getUser().getAccount().preferredEmail());
return e;
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
index ef448dc7cc..0e52af239c 100644
--- a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
@@ -19,6 +19,7 @@ import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USE
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.common.AccountExternalIdInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
@@ -58,7 +59,7 @@ public class GetExternalIds implements RestReadView<AccountResource> {
}
@Override
- public List<AccountExternalIdInfo> apply(AccountResource resource)
+ public Response<List<AccountExternalIdInfo>> apply(AccountResource resource)
throws RestApiException, IOException, PermissionBackendException {
if (!self.get().hasSameAccountId(resource.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
@@ -66,7 +67,7 @@ public class GetExternalIds implements RestReadView<AccountResource> {
Collection<ExternalId> ids = externalIds.byAccount(resource.getUser().getAccountId());
if (ids.isEmpty()) {
- return ImmutableList.of();
+ return Response.ok(ImmutableList.of());
}
List<AccountExternalIdInfo> result = Lists.newArrayListWithCapacity(ids.size());
for (ExternalId id : ids) {
@@ -83,7 +84,7 @@ public class GetExternalIds implements RestReadView<AccountResource> {
}
result.add(info);
}
- return result;
+ return Response.ok(result);
}
private static Boolean toBoolean(boolean v) {
diff --git a/java/com/google/gerrit/server/restapi/account/GetGroups.java b/java/com/google/gerrit/server/restapi/account/GetGroups.java
index 569ff76a4c..22492a751f 100644
--- a/java/com/google/gerrit/server/restapi/account/GetGroups.java
+++ b/java/com/google/gerrit/server/restapi/account/GetGroups.java
@@ -14,11 +14,12 @@
package com.google.gerrit.server.restapi.account;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.GroupControl;
@@ -42,7 +43,8 @@ public class GetGroups implements RestReadView<AccountResource> {
}
@Override
- public List<GroupInfo> apply(AccountResource resource) throws PermissionBackendException {
+ public Response<List<GroupInfo>> apply(AccountResource resource)
+ throws PermissionBackendException {
IdentifiedUser user = resource.getUser();
Account.Id userId = user.getAccountId();
Set<AccountGroup.UUID> knownGroups = user.getEffectiveGroups().getKnownGroups();
@@ -58,6 +60,6 @@ public class GetGroups implements RestReadView<AccountResource> {
visibleGroups.add(json.format(ctl.getGroup()));
}
}
- return visibleGroups;
+ return Response.ok(visibleGroups);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetName.java b/java/com/google/gerrit/server/restapi/account/GetName.java
index bdf379e52c..ca33887eb0 100644
--- a/java/com/google/gerrit/server/restapi/account/GetName.java
+++ b/java/com/google/gerrit/server/restapi/account/GetName.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountResource;
import com.google.inject.Singleton;
@@ -22,7 +23,7 @@ import com.google.inject.Singleton;
@Singleton
public class GetName implements RestReadView<AccountResource> {
@Override
- public String apply(AccountResource rsrc) {
- return Strings.nullToEmpty(rsrc.getUser().getAccount().getFullName());
+ public Response<String> apply(AccountResource rsrc) {
+ return Response.ok(Strings.nullToEmpty(rsrc.getUser().getAccount().fullName()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java b/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
index 395c1599db..24682c02c3 100644
--- a/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
+++ b/java/com/google/gerrit/server/restapi/account/GetOAuthToken.java
@@ -18,6 +18,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.auth.oauth.OAuthToken;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountResource;
@@ -50,7 +51,7 @@ public class GetOAuthToken implements RestReadView<AccountResource> {
}
@Override
- public OAuthTokenInfo apply(AccountResource rsrc)
+ public Response<OAuthTokenInfo> apply(AccountResource rsrc)
throws AuthException, ResourceNotFoundException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
throw new AuthException("not allowed to get access token");
@@ -66,7 +67,7 @@ public class GetOAuthToken implements RestReadView<AccountResource> {
accessTokenInfo.providerId = accessToken.getProviderId();
accessTokenInfo.expiresAt = Long.toString(accessToken.getExpiresAt());
accessTokenInfo.type = BEARER_TYPE;
- return accessTokenInfo;
+ return Response.ok(accessTokenInfo);
}
private static String getHostName(String canonicalWebUrl) {
diff --git a/java/com/google/gerrit/server/restapi/account/GetPreferences.java b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
index 3d2064232f..d4d73c5107 100644
--- a/java/com/google/gerrit/server/restapi/account/GetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
@@ -14,15 +14,16 @@
package com.google.gerrit.server.restapi.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.config.DownloadScheme;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountResource;
@@ -54,7 +55,7 @@ public class GetPreferences implements RestReadView<AccountResource> {
}
@Override
- public GeneralPreferencesInfo apply(AccountResource rsrc)
+ public Response<GeneralPreferencesInfo> apply(AccountResource rsrc)
throws RestApiException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
@@ -64,9 +65,9 @@ public class GetPreferences implements RestReadView<AccountResource> {
GeneralPreferencesInfo preferencesInfo =
accountCache
.get(id)
- .map(AccountState::getGeneralPreferences)
+ .map(AccountState::generalPreferences)
.orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
- return unsetDownloadSchemeIfUnsupported(preferencesInfo);
+ return Response.ok(unsetDownloadSchemeIfUnsupported(preferencesInfo));
}
private GeneralPreferencesInfo unsetDownloadSchemeIfUnsupported(
diff --git a/java/com/google/gerrit/server/restapi/account/GetSshKey.java b/java/com/google/gerrit/server/restapi/account/GetSshKey.java
index dc726630c2..58b5d12c14 100644
--- a/java/com/google/gerrit/server/restapi/account/GetSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKey.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.gerrit.extensions.common.SshKeyInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountResource.SshKey;
@@ -24,7 +25,7 @@ import com.google.inject.Singleton;
public class GetSshKey implements RestReadView<AccountResource.SshKey> {
@Override
- public SshKeyInfo apply(SshKey rsrc) {
- return GetSshKeys.newSshKeyInfo(rsrc.getSshKey());
+ public Response<SshKeyInfo> apply(SshKey rsrc) {
+ return Response.ok(GetSshKeys.newSshKeyInfo(rsrc.getSshKey()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
index 408aa5fd48..0ca9b9e4cf 100644
--- a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
@@ -18,6 +18,7 @@ import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.common.SshKeyInfo;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -53,13 +54,13 @@ public class GetSshKeys implements RestReadView<AccountResource> {
}
@Override
- public List<SshKeyInfo> apply(AccountResource rsrc)
+ public Response<List<SshKeyInfo>> apply(AccountResource rsrc)
throws AuthException, RepositoryNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
- return apply(rsrc.getUser());
+ return Response.ok(apply(rsrc.getUser()));
}
public List<SshKeyInfo> apply(IdentifiedUser user)
diff --git a/java/com/google/gerrit/server/restapi/account/GetStatus.java b/java/com/google/gerrit/server/restapi/account/GetStatus.java
index bc7094f9ea..447ad76f9f 100644
--- a/java/com/google/gerrit/server/restapi/account/GetStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/GetStatus.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountResource;
import com.google.inject.Singleton;
@@ -22,7 +23,7 @@ import com.google.inject.Singleton;
@Singleton
public class GetStatus implements RestReadView<AccountResource> {
@Override
- public String apply(AccountResource rsrc) {
- return Strings.nullToEmpty(rsrc.getUser().getAccount().getStatus());
+ public Response<String> apply(AccountResource rsrc) {
+ return Response.ok(Strings.nullToEmpty(rsrc.getUser().getAccount().status()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetUsername.java b/java/com/google/gerrit/server/restapi/account/GetUsername.java
index 01185c3f71..7e58f945cf 100644
--- a/java/com/google/gerrit/server/restapi/account/GetUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/GetUsername.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.account;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountResource;
import com.google.inject.Inject;
@@ -27,7 +28,8 @@ public class GetUsername implements RestReadView<AccountResource> {
public GetUsername() {}
@Override
- public String apply(AccountResource rsrc) throws AuthException, ResourceNotFoundException {
- return rsrc.getUser().getUserName().orElseThrow(ResourceNotFoundException::new);
+ public Response<String> apply(AccountResource rsrc)
+ throws AuthException, ResourceNotFoundException {
+ return Response.ok(rsrc.getUser().getUserName().orElseThrow(ResourceNotFoundException::new));
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index fce324e481..353e3f6457 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -19,11 +19,12 @@ import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountState;
@@ -55,7 +56,7 @@ public class GetWatchedProjects implements RestReadView<AccountResource> {
}
@Override
- public List<ProjectWatchInfo> apply(AccountResource rsrc)
+ public Response<List<ProjectWatchInfo>> apply(AccountResource rsrc)
throws AuthException, IOException, ConfigInvalidException, PermissionBackendException,
ResourceNotFoundException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
@@ -64,12 +65,13 @@ public class GetWatchedProjects implements RestReadView<AccountResource> {
Account.Id accountId = rsrc.getUser().getAccountId();
AccountState account = accounts.get(accountId).orElseThrow(ResourceNotFoundException::new);
- return account.getProjectWatches().entrySet().stream()
- .map(e -> toProjectWatchInfo(e.getKey(), e.getValue()))
- .sorted(
- comparing((ProjectWatchInfo pwi) -> pwi.project)
- .thenComparing(pwi -> Strings.nullToEmpty(pwi.filter)))
- .collect(toList());
+ return Response.ok(
+ account.projectWatches().entrySet().stream()
+ .map(e -> toProjectWatchInfo(e.getKey(), e.getValue()))
+ .sorted(
+ comparing((ProjectWatchInfo pwi) -> pwi.project)
+ .thenComparing(pwi -> Strings.nullToEmpty(pwi.filter)))
+ .collect(toList()));
}
private static ProjectWatchInfo toProjectWatchInfo(
diff --git a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
index 14bd492d99..52361748d0 100644
--- a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.account;
import com.google.gerrit.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.IdentifiedUser;
@@ -64,7 +65,7 @@ public class PostWatchedProjects
}
@Override
- public List<ProjectWatchInfo> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
+ public Response<List<ProjectWatchInfo>> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
diff --git a/java/com/google/gerrit/server/restapi/account/PutAgreement.java b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
index 147eec8554..b4b3314a10 100644
--- a/java/com/google/gerrit/server/restapi/account/PutAgreement.java
+++ b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.ContributorAgreement;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.api.accounts.AgreementInput;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -27,7 +28,6 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountState;
@@ -93,7 +93,7 @@ public class PutAgreement implements RestModifyView<AccountResource, AgreementIn
AccountState accountState = self.get().state();
try {
- addMembers.addMembers(uuid, ImmutableSet.of(accountState.getAccount().getId()));
+ addMembers.addMembers(uuid, ImmutableSet.of(accountState.account().id()));
} catch (NoSuchGroupException e) {
throw new ResourceConflictException("autoverify group not found", e);
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
index f029ef94b1..3e7753fb32 100644
--- a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
+++ b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
@@ -58,7 +58,7 @@ public class PutHttpPassword implements RestModifyView<AccountResource, HttpPass
try {
rng = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("Cannot create RNG for password generator", e);
+ throw new IllegalStateException("Cannot create RNG for password generator", e);
}
}
@@ -133,7 +133,7 @@ public class PutHttpPassword implements RestModifyView<AccountResource, HttpPass
.send();
} catch (EmailException e) {
logger.atSevere().withCause(e).log(
- "Cannot send HttpPassword update message to %s", user.getAccount().getPreferredEmail());
+ "Cannot send HttpPassword update message to %s", user.getAccount().preferredEmail());
}
return Strings.isNullOrEmpty(newPassword) ? Response.none() : Response.ok(newPassword);
diff --git a/java/com/google/gerrit/server/restapi/account/PutName.java b/java/com/google/gerrit/server/restapi/account/PutName.java
index 9f9dd073ec..0389014666 100644
--- a/java/com/google/gerrit/server/restapi/account/PutName.java
+++ b/java/com/google/gerrit/server/restapi/account/PutName.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.common.NameInput;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -22,7 +23,6 @@ import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
@@ -91,8 +91,8 @@ public class PutName implements RestModifyView<AccountResource, NameInput> {
.get()
.update("Set Full Name via API", accountId, u -> u.setFullName(newName))
.orElseThrow(() -> new ResourceNotFoundException("account not found"));
- return Strings.isNullOrEmpty(accountState.getAccount().getFullName())
+ return Strings.isNullOrEmpty(accountState.account().fullName())
? Response.none()
- : Response.ok(accountState.getAccount().getFullName());
+ : Response.ok(accountState.account().fullName());
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutPreferred.java b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
index b8edec3f6e..2ddea2f05d 100644
--- a/java/com/google/gerrit/server/restapi/account/PutPreferred.java
+++ b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
@@ -85,13 +85,13 @@ public class PutPreferred implements RestModifyView<AccountResource.Email, Input
"Set Preferred Email via API",
user.getAccountId(),
(a, u) -> {
- if (preferredEmail.equals(a.getAccount().getPreferredEmail())) {
+ if (preferredEmail.equals(a.account().preferredEmail())) {
alreadyPreferred.set(true);
} else {
// check if the user has a matching email
String matchingEmail = null;
for (String email :
- a.getExternalIds().stream()
+ a.externalIds().stream()
.map(ExternalId::email)
.filter(Objects::nonNull)
.collect(toSet())) {
@@ -128,7 +128,7 @@ public class PutPreferred implements RestModifyView<AccountResource.Email, Input
}
// claim the email now
- u.addExternalId(ExternalId.createEmail(a.getAccount().getId(), preferredEmail));
+ u.addExternalId(ExternalId.createEmail(a.account().id(), preferredEmail));
matchingEmail = preferredEmail;
} else {
// Realm says that the email doesn't belong to the user. This can only happen as
diff --git a/java/com/google/gerrit/server/restapi/account/PutStatus.java b/java/com/google/gerrit/server/restapi/account/PutStatus.java
index 4f1128d5da..7e274897be 100644
--- a/java/com/google/gerrit/server/restapi/account/PutStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/PutStatus.java
@@ -73,8 +73,8 @@ public class PutStatus implements RestModifyView<AccountResource, StatusInput> {
.get()
.update("Set Status via API", user.getAccountId(), u -> u.setStatus(newStatus))
.orElseThrow(() -> new ResourceNotFoundException("account not found"));
- return Strings.isNullOrEmpty(accountState.getAccount().getStatus())
+ return Strings.isNullOrEmpty(accountState.account().status())
? Response.none()
- : Response.ok(accountState.getAccount().getStatus());
+ : Response.ok(accountState.account().status());
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
index 8d949b8dad..d5897e582b 100644
--- a/java/com/google/gerrit/server/restapi/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -17,15 +17,17 @@ package com.google.gerrit.server.restapi.account;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.api.accounts.UsernameInput;
import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountResource;
@@ -70,18 +72,12 @@ public class PutUsername implements RestModifyView<AccountResource, UsernameInpu
}
@Override
- public String apply(AccountResource rsrc, UsernameInput input)
- throws AuthException, MethodNotAllowedException, UnprocessableEntityException,
- ResourceConflictException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ public Response<String> apply(AccountResource rsrc, UsernameInput input)
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
- if (input == null) {
- input = new UsernameInput();
- }
-
Account.Id accountId = rsrc.getUser().getAccountId();
if (!externalIds.byAccount(accountId, SCHEME_USERNAME).isEmpty()) {
throw new MethodNotAllowedException("Username cannot be changed.");
@@ -92,8 +88,8 @@ public class PutUsername implements RestModifyView<AccountResource, UsernameInpu
throw new MethodNotAllowedException("realm does not allow editing username");
}
- if (Strings.isNullOrEmpty(input.username)) {
- return input.username;
+ if (input == null || Strings.isNullOrEmpty(input.username)) {
+ throw new BadRequestException("input required");
}
if (!ExternalId.isValidUsername(input.username)) {
@@ -112,7 +108,7 @@ public class PutUsername implements RestModifyView<AccountResource, UsernameInpu
// If we are using this identity, don't report the exception.
Optional<ExternalId> other = externalIds.get(key);
if (other.isPresent() && other.get().accountId().equals(accountId)) {
- return input.username;
+ return Response.ok(input.username);
}
// Otherwise, someone else has this identity.
@@ -120,6 +116,6 @@ public class PutUsername implements RestModifyView<AccountResource, UsernameInpu
}
sshKeyCache.evict(input.username);
- return input.username;
+ return Response.ok(input.username);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
index 522301d0a6..f9e753c3b9 100644
--- a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
+++ b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.ListAccountsOption;
import com.google.gerrit.extensions.client.ListOption;
import com.google.gerrit.extensions.common.AccountInfo;
@@ -23,13 +24,13 @@ import com.google.gerrit.extensions.common.AccountVisibility;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.QueryResult;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountDirectory.FillOptions;
import com.google.gerrit.server.account.AccountInfoComparator;
import com.google.gerrit.server.account.AccountLoader;
@@ -149,14 +150,14 @@ public class QueryAccounts implements RestReadView<TopLevelResource> {
}
@Override
- public List<AccountInfo> apply(TopLevelResource rsrc)
+ public Response<List<AccountInfo>> apply(TopLevelResource rsrc)
throws RestApiException, PermissionBackendException {
if (Strings.isNullOrEmpty(query)) {
throw new BadRequestException("missing query field");
}
if (suggest && (!suggestConfig || query.length() < suggestFrom)) {
- return Collections.emptyList();
+ return Response.ok(Collections.emptyList());
}
Set<FillOptions> fillOptions = EnumSet.of(FillOptions.ID);
@@ -216,7 +217,7 @@ public class QueryAccounts implements RestReadView<TopLevelResource> {
}
QueryResult<AccountState> result = queryProcessor.query(queryPred);
for (AccountState accountState : result.entities()) {
- Account.Id id = accountState.getAccount().getId();
+ Account.Id id = accountState.account().id();
matches.put(id, accountLoader.get(id));
}
@@ -227,10 +228,10 @@ public class QueryAccounts implements RestReadView<TopLevelResource> {
if (!sorted.isEmpty() && result.more()) {
sorted.get(sorted.size() - 1)._moreAccounts = true;
}
- return sorted;
+ return Response.ok(sorted);
} catch (QueryParseException e) {
if (suggest) {
- return ImmutableList.of();
+ return Response.ok(ImmutableList.of());
}
throw new BadRequestException(e.getMessage());
}
diff --git a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
index ee72ab7f2a..cf56965426 100644
--- a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
@@ -14,13 +14,14 @@
package com.google.gerrit.server.restapi.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountResource;
@@ -53,7 +54,7 @@ public class SetDiffPreferences implements RestModifyView<AccountResource, DiffP
}
@Override
- public DiffPreferencesInfo apply(AccountResource rsrc, DiffPreferencesInfo input)
+ public Response<DiffPreferencesInfo> apply(AccountResource rsrc, DiffPreferencesInfo input)
throws RestApiException, ConfigInvalidException, RepositoryNotFoundException, IOException,
PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
@@ -65,10 +66,11 @@ public class SetDiffPreferences implements RestModifyView<AccountResource, DiffP
}
Account.Id id = rsrc.getUser().getAccountId();
- return accountsUpdateProvider
- .get()
- .update("Set Diff Preferences via API", id, u -> u.setDiffPreferences(input))
- .map(AccountState::getDiffPreferences)
- .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+ return Response.ok(
+ accountsUpdateProvider
+ .get()
+ .update("Set Diff Preferences via API", id, u -> u.setDiffPreferences(input))
+ .map(AccountState::diffPreferences)
+ .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
index 27d32f2c34..085adaae45 100644
--- a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
@@ -14,13 +14,14 @@
package com.google.gerrit.server.restapi.account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountResource;
@@ -54,7 +55,7 @@ public class SetEditPreferences implements RestModifyView<AccountResource, EditP
}
@Override
- public EditPreferencesInfo apply(AccountResource rsrc, EditPreferencesInfo input)
+ public Response<EditPreferencesInfo> apply(AccountResource rsrc, EditPreferencesInfo input)
throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
@@ -66,10 +67,11 @@ public class SetEditPreferences implements RestModifyView<AccountResource, EditP
}
Account.Id id = rsrc.getUser().getAccountId();
- return accountsUpdateProvider
- .get()
- .update("Set Edit Preferences via API", id, u -> u.setEditPreferences(input))
- .map(AccountState::getEditPreferences)
- .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+ return Response.ok(
+ accountsUpdateProvider
+ .get()
+ .update("Set Edit Preferences via API", id, u -> u.setEditPreferences(input))
+ .map(AccountState::editPreferences)
+ .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
index c6623dbf7b..3f2211ef06 100644
--- a/java/com/google/gerrit/server/restapi/account/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.config.DownloadScheme;
import com.google.gerrit.extensions.registration.DynamicMap;
@@ -22,15 +23,15 @@ import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.AccountsUpdate;
-import com.google.gerrit.server.account.Preferences;
+import com.google.gerrit.server.account.StoredPreferences;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -60,21 +61,22 @@ public class SetPreferences implements RestModifyView<AccountResource, GeneralPr
}
@Override
- public GeneralPreferencesInfo apply(AccountResource rsrc, GeneralPreferencesInfo input)
+ public Response<GeneralPreferencesInfo> apply(AccountResource rsrc, GeneralPreferencesInfo input)
throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
checkDownloadScheme(input.downloadScheme);
- Preferences.validateMy(input.my);
+ StoredPreferences.validateMy(input.my);
Account.Id id = rsrc.getUser().getAccountId();
- return accountsUpdateProvider
- .get()
- .update("Set General Preferences via API", id, u -> u.setGeneralPreferences(input))
- .map(AccountState::getGeneralPreferences)
- .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
+ return Response.ok(
+ accountsUpdateProvider
+ .get()
+ .update("Set General Preferences via API", id, u -> u.setGeneralPreferences(input))
+ .map(AccountState::generalPreferences)
+ .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
}
private void checkDownloadScheme(String downloadScheme) throws BadRequestException {
diff --git a/java/com/google/gerrit/server/restapi/account/Stars.java b/java/com/google/gerrit/server/restapi/account/Stars.java
index c610adfd70..cdaa99dac7 100644
--- a/java/com/google/gerrit/server/restapi/account/Stars.java
+++ b/java/com/google/gerrit/server/restapi/account/Stars.java
@@ -21,6 +21,7 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestReadView;
@@ -97,14 +98,23 @@ public class Stars implements ChildCollection<AccountResource, AccountResource.S
@Override
@SuppressWarnings("unchecked")
- public List<ChangeInfo> apply(AccountResource rsrc)
- throws BadRequestException, AuthException, PermissionBackendException {
+ public Response<List<ChangeInfo>> apply(AccountResource rsrc) throws Exception {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
throw new AuthException("not allowed to list stars of another account");
}
+
+ // The type of the value in the response that is returned by QueryChanges depends on the
+ // number of queries that is provided as input. If a single query is provided as input the
+ // value type is {@code List<ChangeInfo>}, if multiple queries are provided as input the value
+ // type is {@code List<List<ChangeInfo>>) (one {@code List<ChangeInfo>} as result to each
+ // query). Since in this case we provide exactly one query ("has:stars") as input we know that
+ // the value always has the type {@code List<ChangeInfo>} and hence we can safely cast the
+ // value to this type.
QueryChanges query = changes.list();
query.addQuery("has:stars");
- return (List<ChangeInfo>) query.apply(TopLevelResource.INSTANCE);
+ Response<?> response = query.apply(TopLevelResource.INSTANCE);
+ List<ChangeInfo> value = (List<ChangeInfo>) response.value();
+ return Response.ok(value);
}
}
@@ -120,11 +130,12 @@ public class Stars implements ChildCollection<AccountResource, AccountResource.S
}
@Override
- public SortedSet<String> apply(AccountResource.Star rsrc) throws AuthException {
+ public Response<SortedSet<String>> apply(AccountResource.Star rsrc) throws AuthException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
throw new AuthException("not allowed to get stars of another account");
}
- return starredChangesUtil.getLabels(self.get().getAccountId(), rsrc.getChange().getId());
+ return Response.ok(
+ starredChangesUtil.getLabels(self.get().getAccountId(), rsrc.getChange().getId()));
}
}
@@ -140,18 +151,19 @@ public class Stars implements ChildCollection<AccountResource, AccountResource.S
}
@Override
- public Collection<String> apply(AccountResource.Star rsrc, StarsInput in)
+ public Response<Collection<String>> apply(AccountResource.Star rsrc, StarsInput in)
throws AuthException, BadRequestException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
throw new AuthException("not allowed to update stars of another account");
}
try {
- return starredChangesUtil.star(
- self.get().getAccountId(),
- rsrc.getChange().getProject(),
- rsrc.getChange().getId(),
- in.add,
- in.remove);
+ return Response.ok(
+ starredChangesUtil.star(
+ self.get().getAccountId(),
+ rsrc.getChange().getProject(),
+ rsrc.getChange().getId(),
+ in.add,
+ in.remove));
} catch (IllegalLabelException e) {
throw new BadRequestException(e.getMessage());
}
diff --git a/java/com/google/gerrit/server/restapi/change/Abandon.java b/java/com/google/gerrit/server/restapi/change/Abandon.java
index c3327e4439..df3b58ee0f 100644
--- a/java/com/google/gerrit/server/restapi/change/Abandon.java
+++ b/java/com/google/gerrit/server/restapi/change/Abandon.java
@@ -15,13 +15,14 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.AbandonInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountState;
@@ -67,7 +68,7 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
}
@Override
- protected ChangeInfo applyImpl(
+ protected Response<ChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AbandonInput input)
throws RestApiException, UpdateException, PermissionBackendException, IOException,
ConfigInvalidException {
@@ -84,7 +85,7 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
rsrc.getUser(),
input.message,
notifyResolver.resolve(notify, input.notifyDetails));
- return json.noOptions().format(change);
+ return Response.ok(json.noOptions().format(change));
}
private NotifyHandling defaultNotify(Change change) {
diff --git a/java/com/google/gerrit/server/restapi/change/ApplyFix.java b/java/com/google/gerrit/server/restapi/change/ApplyFix.java
index 085f9dee83..74c5bc2f57 100644
--- a/java/com/google/gerrit/server/restapi/change/ApplyFix.java
+++ b/java/com/google/gerrit/server/restapi/change/ApplyFix.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -21,8 +23,6 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.FixResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.edit.ChangeEdit;
@@ -39,7 +39,6 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.List;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@Singleton
@@ -73,12 +72,11 @@ public class ApplyFix implements RestModifyView<FixResource, Void> {
Project.NameKey project = revisionResource.getProject();
ProjectState projectState = projectCache.checkedGet(project);
PatchSet patchSet = revisionResource.getPatchSet();
- ObjectId patchSetCommitId = ObjectId.fromString(patchSet.getRevision().get());
try (Repository repository = gitRepositoryManager.openRepository(project)) {
List<TreeModification> treeModifications =
fixReplacementInterpreter.toTreeModifications(
- repository, projectState, patchSetCommitId, fixResource.getFixReplacements());
+ repository, projectState, patchSet.commitId(), fixResource.getFixReplacements());
ChangeEdit changeEdit =
changeEditModifier.combineWithModifiedPatchSetTree(
repository, revisionResource.getNotes(), patchSet, treeModifications);
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index f0689a8c68..9ef36ddb98 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -17,7 +17,12 @@ package com.google.gerrit.server.restapi.change;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.DiffWebLinkInfo;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.common.Input;
@@ -38,10 +43,6 @@ import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.change.ChangeEditResource;
import com.google.gerrit.server.change.ChangeResource;
@@ -64,7 +65,6 @@ import com.google.inject.Singleton;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -379,16 +379,14 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
public Response<BinaryResult> apply(ChangeEditResource rsrc) throws AuthException, IOException {
try {
if (Patch.COMMIT_MSG.equals(rsrc.getPath())) {
- return Response.ok(getMessage.apply(rsrc.getChangeResource()));
+ return getMessage.apply(rsrc.getChangeResource());
}
ChangeEdit edit = rsrc.getChangeEdit();
return Response.ok(
fileContentUtil.getContent(
projectCache.checkedGet(rsrc.getChangeResource().getProject()),
- base
- ? ObjectId.fromString(edit.getBasePatchSet().getRevision().get())
- : edit.getEditCommit(),
+ base ? edit.getBasePatchSet().commitId() : edit.getEditCommit(),
rsrc.getPath(),
null));
} catch (ResourceNotFoundException | BadRequestException e) {
@@ -407,22 +405,22 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
}
@Override
- public FileInfo apply(ChangeEditResource rsrc) {
+ public Response<FileInfo> apply(ChangeEditResource rsrc) {
FileInfo r = new FileInfo();
ChangeEdit edit = rsrc.getChangeEdit();
Change change = edit.getChange();
- List<DiffWebLinkInfo> links =
+ ImmutableList<DiffWebLinkInfo> links =
webLinks.getDiffLinks(
change.getProject().get(),
change.getChangeId(),
- edit.getBasePatchSet().getPatchSetId(),
- edit.getBasePatchSet().getRefName(),
+ edit.getBasePatchSet().number(),
+ edit.getBasePatchSet().refName(),
rsrc.getPath(),
0,
edit.getRefName(),
rsrc.getPath());
r.webLinks = links.isEmpty() ? null : links;
- return r;
+ return Response.ok(r);
}
public static class FileInfo {
@@ -446,7 +444,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
}
@Override
- public Response<?> apply(ChangeResource rsrc, Input input)
+ public Response<Object> apply(ChangeResource rsrc, Input input)
throws AuthException, IOException, BadRequestException, ResourceConflictException,
PermissionBackendException {
if (input == null || Strings.isNullOrEmpty(input.message)) {
@@ -481,7 +479,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
}
@Override
- public BinaryResult apply(ChangeResource rsrc)
+ public Response<BinaryResult> apply(ChangeResource rsrc)
throws AuthException, IOException, ResourceNotFoundException {
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
String msg;
@@ -489,18 +487,17 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
if (base) {
try (Repository repo = repoManager.openRepository(rsrc.getProject());
RevWalk rw = new RevWalk(repo)) {
- RevCommit commit =
- rw.parseCommit(
- ObjectId.fromString(edit.get().getBasePatchSet().getRevision().get()));
+ RevCommit commit = rw.parseCommit(edit.get().getBasePatchSet().commitId());
msg = commit.getFullMessage();
}
} else {
msg = edit.get().getEditCommit().getFullMessage();
}
- return BinaryResult.create(msg)
- .setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
- .base64();
+ return Response.ok(
+ BinaryResult.create(msg)
+ .setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
+ .base64());
}
throw new ResourceNotFoundException();
}
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
index 6ec4fdbc53..67b5870ba7 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
@@ -14,10 +14,11 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.IncludedIn;
@@ -37,8 +38,8 @@ public class ChangeIncludedIn implements RestReadView<ChangeResource> {
}
@Override
- public IncludedInInfo apply(ChangeResource rsrc) throws RestApiException, IOException {
+ public Response<IncludedInInfo> apply(ChangeResource rsrc) throws RestApiException, IOException {
PatchSet ps = psUtil.current(rsrc.getNotes());
- return includedIn.apply(rsrc.getProject(), ps.getRevision().get());
+ return Response.ok(includedIn.apply(rsrc.getProject(), ps.commitId().name()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
index 96c517fe25..fae91803a8 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
@@ -22,7 +22,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.change.ChangeMessageResource;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;
@@ -50,11 +49,10 @@ public class ChangeMessages implements ChildCollection<ChangeResource, ChangeMes
}
@Override
- public ChangeMessageResource parse(ChangeResource parent, IdString id)
- throws ResourceNotFoundException, PermissionBackendException {
+ public ChangeMessageResource parse(ChangeResource parent, IdString id) throws Exception {
String uuid = id.get();
- List<ChangeMessageInfo> changeMessages = listChangeMessages.apply(parent);
+ List<ChangeMessageInfo> changeMessages = listChangeMessages.apply(parent).value();
int index = -1;
for (int i = 0; i < changeMessages.size(); ++i) {
ChangeMessageInfo changeMessage = changeMessages.get(i);
diff --git a/java/com/google/gerrit/server/restapi/change/ChangesCollection.java b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
index 028eb52a5e..3b0321d5f1 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.IdString;
@@ -23,8 +25,6 @@ import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollection;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.change.ChangeResource;
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java
index 72781c3bea..1a89935757 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPick.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -17,14 +17,15 @@ package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.common.CherryPickChangeInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -75,14 +76,12 @@ public class CherryPick
}
@Override
- public CherryPickChangeInfo applyImpl(
+ public Response<CherryPickChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, RevisionResource rsrc, CherryPickInput input)
throws IOException, UpdateException, RestApiException, PermissionBackendException,
ConfigInvalidException, NoSuchProjectException {
input.parent = input.parent == null ? 1 : input.parent;
- if (input.message == null || input.message.trim().isEmpty()) {
- throw new BadRequestException("message must be non-empty");
- } else if (input.destination == null || input.destination.trim().isEmpty()) {
+ if (input.destination == null || input.destination.trim().isEmpty()) {
throw new BadRequestException("destination must be non-empty");
}
@@ -103,13 +102,13 @@ public class CherryPick
rsrc.getChange(),
rsrc.getPatchSet(),
input,
- new Branch.NameKey(rsrc.getProject(), refName));
+ BranchNameKey.create(rsrc.getProject(), refName));
CherryPickChangeInfo changeInfo =
json.noOptions()
.format(rsrc.getProject(), cherryPickResult.changeId(), CherryPickChangeInfo::new);
changeInfo.containsGitConflicts =
!cherryPickResult.filesWithGitConflicts().isEmpty() ? true : null;
- return changeInfo;
+ return Response.ok(changeInfo);
} catch (InvalidChangeOperationException e) {
throw new BadRequestException(e.getMessage());
} catch (IntegrationException | NoSuchChangeException e) {
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 132310d048..407c5606a1 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -21,6 +21,11 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -28,11 +33,6 @@ import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.GerritPersonIdent;
@@ -136,30 +136,116 @@ public class CherryPickChange {
this.notifyResolver = notifyResolver;
}
+ /**
+ * This function is used for cherry picking a change.
+ *
+ * @param batchUpdateFactory Used for applying changes to the database.
+ * @param change Change to cherry pick.
+ * @param patch The patch of that change.
+ * @param input Input object for different configurations of cherry pick.
+ * @param dest Destination branch for the cherry pick.
+ * @return Result object that describes the cherry pick.
+ * @throws IOException Unable to open repository or read from the database.
+ * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
+ * key exist in the branch.
+ * @throws IntegrationException Merge conflict or trees are identical after cherry pick.
+ * @throws UpdateException Problem updating the database using batchUpdateFactory.
+ * @throws RestApiException Error such as invalid SHA1
+ * @throws ConfigInvalidException Can't find account to notify.
+ * @throws NoSuchProjectException Can't find project state.
+ */
public Result cherryPick(
BatchUpdate.Factory batchUpdateFactory,
Change change,
PatchSet patch,
CherryPickInput input,
- Branch.NameKey dest)
+ BranchNameKey dest)
throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
RestApiException, ConfigInvalidException, NoSuchProjectException {
return cherryPick(
batchUpdateFactory,
change,
change.getProject(),
- ObjectId.fromString(patch.getRevision().get()),
+ patch.commitId(),
input,
- dest);
+ dest,
+ null,
+ null,
+ null);
}
+ /**
+ * This function is called directly to cherry pick a commit. Also, it is used to cherry pick a
+ * change as well as long as sourceChange is not null.
+ *
+ * @param batchUpdateFactory Used for applying changes to the database.
+ * @param sourceChange Change to cherry pick. Can be null, and then the function will only cherry
+ * pick a commit.
+ * @param project Project name
+ * @param sourceCommit Id of the commit to be cherry picked.
+ * @param input Input object for different configurations of cherry pick.
+ * @param dest Destination branch for the cherry pick.
+ * @return Result object that describes the cherry pick.
+ * @throws IOException Unable to open repository or read from the database.
+ * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
+ * key exist in the branch.
+ * @throws IntegrationException Merge conflict or trees are identical after cherry pick.
+ * @throws UpdateException Problem updating the database using batchUpdateFactory.
+ * @throws RestApiException Error such as invalid SHA1
+ * @throws ConfigInvalidException Can't find account to notify.
+ * @throws NoSuchProjectException Can't find project state.
+ */
public Result cherryPick(
BatchUpdate.Factory batchUpdateFactory,
@Nullable Change sourceChange,
Project.NameKey project,
ObjectId sourceCommit,
CherryPickInput input,
- Branch.NameKey dest)
+ BranchNameKey dest)
+ throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
+ RestApiException, ConfigInvalidException, NoSuchProjectException {
+ return cherryPick(
+ batchUpdateFactory, sourceChange, project, sourceCommit, input, dest, null, null, null);
+ }
+
+ /**
+ * This function can be called directly to cherry-pick a change (or commit if sourceChange is
+ * null) with a few other parameters that are especially useful for cherry-picking a commit that
+ * is the revert-of another change.
+ *
+ * @param batchUpdateFactory Used for applying changes to the database.
+ * @param sourceChange Change to cherry pick. Can be null, and then the function will only cherry
+ * pick a commit.
+ * @param project Project name
+ * @param sourceCommit Id of the commit to be cherry picked.
+ * @param input Input object for different configurations of cherry pick.
+ * @param dest Destination branch for the cherry pick.
+ * @param topic Topic name for the change created.
+ * @param revertedChange The id of the change that is reverted. This is used for the "revertOf"
+ * field to mark the created cherry pick change as "revertOf" the original change that was
+ * reverted.
+ * @param changeIdForNewChange The Change-Id that the new change that of the cherry pick will
+ * have.
+ * @return Result object that describes the cherry pick.
+ * @throws IOException Unable to open repository or read from the database.
+ * @throws InvalidChangeOperationException Parent or branch don't exist, or two changes with same
+ * key exist in the branch.
+ * @throws IntegrationException Merge conflict or trees are identical after cherry pick.
+ * @throws UpdateException Problem updating the database using batchUpdateFactory.
+ * @throws RestApiException Error such as invalid SHA1
+ * @throws ConfigInvalidException Can't find account to notify.
+ * @throws NoSuchProjectException Can't find project state.
+ */
+ public Result cherryPick(
+ BatchUpdate.Factory batchUpdateFactory,
+ @Nullable Change sourceChange,
+ Project.NameKey project,
+ ObjectId sourceCommit,
+ CherryPickInput input,
+ BranchNameKey dest,
+ @Nullable String topic,
+ @Nullable Change.Id revertedChange,
+ @Nullable ObjectId changeIdForNewChange)
throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
RestApiException, ConfigInvalidException, NoSuchProjectException {
@@ -171,10 +257,10 @@ public class CherryPickChange {
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
CodeReviewRevWalk revWalk = CodeReviewCommit.newRevWalk(reader)) {
- Ref destRef = git.getRefDatabase().exactRef(dest.get());
+ Ref destRef = git.getRefDatabase().exactRef(dest.branch());
if (destRef == null) {
throw new InvalidChangeOperationException(
- String.format("Branch %s does not exist.", dest.get()));
+ String.format("Branch %s does not exist.", dest.branch()));
}
RevCommit baseCommit = getBaseCommit(destRef, project.get(), revWalk, input.base);
@@ -189,16 +275,22 @@ public class CherryPickChange {
input.parent, commitToCherryPick.getParentCount()));
}
+ String message = Strings.nullToEmpty(input.message).trim();
+ message = message.isEmpty() ? commitToCherryPick.getFullMessage() : message;
+
Timestamp now = TimeUtil.nowTs();
PersonIdent committerIdent = identifiedUser.newCommitterIdent(now, serverTimeZone);
- final ObjectId generatedChangeId = CommitMessageUtil.generateChangeId();
- String commitMessage = ChangeIdUtil.insertId(input.message, generatedChangeId).trim() + '\n';
+ final ObjectId generatedChangeId =
+ changeIdForNewChange != null
+ ? changeIdForNewChange
+ : CommitMessageUtil.generateChangeId();
+ String commitMessage = ChangeIdUtil.insertId(message, generatedChangeId).trim() + '\n';
CodeReviewCommit cherryPickCommit;
- ProjectState projectState = projectCache.checkedGet(dest.getParentKey());
+ ProjectState projectState = projectCache.checkedGet(dest.project());
if (projectState == null) {
- throw new NoSuchProjectException(dest.getParentKey());
+ throw new NoSuchProjectException(dest.project());
}
try {
MergeUtil mergeUtil;
@@ -226,12 +318,12 @@ public class CherryPickChange {
final List<String> idList = cherryPickCommit.getFooterLines(FooterConstants.CHANGE_ID);
if (!idList.isEmpty()) {
final String idStr = idList.get(idList.size() - 1).trim();
- changeKey = new Change.Key(idStr);
+ changeKey = Change.key(idStr);
} else {
- changeKey = new Change.Key("I" + generatedChangeId.name());
+ changeKey = Change.key("I" + generatedChangeId.name());
}
- Branch.NameKey newDest = new Branch.NameKey(project, destRef.getName());
+ BranchNameKey newDest = BranchNameKey.create(project, destRef.getName());
List<ChangeData> destChanges =
queryProvider.get().setLimit(2).byBranchKey(newDest, changeKey);
if (destChanges.size() > 1) {
@@ -252,13 +344,22 @@ public class CherryPickChange {
} else {
// Change key not found on destination branch. We can create a new
// change.
- String newTopic = null;
- if (sourceChange != null && !Strings.isNullOrEmpty(sourceChange.getTopic())) {
- newTopic = sourceChange.getTopic() + "-" + newDest.getShortName();
+ String newTopic = topic;
+ if (topic == null
+ && sourceChange != null
+ && !Strings.isNullOrEmpty(sourceChange.getTopic())) {
+ newTopic = sourceChange.getTopic() + "-" + newDest.shortName();
}
changeId =
createNewChange(
- bu, cherryPickCommit, dest.get(), newTopic, sourceChange, sourceCommit, input);
+ bu,
+ cherryPickCommit,
+ dest.branch(),
+ newTopic,
+ sourceChange,
+ sourceCommit,
+ input,
+ revertedChange);
}
bu.execute();
return Result.create(changeId, cherryPickCommit.getFilesWithGitConflicts());
@@ -337,15 +438,20 @@ public class CherryPickChange {
String refName,
String topic,
@Nullable Change sourceChange,
- ObjectId sourceCommit,
- CherryPickInput input)
+ @Nullable ObjectId sourceCommit,
+ CherryPickInput input,
+ @Nullable Change.Id revertOf)
throws IOException {
- Change.Id changeId = new Change.Id(seq.nextChangeId());
+ Change.Id changeId = Change.id(seq.nextChangeId());
ChangeInserter ins = changeInserterFactory.create(changeId, cherryPickCommit, refName);
- Branch.NameKey sourceBranch = sourceChange == null ? null : sourceChange.getDest();
+ ins.setRevertOf(revertOf);
+ BranchNameKey sourceBranch = sourceChange == null ? null : sourceChange.getDest();
ins.setMessage(
- messageForDestinationChange(
- ins.getPatchSetId(), sourceBranch, sourceCommit, cherryPickCommit))
+ revertOf == null
+ ? messageForDestinationChange(
+ ins.getPatchSetId(), sourceBranch, sourceCommit, cherryPickCommit)
+ : "Uploaded patch set 1.") // For revert commits, the message should not include
+ // cherry-pick information.
.setTopic(topic)
.setWorkInProgress(
(sourceChange != null && sourceChange.isWorkInProgress())
@@ -373,12 +479,12 @@ public class CherryPickChange {
private String messageForDestinationChange(
PatchSet.Id patchSetId,
- Branch.NameKey sourceBranch,
+ BranchNameKey sourceBranch,
ObjectId sourceCommit,
CodeReviewCommit cherryPickCommit) {
StringBuilder stringBuilder = new StringBuilder("Patch Set ").append(patchSetId.get());
if (sourceBranch != null) {
- stringBuilder.append(": Cherry Picked from branch ").append(sourceBranch.getShortName());
+ stringBuilder.append(": Cherry Picked from branch ").append(sourceBranch.shortName());
} else {
stringBuilder.append(": Cherry Picked from commit ").append(sourceCommit.getName());
}
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
index f34f1787fd..a3c8a97cbc 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -15,14 +15,15 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.common.CherryPickChangeInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -42,7 +43,6 @@ import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.revwalk.RevCommit;
@Singleton
public class CherryPickCommit
@@ -70,13 +70,10 @@ public class CherryPickCommit
}
@Override
- public CherryPickChangeInfo applyImpl(
+ public Response<CherryPickChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, CommitResource rsrc, CherryPickInput input)
throws IOException, UpdateException, RestApiException, PermissionBackendException,
ConfigInvalidException, NoSuchProjectException {
- RevCommit commit = rsrc.getCommit();
- String message = Strings.nullToEmpty(input.message).trim();
- input.message = message.isEmpty() ? commit.getFullMessage() : message;
String destination = Strings.nullToEmpty(input.destination).trim();
input.parent = input.parent == null ? 1 : input.parent;
Project.NameKey projectName = rsrc.getProjectState().getNameKey();
@@ -100,15 +97,15 @@ public class CherryPickCommit
updateFactory,
null,
projectName,
- commit,
+ rsrc.getCommit(),
input,
- new Branch.NameKey(rsrc.getProjectState().getNameKey(), refName));
+ BranchNameKey.create(rsrc.getProjectState().getNameKey(), refName));
CherryPickChangeInfo changeInfo =
json.noOptions()
.format(projectName, cherryPickResult.changeId(), CherryPickChangeInfo::new);
changeInfo.containsGitConflicts =
!cherryPickResult.filesWithGitConflicts().isEmpty() ? true : null;
- return changeInfo;
+ return Response.ok(changeInfo);
} catch (InvalidChangeOperationException e) {
throw new BadRequestException(e.getMessage());
} catch (IntegrationException e) {
diff --git a/java/com/google/gerrit/server/restapi/change/CommentJson.java b/java/com/google/gerrit/server/restapi/change/CommentJson.java
index 7112bbfb44..03898b1c99 100644
--- a/java/com/google/gerrit/server/restapi/change/CommentJson.java
+++ b/java/com/google/gerrit/server/restapi/change/CommentJson.java
@@ -22,6 +22,10 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.FixReplacement;
+import com.google.gerrit.entities.FixSuggestion;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.extensions.client.Comment.Range;
import com.google.gerrit.extensions.client.Side;
import com.google.gerrit.extensions.common.CommentInfo;
@@ -29,10 +33,6 @@ import com.google.gerrit.extensions.common.FixReplacementInfo;
import com.google.gerrit.extensions.common.FixSuggestionInfo;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.FixReplacement;
-import com.google.gerrit.reviewdb.client.FixSuggestion;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/restapi/change/Comments.java b/java/com/google/gerrit/server/restapi/change/Comments.java
index d9a0d57201..078c2394e9 100644
--- a/java/com/google/gerrit/server/restapi/change/Comments.java
+++ b/java/com/google/gerrit/server/restapi/change/Comments.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Comment;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.CommentResource;
import com.google.gerrit.server.change.RevisionResource;
@@ -58,7 +58,7 @@ public class Comments implements ChildCollection<RevisionResource, CommentResour
String uuid = id.get();
ChangeNotes notes = rev.getNotes();
- for (Comment c : commentsUtil.publishedByPatchSet(notes, rev.getPatchSet().getId())) {
+ for (Comment c : commentsUtil.publishedByPatchSet(notes, rev.getPatchSet().id())) {
if (uuid.equals(c.key.uuid)) {
return new CommentResource(rev, c);
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index f626db2e68..9d65940694 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -21,6 +21,13 @@ import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.exceptions.InvalidMergeStrategyException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.SubmitType;
@@ -36,11 +43,6 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@@ -64,6 +66,7 @@ import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.restapi.project.CommitsCollection;
import com.google.gerrit.server.restapi.project.ProjectsCollection;
import com.google.gerrit.server.update.BatchUpdate;
@@ -79,6 +82,7 @@ import java.io.IOException;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.TimeZone;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.InvalidObjectIdException;
@@ -87,6 +91,7 @@ import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
@@ -101,7 +106,7 @@ import org.eclipse.jgit.util.ChangeIdUtil;
@Singleton
public class CreateChange
extends RetryingRestCollectionModifyView<
- TopLevelResource, ChangeResource, ChangeInput, Response<ChangeInfo>> {
+ TopLevelResource, ChangeResource, ChangeInput, ChangeInfo> {
private final String anonymousCowardName;
private final GitRepositoryManager gitManager;
private final Sequences seq;
@@ -113,6 +118,7 @@ public class CreateChange
private final ChangeInserter.Factory changeInserterFactory;
private final ChangeJson.Factory jsonFactory;
private final ChangeFinder changeFinder;
+ private final Provider<InternalChangeQuery> queryProvider;
private final PatchSetUtil psUtil;
private final MergeUtil.Factory mergeUtilFactory;
private final SubmitType submitType;
@@ -133,6 +139,7 @@ public class CreateChange
ChangeInserter.Factory changeInserterFactory,
ChangeJson.Factory json,
ChangeFinder changeFinder,
+ Provider<InternalChangeQuery> queryProvider,
RetryHelper retryHelper,
PatchSetUtil psUtil,
@GerritServerConfig Config config,
@@ -151,6 +158,7 @@ public class CreateChange
this.changeInserterFactory = changeInserterFactory;
this.jsonFactory = json;
this.changeFinder = changeFinder;
+ this.queryProvider = queryProvider;
this.psUtil = psUtil;
this.submitType = config.getEnum("project", null, "submitType", SubmitType.MERGE_IF_NECESSARY);
this.disablePrivateChanges = config.getBoolean("change", null, "disablePrivateChanges", false);
@@ -164,6 +172,9 @@ public class CreateChange
BatchUpdate.Factory updateFactory, TopLevelResource parent, ChangeInput input)
throws IOException, InvalidChangeOperationException, RestApiException, UpdateException,
PermissionBackendException, ConfigInvalidException {
+ if (!user.get().isIdentifiedUser()) {
+ throw new AuthException("Authentication required");
+ }
IdentifiedUser me = user.get().asIdentifiedUser();
checkAndSanitizeChangeInput(input, me);
@@ -209,6 +220,20 @@ public class CreateChange
}
input.subject = subject;
+ Optional<String> changeId = getChangeIdFromMessage(input.subject);
+ if (changeId.isPresent()) {
+ if (!queryProvider
+ .get()
+ .setLimit(1)
+ .byBranchKey(
+ BranchNameKey.create(input.project, input.branch), Change.key(changeId.get()))
+ .isEmpty()) {
+ throw new ResourceConflictException(
+ String.format(
+ "A change with Change-Id %s already exists for this branch.", changeId.get()));
+ }
+ }
+
if (input.topic != null) {
input.topic = Strings.emptyToNull(input.topic.trim());
}
@@ -238,7 +263,7 @@ public class CreateChange
input.workInProgress = true;
} else {
input.workInProgress =
- firstNonNull(me.state().getGeneralPreferences().workInProgressByDefault, false);
+ firstNonNull(me.state().generalPreferences().workInProgressByDefault, false);
}
}
@@ -281,7 +306,7 @@ public class CreateChange
if (input.baseChange != null) {
ChangeNotes baseChange = getBaseChange(input.baseChange);
basePatchSet = psUtil.current(baseChange);
- groups = basePatchSet.getGroups();
+ groups = basePatchSet.groups();
}
ObjectId parentCommit =
getParentCommit(
@@ -302,7 +327,7 @@ public class CreateChange
c = newCommit(oi, rw, author, mergeTip, commitMessage);
}
- Change.Id changeId = new Change.Id(seq.nextChangeId());
+ Change.Id changeId = Change.id(seq.nextChangeId());
ChangeInserter ins = changeInserterFactory.create(changeId, c, input.branch);
ins.setMessage(String.format("Uploaded patch set %s.", ins.getPatchSetId().get()));
ins.setTopic(input.topic);
@@ -318,7 +343,7 @@ public class CreateChange
bu.execute();
}
return ins.getChange();
- } catch (IllegalArgumentException e) {
+ } catch (InvalidMergeStrategyException e) {
throw new BadRequestException(e.getMessage());
}
}
@@ -351,7 +376,7 @@ public class CreateChange
throws BadRequestException, IOException, UnprocessableEntityException,
ResourceConflictException {
if (basePatchSet != null) {
- return ObjectId.fromString(basePatchSet.getRevision().get());
+ return basePatchSet.commitId();
}
Ref destRef = repo.getRefDatabase().exactRef(inputBranch);
@@ -403,6 +428,17 @@ public class CreateChange
return parentCommit;
}
+ private Optional<String> getChangeIdFromMessage(String subject) {
+ int indexOfChangeId = ChangeIdUtil.indexOfChangeId(subject, "\n");
+ if (indexOfChangeId == -1) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ subject.substring(
+ indexOfChangeId + 11 /* "Change-Id: "*/,
+ indexOfChangeId + 12 /* "Change-Id: I" */ + Constants.OBJECT_ID_STRING_LENGTH));
+ }
+
private String getCommitMessage(String subject, IdentifiedUser me) {
// Add a Change-Id line if there isn't already one
String commitMessage = subject;
@@ -411,15 +447,14 @@ public class CreateChange
commitMessage = ChangeIdUtil.insertId(commitMessage, id);
}
- if (Boolean.TRUE.equals(me.state().getGeneralPreferences().signedOffBy)) {
+ if (Boolean.TRUE.equals(me.state().generalPreferences().signedOffBy)) {
commitMessage =
Joiner.on("\n")
.join(
commitMessage.trim(),
String.format(
"%s%s",
- SIGNED_OFF_BY_TAG,
- me.state().getAccount().getNameEmail(anonymousCowardName)));
+ SIGNED_OFF_BY_TAG, me.state().account().getNameEmail(anonymousCowardName)));
}
return commitMessage;
diff --git a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index b6e7628731..f434e31bee 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -14,9 +14,11 @@
package com.google.gerrit.server.restapi.change;
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentCommitId;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -25,9 +27,6 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.RevisionResource;
@@ -48,7 +47,7 @@ import java.util.Collections;
@Singleton
public class CreateDraftComment
- extends RetryingRestModifyView<RevisionResource, DraftInput, Response<CommentInfo>> {
+ extends RetryingRestModifyView<RevisionResource, DraftInput, CommentInfo> {
private final Provider<CommentJson> commentJson;
private final CommentsUtil commentsUtil;
private final PatchSetUtil psUtil;
@@ -84,7 +83,7 @@ public class CreateDraftComment
try (BatchUpdate bu =
updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
- Op op = new Op(rsrc.getPatchSet().getId(), in);
+ Op op = new Op(rsrc.getPatchSet().id(), in);
bu.addOp(rsrc.getChange().getId(), op);
bu.execute();
return Response.created(
@@ -115,13 +114,14 @@ public class CreateDraftComment
comment =
commentsUtil.newComment(
- ctx, in.path, ps.getId(), in.side(), in.message.trim(), in.unresolved, parentUuid);
+ ctx, in.path, ps.id(), in.side(), in.message.trim(), in.unresolved, parentUuid);
comment.setLineNbrAndRange(in.line, in.range);
comment.tag = in.tag;
- setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
+ setCommentCommitId(comment, patchListCache, ctx.getChange(), ps);
- commentsUtil.putComments(ctx.getUpdate(psId), Status.DRAFT, Collections.singleton(comment));
+ commentsUtil.putComments(
+ ctx.getUpdate(psId), Comment.Status.DRAFT, Collections.singleton(comment));
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
index b7bcaf9852..a1594861ce 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
@@ -17,6 +17,11 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.exceptions.InvalidMergeStrategyException;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.MergeInput;
@@ -28,10 +33,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -76,7 +77,7 @@ import org.eclipse.jgit.util.ChangeIdUtil;
@Singleton
public class CreateMergePatchSet
- extends RetryingRestModifyView<ChangeResource, MergePatchSetInput, Response<ChangeInfo>> {
+ extends RetryingRestModifyView<ChangeResource, MergePatchSetInput, ChangeInfo> {
private final GitRepositoryManager gitManager;
private final CommitsCollection commits;
private final TimeZone serverTimeZone;
@@ -138,7 +139,7 @@ public class CreateMergePatchSet
PatchSet ps = psUtil.current(rsrc.getNotes());
Change change = rsrc.getChange();
Project.NameKey project = change.getProject();
- Branch.NameKey dest = change.getDest();
+ BranchNameKey dest = change.getDest();
try (Repository git = gitManager.openRepository(project);
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
@@ -154,10 +155,10 @@ public class CreateMergePatchSet
List<String> groups = null;
if (!in.inheritParent && !in.baseChange.isEmpty()) {
PatchSet basePS = findBasePatchSet(in.baseChange);
- currentPsCommit = rw.parseCommit(ObjectId.fromString(basePS.getRevision().get()));
- groups = basePS.getGroups();
+ currentPsCommit = rw.parseCommit(basePS.commitId());
+ groups = basePS.groups();
} else {
- currentPsCommit = rw.parseCommit(ObjectId.fromString(ps.getRevision().get()));
+ currentPsCommit = rw.parseCommit(ps.commitId());
}
Timestamp now = TimeUtil.nowTs();
@@ -176,7 +177,7 @@ public class CreateMergePatchSet
author,
ObjectId.fromString(change.getKey().get().substring(1)));
- PatchSet.Id nextPsId = ChangeUtil.nextPatchSetId(ps.getId());
+ PatchSet.Id nextPsId = ChangeUtil.nextPatchSetId(ps.id());
PatchSetInserter psInserter =
patchSetInserterFactory.create(rsrc.getNotes(), nextPsId, newCommit);
try (BatchUpdate bu = updateFactory.create(project, me, now)) {
@@ -194,6 +195,8 @@ public class CreateMergePatchSet
ChangeJson json = jsonFactory.create(ListChangesOption.CURRENT_REVISION);
return Response.ok(json.format(psInserter.getChange()));
+ } catch (InvalidMergeStrategyException e) {
+ throw new BadRequestException(e.getMessage());
}
}
@@ -215,7 +218,7 @@ public class CreateMergePatchSet
private RevCommit createMergeCommit(
MergePatchSetInput in,
ProjectState projectState,
- Branch.NameKey dest,
+ BranchNameKey dest,
Repository git,
ObjectInserter oi,
RevWalk rw,
@@ -234,7 +237,7 @@ public class CreateMergePatchSet
parentCommit = currentPsCommit.getId();
} else {
// get the current branch tip of destination branch
- Ref destRef = git.getRefDatabase().exactRef(dest.get());
+ Ref destRef = git.getRefDatabase().exactRef(dest.branch());
if (destRef != null) {
parentCommit = destRef.getObjectId();
} else {
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
index 02387be556..834782ffc0 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
@@ -14,13 +14,13 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountLoader;
@@ -42,8 +42,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
@Singleton
-public class DeleteAssignee
- extends RetryingRestModifyView<ChangeResource, Input, Response<AccountInfo>> {
+public class DeleteAssignee extends RetryingRestModifyView<ChangeResource, Input, AccountInfo> {
private final ChangeMessagesUtil cmUtil;
private final AssigneeChanged assigneeChanged;
@@ -105,7 +104,7 @@ public class DeleteAssignee
}
public Account.Id getDeletedAssignee() {
- return deletedAssignee != null ? deletedAssignee.getAccount().getId() : null;
+ return deletedAssignee != null ? deletedAssignee.account().id() : null;
}
private void addMessage(
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChange.java b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
index 3021d8142b..aa4dcf0598 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChange.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
@@ -16,12 +16,12 @@ package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.DeleteChangeOp;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -36,7 +36,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
@Singleton
-public class DeleteChange extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
+public class DeleteChange extends RetryingRestModifyView<ChangeResource, Input, Object>
implements UiAction<ChangeResource> {
private final DeleteChangeOp.Factory opFactory;
@@ -48,7 +48,7 @@ public class DeleteChange extends RetryingRestModifyView<ChangeResource, Input,
}
@Override
- protected Response<?> applyImpl(
+ protected Response<Object> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws RestApiException, UpdateException, PermissionBackendException {
if (!isChangeDeletable(rsrc)) {
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
index 0fb8e18403..30cfad6b65 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
@@ -20,14 +20,14 @@ import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.DeleteChangeMessageInput;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountLoader;
@@ -53,7 +53,7 @@ import java.util.List;
@Singleton
public class DeleteChangeMessage
extends RetryingRestModifyView<
- ChangeMessageResource, DeleteChangeMessageInput, Response<ChangeMessageInfo>> {
+ ChangeMessageResource, DeleteChangeMessageInput, ChangeMessageInfo> {
private final Provider<CurrentUser> userProvider;
private final PermissionBackend permissionBackend;
@@ -146,7 +146,7 @@ public class DeleteChangeMessage
@Singleton
public static class DefaultDeleteChangeMessage
- extends RetryingRestModifyView<ChangeMessageResource, Input, Response<ChangeMessageInfo>> {
+ extends RetryingRestModifyView<ChangeMessageResource, Input, ChangeMessageInfo> {
private final DeleteChangeMessage deleteChangeMessage;
@Inject
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteComment.java b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
index 30a8efd713..95479a63a7 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
@@ -15,13 +15,14 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.DeleteCommentInput;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.CommentResource;
@@ -71,7 +72,7 @@ public class DeleteComment
}
@Override
- public CommentInfo applyImpl(
+ public Response<CommentInfo> applyImpl(
BatchUpdate.Factory batchUpdateFactory, CommentResource rsrc, DeleteCommentInput input)
throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException,
UpdateException {
@@ -100,7 +101,7 @@ public class DeleteComment
throw new ResourceNotFoundException("comment not found: " + rsrc.getComment().key);
}
- return commentJson.get().newCommentFormatter().format(updatedComment.get());
+ return Response.ok(commentJson.get().newCommentFormatter().format(updatedComment.get()));
}
private static String getCommentNewMessage(String name, String reason) {
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
index de04d36184..9296988a6a 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
@@ -14,15 +14,15 @@
package com.google.gerrit.server.restapi.change;
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentCommitId;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.DraftCommentResource;
@@ -42,7 +42,7 @@ import java.util.Optional;
@Singleton
public class DeleteDraftComment
- extends RetryingRestModifyView<DraftCommentResource, Input, Response<CommentInfo>> {
+ extends RetryingRestModifyView<DraftCommentResource, Input, CommentInfo> {
private final CommentsUtil commentsUtil;
private final PatchSetUtil psUtil;
@@ -88,13 +88,13 @@ public class DeleteDraftComment
if (!maybeComment.isPresent()) {
return false; // Nothing to do.
}
- PatchSet.Id psId = new PatchSet.Id(ctx.getChange().getId(), key.patchSetId);
+ PatchSet.Id psId = PatchSet.id(ctx.getChange().getId(), key.patchSetId);
PatchSet ps = psUtil.get(ctx.getNotes(), psId);
if (ps == null) {
throw new ResourceNotFoundException("patch set not found: " + psId);
}
Comment c = maybeComment.get();
- setCommentRevId(c, patchListCache, ctx.getChange(), ps);
+ setCommentCommitId(c, patchListCache, ctx.getChange(), ps);
commentsUtil.deleteComments(ctx.getUpdate(psId), Collections.singleton(c));
return true;
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeletePrivate.java b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
index 8601e683b4..de7a683ca2 100644
--- a/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
@@ -36,7 +36,7 @@ import com.google.inject.Singleton;
@Singleton
public class DeletePrivate
- extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, Response<String>> {
+ extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, String> {
private final PermissionBackend permissionBackend;
private final SetPrivateOp.Factory setPrivateOpFactory;
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
index 12dbcdd6d6..b98bb3bdfe 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.DeleteReviewerByEmailOp;
import com.google.gerrit.server.change.DeleteReviewerOp;
import com.google.gerrit.server.change.NotifyResolver;
@@ -34,7 +34,7 @@ import com.google.inject.Singleton;
@Singleton
public class DeleteReviewer
- extends RetryingRestModifyView<ReviewerResource, DeleteReviewerInput, Response<?>> {
+ extends RetryingRestModifyView<ReviewerResource, DeleteReviewerInput, Object> {
private final DeleteReviewerOp.Factory deleteReviewerOpFactory;
private final DeleteReviewerByEmailOp.Factory deleteReviewerByEmailOpFactory;
@@ -50,7 +50,7 @@ public class DeleteReviewer
}
@Override
- protected Response<?> applyImpl(
+ protected Response<Object> applyImpl(
BatchUpdate.Factory updateFactory, ReviewerResource rsrc, DeleteReviewerInput input)
throws RestApiException, UpdateException {
if (input == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteVote.java b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
index 77894fb246..1193ad6974 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteVote.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
@@ -19,6 +19,11 @@ import static java.util.Objects.requireNonNull;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -27,11 +32,6 @@ import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
@@ -64,7 +64,7 @@ import java.util.Map;
import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
-public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteInput, Response<?>> {
+public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteInput, Object> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final ApprovalsUtil approvalsUtil;
@@ -102,7 +102,7 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
}
@Override
- protected Response<?> applyImpl(
+ protected Response<Object> applyImpl(
BatchUpdate.Factory updateFactory, VoteResource rsrc, DeleteVoteInput input)
throws RestApiException, UpdateException, IOException, ConfigInvalidException {
if (input == null) {
@@ -170,16 +170,16 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
boolean found = false;
LabelTypes labelTypes = projectState.getLabelTypes(ctx.getNotes());
- Account.Id accountId = accountState.getAccount().getId();
+ Account.Id accountId = accountState.account().id();
for (PatchSetApproval a :
approvalsUtil.byPatchSetUser(
ctx.getNotes(), psId, accountId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
- if (labelTypes.byLabel(a.getLabelId()) == null) {
+ if (labelTypes.byLabel(a.labelId()) == null) {
continue; // Ignore undefined labels.
- } else if (!a.getLabel().equals(label)) {
+ } else if (!a.label().equals(label)) {
// Populate map for non-matching labels, needed by VoteDeleted.
- newApprovals.put(a.getLabel(), a.getValue());
+ newApprovals.put(a.label(), a.value());
continue;
} else {
try {
@@ -189,11 +189,11 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
}
}
// Set the approval to 0 if vote is being removed.
- newApprovals.put(a.getLabel(), (short) 0);
+ newApprovals.put(a.label(), (short) 0);
found = true;
// Set old value, as required by VoteDeleted.
- oldApprovals.put(a.getLabel(), a.getValue());
+ oldApprovals.put(a.label(), a.value());
break;
}
if (!found) {
diff --git a/java/com/google/gerrit/server/restapi/change/DownloadContent.java b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
index 1022cad965..4a4a6808f4 100644
--- a/java/com/google/gerrit/server/restapi/change/DownloadContent.java
+++ b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.FileContentUtil;
import com.google.gerrit.server.change.FileResource;
@@ -24,7 +25,6 @@ import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import java.io.IOException;
-import org.eclipse.jgit.lib.ObjectId;
import org.kohsuke.args4j.Option;
public class DownloadContent implements RestReadView<FileResource> {
@@ -41,12 +41,12 @@ public class DownloadContent implements RestReadView<FileResource> {
}
@Override
- public BinaryResult apply(FileResource rsrc)
+ public Response<BinaryResult> apply(FileResource rsrc)
throws ResourceNotFoundException, IOException, NoSuchChangeException {
- String path = rsrc.getPatchKey().get();
+ String path = rsrc.getPatchKey().fileName();
RevisionResource rev = rsrc.getRevision();
- ObjectId revstr = ObjectId.fromString(rev.getPatchSet().getRevision().get());
- return fileContentUtil.downloadContent(
- projectCache.checkedGet(rev.getProject()), revstr, path, parent);
+ return Response.ok(
+ fileContentUtil.downloadContent(
+ projectCache.checkedGet(rev.getProject()), rev.getPatchSet().commitId(), path, parent));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/DraftComments.java b/java/com/google/gerrit/server/restapi/change/DraftComments.java
index dd61ca0855..bab1ac9d9f 100644
--- a/java/com/google/gerrit/server/restapi/change/DraftComments.java
+++ b/java/com/google/gerrit/server/restapi/change/DraftComments.java
@@ -14,13 +14,13 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Comment;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.DraftCommentResource;
@@ -66,7 +66,7 @@ public class DraftComments implements ChildCollection<RevisionResource, DraftCom
String uuid = id.get();
for (Comment c :
commentsUtil.draftByPatchSetAuthor(
- rev.getPatchSet().getId(), rev.getAccountId(), rev.getNotes())) {
+ rev.getPatchSet().id(), rev.getAccountId(), rev.getNotes())) {
if (uuid.equals(c.key.uuid)) {
return new DraftCommentResource(rev, c);
}
diff --git a/java/com/google/gerrit/server/restapi/change/Files.java b/java/com/google/gerrit/server/restapi/change/Files.java
index d8928105a2..392aef7dc2 100644
--- a/java/com/google/gerrit/server/restapi/change/Files.java
+++ b/java/com/google/gerrit/server/restapi/change/Files.java
@@ -19,6 +19,10 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
@@ -31,10 +35,6 @@ import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.AccountPatchReviewStore;
@@ -63,7 +63,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -166,13 +165,13 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
Response.ok(
fileInfoJson.toFileInfoMap(
resource.getChange(),
- resource.getPatchSet().getRevision(),
+ resource.getPatchSet().commitId(),
baseResource.getPatchSet()));
} else if (parentNum != 0) {
int parents =
gApi.changes()
.id(resource.getChange().getChangeId())
- .revision(resource.getPatchSet().getId().get())
+ .revision(resource.getPatchSet().id().get())
.commit(false)
.parents
.size();
@@ -182,7 +181,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
r =
Response.ok(
fileInfoJson.toFileInfoMap(
- resource.getChange(), resource.getPatchSet().getRevision(), parentNum - 1));
+ resource.getChange(), resource.getPatchSet().commitId(), parentNum - 1));
} else {
r = Response.ok(fileInfoJson.toFileInfoMap(resource.getChange(), resource.getPatchSet()));
}
@@ -219,8 +218,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
ObjectReader or = git.newObjectReader();
RevWalk rw = new RevWalk(or);
TreeWalk tw = new TreeWalk(or)) {
- RevCommit c =
- rw.parseCommit(ObjectId.fromString(resource.getPatchSet().getRevision().get()));
+ RevCommit c = rw.parseCommit(resource.getPatchSet().commitId());
tw.addTree(c.getTree());
tw.setRecursive(true);
@@ -244,11 +242,11 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
Account.Id userId = user.getAccountId();
PatchSet patchSetId = resource.getPatchSet();
Optional<PatchSetWithReviewedFiles> o;
- o = accountPatchReviewStore.call(s -> s.findReviewed(patchSetId.getId(), userId));
+ o = accountPatchReviewStore.call(s -> s.findReviewed(patchSetId.id(), userId));
if (o.isPresent()) {
PatchSetWithReviewedFiles res = o.get();
- if (res.patchSetId().equals(patchSetId.getId())) {
+ if (res.patchSetId().equals(patchSetId.id())) {
return res.files();
}
@@ -327,7 +325,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
}
accountPatchReviewStore.run(
- s -> s.markReviewed(resource.getPatchSet().getId(), userId, pathList));
+ s -> s.markReviewed(resource.getPatchSet().id(), userId, pathList));
return pathList;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Fixes.java b/java/com/google/gerrit/server/restapi/change/Fixes.java
index 855d1f48cb..38240e3a98 100644
--- a/java/com/google/gerrit/server/restapi/change/Fixes.java
+++ b/java/com/google/gerrit/server/restapi/change/Fixes.java
@@ -14,13 +14,13 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.FixSuggestion;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.FixSuggestion;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.FixResource;
import com.google.gerrit.server.change.RevisionResource;
@@ -54,7 +54,7 @@ public class Fixes implements ChildCollection<RevisionResource, FixResource> {
ChangeNotes changeNotes = revisionResource.getNotes();
List<RobotComment> robotComments =
- commentsUtil.robotCommentsByPatchSet(changeNotes, revisionResource.getPatchSet().getId());
+ commentsUtil.robotCommentsByPatchSet(changeNotes, revisionResource.getPatchSet().id());
for (RobotComment robotComment : robotComments) {
for (FixSuggestion fixSuggestion : robotComment.fixSuggestions) {
if (Objects.equals(fixId, fixSuggestion.fixId)) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetArchive.java b/java/com/google/gerrit/server/restapi/change/GetArchive.java
index 1bd1bce31e..4ebcbdda2f 100644
--- a/java/com/google/gerrit/server/restapi/change/GetArchive.java
+++ b/java/com/google/gerrit/server/restapi/change/GetArchive.java
@@ -14,10 +14,13 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
+
import com.google.common.base.Strings;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.ArchiveFormat;
import com.google.gerrit.server.change.RevisionResource;
@@ -27,7 +30,6 @@ import java.io.IOException;
import java.io.OutputStream;
import org.eclipse.jgit.api.ArchiveCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -47,7 +49,7 @@ public class GetArchive implements RestReadView<RevisionResource> {
}
@Override
- public BinaryResult apply(RevisionResource rsrc)
+ public Response<BinaryResult> apply(RevisionResource rsrc)
throws BadRequestException, IOException, MethodNotAllowedException {
if (Strings.isNullOrEmpty(format)) {
throw new BadRequestException("format is not specified");
@@ -65,7 +67,7 @@ public class GetArchive implements RestReadView<RevisionResource> {
final RevCommit commit;
String name;
try (RevWalk rw = new RevWalk(repo)) {
- commit = rw.parseCommit(ObjectId.fromString(rsrc.getPatchSet().getRevision().get()));
+ commit = rw.parseCommit(rsrc.getPatchSet().commitId());
name = name(f, rw, commit);
}
@@ -93,7 +95,7 @@ public class GetArchive implements RestReadView<RevisionResource> {
bin.disableGzip().setContentType(f.getMimeType()).setAttachmentName(name);
close = false;
- return bin;
+ return Response.ok(bin);
} finally {
if (close) {
repo.close();
@@ -104,6 +106,6 @@ public class GetArchive implements RestReadView<RevisionResource> {
private static String name(ArchiveFormat format, RevWalk rw, RevCommit commit)
throws IOException {
return String.format(
- "%s%s", rw.getObjectReader().abbreviate(commit, 7).name(), format.getDefaultSuffix());
+ "%s%s", abbreviateName(commit, rw.getObjectReader()), format.getDefaultSuffix());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetAssignee.java b/java/com/google/gerrit/server/restapi/change/GetAssignee.java
index f89fe1b0ea..a5820bfa8e 100644
--- a/java/com/google/gerrit/server/restapi/change/GetAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/GetAssignee.java
@@ -14,10 +14,10 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/restapi/change/GetBlame.java b/java/com/google/gerrit/server/restapi/change/GetBlame.java
index 98a3a8aa5a..12b4d440f3 100644
--- a/java/com/google/gerrit/server/restapi/change/GetBlame.java
+++ b/java/com/google/gerrit/server/restapi/change/GetBlame.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.BlameInfo;
import com.google.gerrit.extensions.common.RangeInfo;
import com.google.gerrit.extensions.restapi.CacheControl;
@@ -23,7 +24,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.FileResource;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -92,7 +92,7 @@ public class GetBlame implements RestReadView<FileResource> {
String refName =
resource.getRevision().getEdit().isPresent()
? resource.getRevision().getEdit().get().getRefName()
- : resource.getRevision().getPatchSet().getRefName();
+ : resource.getRevision().getPatchSet().refName();
Ref ref = repository.findRef(refName);
if (ref == null) {
@@ -102,7 +102,7 @@ public class GetBlame implements RestReadView<FileResource> {
RevCommit revCommit = revWalk.parseCommit(objectId);
RevCommit[] parents = revCommit.getParents();
- String path = resource.getPatchKey().getFileName();
+ String path = resource.getPatchKey().fileName();
List<BlameInfo> result;
if (!base) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetChangeMessage.java b/java/com/google/gerrit/server/restapi/change/GetChangeMessage.java
index f55785d161..9e0e0e3b7b 100644
--- a/java/com/google/gerrit/server/restapi/change/GetChangeMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/GetChangeMessage.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.ChangeMessageResource;
import com.google.inject.Singleton;
@@ -23,7 +24,7 @@ import com.google.inject.Singleton;
@Singleton
public class GetChangeMessage implements RestReadView<ChangeMessageResource> {
@Override
- public ChangeMessageInfo apply(ChangeMessageResource resource) {
- return resource.getChangeMessage();
+ public Response<ChangeMessageInfo> apply(ChangeMessageResource resource) {
+ return Response.ok(resource.getChangeMessage());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetComment.java b/java/com/google/gerrit/server/restapi/change/GetComment.java
index 0109c9501f..5103325557 100644
--- a/java/com/google/gerrit/server/restapi/change/GetComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetComment.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.CommentResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,7 +34,7 @@ public class GetComment implements RestReadView<CommentResource> {
}
@Override
- public CommentInfo apply(CommentResource rsrc) throws PermissionBackendException {
- return commentJson.get().newCommentFormatter().format(rsrc.getComment());
+ public Response<CommentInfo> apply(CommentResource rsrc) throws PermissionBackendException {
+ return Response.ok(commentJson.get().newCommentFormatter().format(rsrc.getComment()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetCommit.java b/java/com/google/gerrit/server/restapi/change/GetCommit.java
index 29286cbabf..21a08dcd23 100644
--- a/java/com/google/gerrit/server/restapi/change/GetCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/GetCommit.java
@@ -15,18 +15,17 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.RevisionJson;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -55,8 +54,7 @@ public class GetCommit implements RestReadView<RevisionResource> {
Project.NameKey p = rsrc.getChange().getProject();
try (Repository repo = repoManager.openRepository(p);
RevWalk rw = new RevWalk(repo)) {
- String rev = rsrc.getPatchSet().getRevision().get();
- RevCommit commit = rw.parseCommit(ObjectId.fromString(rev));
+ RevCommit commit = rw.parseCommit(rsrc.getPatchSet().commitId());
rw.parseBody(commit);
CommitInfo info =
json.create(ImmutableSet.of())
diff --git a/java/com/google/gerrit/server/restapi/change/GetContent.java b/java/com/google/gerrit/server/restapi/change/GetContent.java
index 1d35ab5915..bf7c51f652 100644
--- a/java/com/google/gerrit/server/restapi/change/GetContent.java
+++ b/java/com/google/gerrit/server/restapi/change/GetContent.java
@@ -14,13 +14,14 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.FileContentUtil;
import com.google.gerrit.server.change.FileResource;
@@ -33,7 +34,6 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import java.io.IOException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -61,25 +61,28 @@ public class GetContent implements RestReadView<FileResource> {
}
@Override
- public BinaryResult apply(FileResource rsrc)
+ public Response<BinaryResult> apply(FileResource rsrc)
throws ResourceNotFoundException, IOException, BadRequestException {
- String path = rsrc.getPatchKey().get();
+ String path = rsrc.getPatchKey().fileName();
if (Patch.COMMIT_MSG.equals(path)) {
String msg = getMessage(rsrc.getRevision().getChangeResource().getNotes());
- return BinaryResult.create(msg)
- .setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
- .base64();
+ return Response.ok(
+ BinaryResult.create(msg)
+ .setContentType(FileContentUtil.TEXT_X_GERRIT_COMMIT_MESSAGE)
+ .base64());
} else if (Patch.MERGE_LIST.equals(path)) {
byte[] mergeList = getMergeList(rsrc.getRevision().getChangeResource().getNotes());
- return BinaryResult.create(mergeList)
- .setContentType(FileContentUtil.TEXT_X_GERRIT_MERGE_LIST)
- .base64();
+ return Response.ok(
+ BinaryResult.create(mergeList)
+ .setContentType(FileContentUtil.TEXT_X_GERRIT_MERGE_LIST)
+ .base64());
}
- return fileContentUtil.getContent(
- projectCache.checkedGet(rsrc.getRevision().getProject()),
- ObjectId.fromString(rsrc.getRevision().getPatchSet().getRevision().get()),
- path,
- parent);
+ return Response.ok(
+ fileContentUtil.getContent(
+ projectCache.checkedGet(rsrc.getRevision().getProject()),
+ rsrc.getRevision().getPatchSet().commitId(),
+ path,
+ parent));
}
private String getMessage(ChangeNotes notes) throws IOException {
@@ -91,7 +94,7 @@ public class GetContent implements RestReadView<FileResource> {
try (Repository git = gitManager.openRepository(notes.getProjectName());
RevWalk revWalk = new RevWalk(git)) {
- RevCommit commit = revWalk.parseCommit(ObjectId.fromString(ps.getRevision().get()));
+ RevCommit commit = revWalk.parseCommit(ps.commitId());
return commit.getFullMessage();
} catch (RepositoryNotFoundException e) {
throw new NoSuchChangeException(changeId, e);
@@ -108,9 +111,7 @@ public class GetContent implements RestReadView<FileResource> {
try (Repository git = gitManager.openRepository(notes.getProjectName());
RevWalk revWalk = new RevWalk(git)) {
return Text.forMergeList(
- ComparisonType.againstAutoMerge(),
- revWalk.getObjectReader(),
- ObjectId.fromString(ps.getRevision().get()))
+ ComparisonType.againstAutoMerge(), revWalk.getObjectReader(), ps.commitId())
.getContent();
} catch (RepositoryNotFoundException e) {
throw new NoSuchChangeException(changeId, e);
diff --git a/java/com/google/gerrit/server/restapi/change/GetDescription.java b/java/com/google/gerrit/server/restapi/change/GetDescription.java
index 1a7ec63c8c..6794d81e85 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDescription.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.restapi.change;
-import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.RevisionResource;
import com.google.inject.Singleton;
@@ -22,7 +22,7 @@ import com.google.inject.Singleton;
@Singleton
public class GetDescription implements RestReadView<RevisionResource> {
@Override
- public String apply(RevisionResource rsrc) {
- return Strings.nullToEmpty(rsrc.getPatchSet().getDescription());
+ public Response<String> apply(RevisionResource rsrc) {
+ return Response.ok(rsrc.getPatchSet().description().orElse(""));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetDiff.java b/java/com/google/gerrit/server/restapi/change/GetDiff.java
index 6a7f1faa84..39c5a3bef3 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDiff.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDiff.java
@@ -25,6 +25,9 @@ import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchScript.DisplayMethod;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.common.ChangeType;
@@ -43,9 +46,6 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.jgit.diff.ReplaceEdit;
import com.google.gerrit.prettify.common.SparseFileContent;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.change.FileContentUtil;
import com.google.gerrit.server.change.FileResource;
@@ -140,14 +140,14 @@ public class GetDiff implements RestReadView<FileResource> {
PatchScriptFactory psf;
PatchSet basePatchSet = null;
- PatchSet.Id pId = resource.getPatchKey().getParentKey();
- String fileName = resource.getPatchKey().getFileName();
+ PatchSet.Id pId = resource.getPatchKey().patchSetId();
+ String fileName = resource.getPatchKey().fileName();
ChangeNotes notes = resource.getRevision().getNotes();
if (base != null) {
RevisionResource baseResource =
revisions.parse(resource.getRevision().getChangeResource(), IdString.fromDecoded(base));
basePatchSet = baseResource.getPatchSet();
- psf = patchScriptFactoryFactory.create(notes, fileName, basePatchSet.getId(), pId, prefs);
+ psf = patchScriptFactoryFactory.create(notes, fileName, basePatchSet.id(), pId, prefs);
} else if (parentNum > 0) {
psf = patchScriptFactoryFactory.create(notes, fileName, parentNum - 1, pId, prefs);
} else {
@@ -195,20 +195,20 @@ public class GetDiff implements RestReadView<FileResource> {
ProjectState state = projectCache.get(resource.getRevision().getChange().getProject());
DiffInfo result = new DiffInfo();
- String revA = basePatchSet != null ? basePatchSet.getRefName() : content.commitIdA;
+ String revA = basePatchSet != null ? basePatchSet.refName() : content.commitIdA;
String revB =
resource.getRevision().getEdit().isPresent()
? resource.getRevision().getEdit().get().getRefName()
- : resource.getRevision().getPatchSet().getRefName();
+ : resource.getRevision().getPatchSet().refName();
- List<DiffWebLinkInfo> links =
+ ImmutableList<DiffWebLinkInfo> links =
webLinks.getDiffLinks(
state.getName(),
- resource.getPatchKey().getParentKey().getParentKey().get(),
- basePatchSet != null ? basePatchSet.getId().get() : null,
+ resource.getPatchKey().patchSetId().changeId().get(),
+ basePatchSet != null ? basePatchSet.id().get() : null,
revA,
MoreObjects.firstNonNull(ps.getOldName(), ps.getNewName()),
- resource.getPatchKey().getParentKey().get(),
+ resource.getPatchKey().patchSetId().get(),
revB,
ps.getNewName());
result.webLinks = links.isEmpty() ? null : links;
@@ -271,7 +271,7 @@ public class GetDiff implements RestReadView<FileResource> {
}
private List<WebLinkInfo> getFileWebLinks(Project project, String rev, String file) {
- List<WebLinkInfo> links = webLinks.getFileLinks(project.getName(), rev, file);
+ ImmutableList<WebLinkInfo> links = webLinks.getFileLinks(project.getName(), rev, file);
return links.isEmpty() ? null : links;
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetDraftComment.java b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
index ca5b56f16d..797dc9edc5 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.DraftCommentResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,7 +34,7 @@ public class GetDraftComment implements RestReadView<DraftCommentResource> {
}
@Override
- public CommentInfo apply(DraftCommentResource rsrc) throws PermissionBackendException {
- return commentJson.get().newCommentFormatter().format(rsrc.getComment());
+ public Response<CommentInfo> apply(DraftCommentResource rsrc) throws PermissionBackendException {
+ return Response.ok(commentJson.get().newCommentFormatter().format(rsrc.getComment()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetMergeList.java b/java/com/google/gerrit/server/restapi/change/GetMergeList.java
index 0c18a8f04e..0c67fd639c 100644
--- a/java/com/google/gerrit/server/restapi/change/GetMergeList.java
+++ b/java/com/google/gerrit/server/restapi/change/GetMergeList.java
@@ -16,12 +16,12 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.RevisionJson;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -31,7 +31,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -67,8 +66,7 @@ public class GetMergeList implements RestReadView<RevisionResource> {
Project.NameKey p = rsrc.getChange().getProject();
try (Repository repo = repoManager.openRepository(p);
RevWalk rw = new RevWalk(repo)) {
- String rev = rsrc.getPatchSet().getRevision().get();
- RevCommit commit = rw.parseCommit(ObjectId.fromString(rev));
+ RevCommit commit = rw.parseCommit(rsrc.getPatchSet().commitId());
rw.parseBody(commit);
if (uninterestingParent < 1 || uninterestingParent > commit.getParentCount()) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java b/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
index 1d56669ac6..c1c9a34950 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
@@ -16,10 +16,10 @@ package com.google.gerrit.server.restapi.change;
import static java.util.stream.Collectors.toList;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/restapi/change/GetPatch.java b/java/com/google/gerrit/server/restapi/change/GetPatch.java
index b205ece4ba..187ebcee19 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPatch.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPatch.java
@@ -14,11 +14,13 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -31,8 +33,6 @@ import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.jgit.diff.DiffFormatter;
-import org.eclipse.jgit.lib.AbbreviatedObjectId;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -60,15 +60,15 @@ public class GetPatch implements RestReadView<RevisionResource> {
}
@Override
- public BinaryResult apply(RevisionResource rsrc)
+ public Response<BinaryResult> apply(RevisionResource rsrc)
throws ResourceConflictException, IOException, ResourceNotFoundException {
final Repository repo = repoManager.openRepository(rsrc.getProject());
boolean close = true;
try {
final RevWalk rw = new RevWalk(repo);
+ BinaryResult bin = null;
try {
- final RevCommit commit =
- rw.parseCommit(ObjectId.fromString(rsrc.getPatchSet().getRevision().get()));
+ final RevCommit commit = rw.parseCommit(rsrc.getPatchSet().commitId());
RevCommit[] parents = commit.getParents();
if (parents.length > 1) {
throw new ResourceConflictException("Revision has more than 1 parent.");
@@ -78,7 +78,7 @@ public class GetPatch implements RestReadView<RevisionResource> {
final RevCommit base = parents[0];
rw.parseBody(base);
- BinaryResult bin =
+ bin =
new BinaryResult() {
@Override
public void writeTo(OutputStream out) throws IOException {
@@ -132,10 +132,13 @@ public class GetPatch implements RestReadView<RevisionResource> {
}
close = false;
- return bin;
+ return Response.ok(bin);
} finally {
if (close) {
rw.close();
+ if (bin != null) {
+ bin.close();
+ }
}
}
} finally {
@@ -189,7 +192,6 @@ public class GetPatch implements RestReadView<RevisionResource> {
}
private static String fileName(RevWalk rw, RevCommit commit) throws IOException {
- AbbreviatedObjectId id = rw.getObjectReader().abbreviate(commit, 7);
- return id.name() + ".diff";
+ return abbreviateName(commit, rw.getObjectReader()) + ".diff";
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
index fa5cc36f5e..765be5f51e 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
@@ -19,6 +19,7 @@ import com.google.gerrit.extensions.common.PureRevertInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.PureRevert;
@@ -46,9 +47,9 @@ public class GetPureRevert implements RestReadView<ChangeResource> {
}
@Override
- public PureRevertInfo apply(ChangeResource rsrc)
+ public Response<PureRevertInfo> apply(ChangeResource rsrc)
throws ResourceConflictException, IOException, BadRequestException, AuthException {
boolean isPureRevert = pureRevert.get(rsrc.getNotes(), Optional.ofNullable(claimedOriginal));
- return new PureRevertInfo(isPureRevert);
+ return Response.ok(new PureRevertInfo(isPureRevert));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetRelated.java b/java/com/google/gerrit/server/restapi/change/GetRelated.java
index 332cc4de08..a846d50f1c 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRelated.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRelated.java
@@ -19,14 +19,15 @@ import static java.util.stream.Collectors.toSet;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.RelatedChangeAndCommitInfo;
import com.google.gerrit.extensions.api.changes.RelatedChangesInfo;
import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.index.IndexConfig;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CommonConverters;
import com.google.gerrit.server.PatchSetUtil;
@@ -68,12 +69,12 @@ public class GetRelated implements RestReadView<RevisionResource> {
}
@Override
- public RelatedChangesInfo apply(RevisionResource rsrc)
+ public Response<RelatedChangesInfo> apply(RevisionResource rsrc)
throws RepositoryNotFoundException, IOException, NoSuchProjectException,
PermissionBackendException {
RelatedChangesInfo relatedChangesInfo = new RelatedChangesInfo();
relatedChangesInfo.changes = getRelated(rsrc);
- return relatedChangesInfo;
+ return Response.ok(relatedChangesInfo);
}
private List<RelatedChangeAndCommitInfo> getRelated(RevisionResource rsrc)
@@ -102,7 +103,7 @@ public class GetRelated implements RestReadView<RevisionResource> {
for (RelatedChangesSorter.PatchSetData d : sorter.sort(cds, basePs)) {
PatchSet ps = d.patchSet();
RevCommit commit;
- if (isEdit && ps.getId().equals(basePs.getId())) {
+ if (isEdit && ps.id().equals(basePs.id())) {
// Replace base of an edit with the edit itself.
ps = rsrc.getPatchSet();
commit = rsrc.getEdit().get().getEditCommit();
@@ -114,7 +115,7 @@ public class GetRelated implements RestReadView<RevisionResource> {
if (result.size() == 1) {
RelatedChangeAndCommitInfo r = result.get(0);
- if (r.commit != null && r.commit.commit.equals(rsrc.getPatchSet().getRevision().get())) {
+ if (r.commit != null && r.commit.commit.equals(rsrc.getPatchSet().commitId().name())) {
return Collections.emptyList();
}
}
@@ -123,13 +124,13 @@ public class GetRelated implements RestReadView<RevisionResource> {
@VisibleForTesting
public static Set<String> getAllGroups(ChangeNotes notes, PatchSetUtil psUtil) {
- return psUtil.byChange(notes).stream().flatMap(ps -> ps.getGroups().stream()).collect(toSet());
+ return psUtil.byChange(notes).stream().flatMap(ps -> ps.groups().stream()).collect(toSet());
}
private void reloadChangeIfStale(List<ChangeData> cds, PatchSet wantedPs) {
for (ChangeData cd : cds) {
- if (cd.getId().equals(wantedPs.getId().getParentKey())) {
- if (cd.patchSet(wantedPs.getId()) == null) {
+ if (cd.getId().equals(wantedPs.id().changeId())) {
+ if (cd.patchSet(wantedPs.id()) == null) {
cd.reloadChange();
}
}
@@ -144,7 +145,7 @@ public class GetRelated implements RestReadView<RevisionResource> {
if (change != null) {
info.changeId = change.getKey().get();
info._changeNumber = change.getChangeId();
- info._revisionNumber = ps != null ? ps.getPatchSetId() : null;
+ info._revisionNumber = ps != null ? ps.number() : null;
PatchSet.Id curr = change.currentPatchSetId();
info._currentRevisionNumber = curr != null ? curr.get() : null;
info.status = ChangeUtil.status(change).toUpperCase(Locale.US);
diff --git a/java/com/google/gerrit/server/restapi/change/GetReviewer.java b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
index 73760dab96..a672b176f8 100644
--- a/java/com/google/gerrit/server/restapi/change/GetReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.api.changes.ReviewerInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.ReviewerJson;
import com.google.gerrit.server.change.ReviewerResource;
@@ -33,7 +34,8 @@ public class GetReviewer implements RestReadView<ReviewerResource> {
}
@Override
- public List<ReviewerInfo> apply(ReviewerResource rsrc) throws PermissionBackendException {
- return json.format(rsrc);
+ public Response<List<ReviewerInfo>> apply(ReviewerResource rsrc)
+ throws PermissionBackendException {
+ return Response.ok(json.format(rsrc));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetRobotComment.java b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
index 75d994d1ce..4ff9942e2f 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.common.RobotCommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.RobotCommentResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,7 +34,8 @@ public class GetRobotComment implements RestReadView<RobotCommentResource> {
}
@Override
- public RobotCommentInfo apply(RobotCommentResource rsrc) throws PermissionBackendException {
- return commentJson.get().newRobotCommentFormatter().format(rsrc.getComment());
+ public Response<RobotCommentInfo> apply(RobotCommentResource rsrc)
+ throws PermissionBackendException {
+ return Response.ok(commentJson.get().newRobotCommentFormatter().format(rsrc.getComment()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetTopic.java b/java/com/google/gerrit/server/restapi/change/GetTopic.java
index 7ab1cb1289..6951fa5e4c 100644
--- a/java/com/google/gerrit/server/restapi/change/GetTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/GetTopic.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.ChangeResource;
import com.google.inject.Singleton;
@@ -22,7 +23,7 @@ import com.google.inject.Singleton;
@Singleton
public class GetTopic implements RestReadView<ChangeResource> {
@Override
- public String apply(ChangeResource rsrc) {
- return Strings.nullToEmpty(rsrc.getChange().getTopic());
+ public Response<String> apply(ChangeResource rsrc) {
+ return Response.ok(Strings.nullToEmpty(rsrc.getChange().getTopic()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Index.java b/java/com/google/gerrit/server/restapi/change/Index.java
index 90dad9800b..5a17c07d6f 100644
--- a/java/com/google/gerrit/server/restapi/change/Index.java
+++ b/java/com/google/gerrit/server/restapi/change/Index.java
@@ -30,7 +30,7 @@ import com.google.inject.Singleton;
import java.io.IOException;
@Singleton
-public class Index extends RetryingRestModifyView<ChangeResource, Input, Response<?>> {
+public class Index extends RetryingRestModifyView<ChangeResource, Input, Object> {
private final PermissionBackend permissionBackend;
private final ChangeIndexer indexer;
@@ -42,7 +42,7 @@ public class Index extends RetryingRestModifyView<ChangeResource, Input, Respons
}
@Override
- protected Response<?> applyImpl(
+ protected Response<Object> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws IOException, AuthException, PermissionBackendException {
permissionBackend.currentUser().check(GlobalPermission.MAINTAIN_SERVER);
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
index 403922db99..edd6201f69 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.restapi.change;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.query.change.ChangeData;
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
index f8fda731c9..cc35a5e477 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
@@ -14,10 +14,11 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Comment;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -59,12 +60,12 @@ public class ListChangeDrafts implements RestReadView<ChangeResource> {
}
@Override
- public Map<String, List<CommentInfo>> apply(ChangeResource rsrc)
+ public Response<Map<String, List<CommentInfo>>> apply(ChangeResource rsrc)
throws AuthException, PermissionBackendException {
if (requireAuthentication() && !rsrc.getUser().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
- return getCommentFormatter().format(listComments(rsrc));
+ return Response.ok(getCommentFormatter().format(listComments(rsrc)));
}
public List<CommentInfo> getComments(ChangeResource rsrc)
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
index a2e3d4b589..bfc9f1268e 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
@@ -16,9 +16,10 @@ package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.server.ChangeMessagesUtil.createChangeMessageInfo;
+import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.change.ChangeResource;
@@ -41,13 +42,14 @@ public class ListChangeMessages implements RestReadView<ChangeResource> {
}
@Override
- public List<ChangeMessageInfo> apply(ChangeResource resource) throws PermissionBackendException {
+ public Response<List<ChangeMessageInfo>> apply(ChangeResource resource)
+ throws PermissionBackendException {
List<ChangeMessage> messages = changeMessagesUtil.byChange(resource.getNotes());
List<ChangeMessageInfo> messageInfos =
messages.stream()
.map(m -> createChangeMessageInfo(m, accountLoader))
.collect(Collectors.toList());
accountLoader.fill();
- return messageInfos;
+ return Response.ok(messageInfos);
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
index e5840fdfad..719a4779db 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
@@ -42,14 +43,15 @@ public class ListChangeRobotComments implements RestReadView<ChangeResource> {
}
@Override
- public Map<String, List<RobotCommentInfo>> apply(ChangeResource rsrc)
+ public Response<Map<String, List<RobotCommentInfo>>> apply(ChangeResource rsrc)
throws AuthException, PermissionBackendException {
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
- return commentJson
- .get()
- .setFillAccounts(true)
- .setFillPatchSet(true)
- .newRobotCommentFormatter()
- .format(commentsUtil.robotCommentsByChange(cd.notes()));
+ return Response.ok(
+ commentJson
+ .get()
+ .setFillAccounts(true)
+ .setFillPatchSet(true)
+ .newRobotCommentFormatter()
+ .format(commentsUtil.robotCommentsByChange(cd.notes())));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListReviewers.java b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
index 12732ffed9..25ef480e00 100644
--- a/java/com/google/gerrit/server/restapi/change/ListReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
@@ -14,10 +14,11 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.changes.ReviewerInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ReviewerJson;
@@ -44,7 +45,7 @@ public class ListReviewers implements RestReadView<ChangeResource> {
}
@Override
- public List<ReviewerInfo> apply(ChangeResource rsrc) throws PermissionBackendException {
+ public Response<List<ReviewerInfo>> apply(ChangeResource rsrc) throws PermissionBackendException {
Map<String, ReviewerResource> reviewers = new LinkedHashMap<>();
for (Account.Id accountId : approvalsUtil.getReviewers(rsrc.getNotes()).all()) {
if (!reviewers.containsKey(accountId.toString())) {
@@ -56,6 +57,6 @@ public class ListReviewers implements RestReadView<ChangeResource> {
reviewers.put(adr.toString(), new ReviewerResource(rsrc, adr));
}
}
- return json.format(reviewers.values());
+ return Response.ok(json.format(reviewers.values()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
index b39ba63ed7..de05d2aeea 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.restapi.change;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -37,6 +37,6 @@ public class ListRevisionComments extends ListRevisionDrafts {
@Override
protected Iterable<Comment> listComments(RevisionResource rsrc) {
ChangeNotes notes = rsrc.getNotes();
- return commentsUtil.publishedByPatchSet(notes, rsrc.getPatchSet().getId());
+ return commentsUtil.publishedByPatchSet(notes, rsrc.getPatchSet().id());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
index a46bd6c802..199a752285 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
@@ -15,9 +15,10 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Comment;
import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -40,7 +41,7 @@ public class ListRevisionDrafts implements RestReadView<RevisionResource> {
protected Iterable<Comment> listComments(RevisionResource rsrc) {
return commentsUtil.draftByPatchSetAuthor(
- rsrc.getPatchSet().getId(), rsrc.getAccountId(), rsrc.getNotes());
+ rsrc.getPatchSet().id(), rsrc.getAccountId(), rsrc.getNotes());
}
protected boolean includeAuthorInfo() {
@@ -48,13 +49,14 @@ public class ListRevisionDrafts implements RestReadView<RevisionResource> {
}
@Override
- public Map<String, List<CommentInfo>> apply(RevisionResource rsrc)
+ public Response<Map<String, List<CommentInfo>>> apply(RevisionResource rsrc)
throws PermissionBackendException {
- return commentJson
- .get()
- .setFillAccounts(includeAuthorInfo())
- .newCommentFormatter()
- .format(listComments(rsrc));
+ return Response.ok(
+ commentJson
+ .get()
+ .setFillAccounts(includeAuthorInfo())
+ .newCommentFormatter()
+ .format(listComments(rsrc)));
}
public ImmutableList<CommentInfo> getComments(RevisionResource rsrc)
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
index 920cde9ea1..73b1f59b79 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
@@ -14,11 +14,12 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.changes.ReviewerInfo;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ReviewerJson;
import com.google.gerrit.server.change.ReviewerResource;
@@ -45,7 +46,7 @@ class ListRevisionReviewers implements RestReadView<RevisionResource> {
}
@Override
- public List<ReviewerInfo> apply(RevisionResource rsrc)
+ public Response<List<ReviewerInfo>> apply(RevisionResource rsrc)
throws MethodNotAllowedException, PermissionBackendException {
if (!rsrc.isCurrent()) {
throw new MethodNotAllowedException("Cannot list reviewers on non-current patch set");
@@ -62,6 +63,6 @@ class ListRevisionReviewers implements RestReadView<RevisionResource> {
reviewers.put(address.toString(), new ReviewerResource(rsrc, address));
}
}
- return json.format(reviewers.values());
+ return Response.ok(json.format(reviewers.values()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
index bbf46a3f70..bbbe12dd95 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
@@ -15,9 +15,10 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.extensions.common.RobotCommentInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -39,13 +40,14 @@ public class ListRobotComments implements RestReadView<RevisionResource> {
}
@Override
- public Map<String, List<RobotCommentInfo>> apply(RevisionResource rsrc)
+ public Response<Map<String, List<RobotCommentInfo>>> apply(RevisionResource rsrc)
throws PermissionBackendException {
- return commentJson
- .get()
- .setFillAccounts(true)
- .newRobotCommentFormatter()
- .format(listComments(rsrc));
+ return Response.ok(
+ commentJson
+ .get()
+ .setFillAccounts(true)
+ .newRobotCommentFormatter()
+ .format(listComments(rsrc)));
}
public ImmutableList<RobotCommentInfo> getComments(RevisionResource rsrc)
@@ -58,6 +60,6 @@ public class ListRobotComments implements RestReadView<RevisionResource> {
}
private Iterable<RobotComment> listComments(RevisionResource rsrc) {
- return commentsUtil.robotCommentsByPatchSet(rsrc.getNotes(), rsrc.getPatchSet().getId());
+ return commentsUtil.robotCommentsByPatchSet(rsrc.getNotes(), rsrc.getPatchSet().id());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Mergeable.java b/java/com/google/gerrit/server/restapi/change/Mergeable.java
index ece8938379..9b17ed6264 100644
--- a/java/com/google/gerrit/server/restapi/change/Mergeable.java
+++ b/java/com/google/gerrit/server/restapi/change/Mergeable.java
@@ -14,17 +14,17 @@
package com.google.gerrit.server.restapi.change;
-import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.MergeableInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.MergeabilityCache;
import com.google.gerrit.server.change.RevisionResource;
@@ -50,8 +50,6 @@ import org.eclipse.jgit.lib.Repository;
import org.kohsuke.args4j.Option;
public class Mergeable implements RestReadView<RevisionResource> {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
@Option(
name = "--other-branches",
aliases = {"-o"},
@@ -89,7 +87,7 @@ public class Mergeable implements RestReadView<RevisionResource> {
}
@Override
- public MergeableInfo apply(RevisionResource resource)
+ public Response<MergeableInfo> apply(RevisionResource resource)
throws AuthException, ResourceConflictException, BadRequestException, IOException {
Change change = resource.getChange();
PatchSet ps = resource.getPatchSet();
@@ -97,17 +95,17 @@ public class Mergeable implements RestReadView<RevisionResource> {
if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
- } else if (!ps.getId().equals(change.currentPatchSetId())) {
+ } else if (!ps.id().equals(change.currentPatchSetId())) {
// Only the current revision is mergeable. Others always fail.
- return result;
+ return Response.ok(result);
}
ChangeData cd = changeDataFactory.create(resource.getNotes());
result.submitType = getSubmitType(cd);
try (Repository git = gitManager.openRepository(change.getProject())) {
- ObjectId commit = toId(ps);
- Ref ref = git.getRefDatabase().exactRef(change.getDest().get());
+ ObjectId commit = ps.commitId();
+ Ref ref = git.getRefDatabase().exactRef(change.getDest().branch());
ProjectState projectState = projectCache.get(change.getProject());
String strategy = mergeUtilFactory.create(projectState).mergeStrategyName();
result.strategy = strategy;
@@ -132,7 +130,7 @@ public class Mergeable implements RestReadView<RevisionResource> {
}
}
}
- return result;
+ return Response.ok(result);
}
private SubmitType getSubmitType(ChangeData cd) {
@@ -161,15 +159,6 @@ public class Mergeable implements RestReadView<RevisionResource> {
return refresh(change, commit, ref, submitType, strategy, git, old);
}
- private static ObjectId toId(PatchSet ps) {
- try {
- return ObjectId.fromString(ps.getRevision().get());
- } catch (IllegalArgumentException e) {
- logger.atSevere().log("Invalid revision on patch set %s", ps);
- return null;
- }
- }
-
private boolean refresh(
final Change change,
ObjectId commit,
diff --git a/java/com/google/gerrit/server/restapi/change/Move.java b/java/com/google/gerrit/server/restapi/change/Move.java
index f2335b155d..7d4c4d19f4 100644
--- a/java/com/google/gerrit/server/restapi/change/Move.java
+++ b/java/com/google/gerrit/server/restapi/change/Move.java
@@ -23,6 +23,14 @@ import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.MoveInput;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -30,16 +38,9 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
@@ -114,7 +115,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
}
@Override
- protected ChangeInfo applyImpl(
+ protected Response<ChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, MoveInput input)
throws RestApiException, UpdateException, PermissionBackendException, IOException {
if (!moveEnabled) {
@@ -135,7 +136,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
throw new ResourceConflictException("Change is " + ChangeUtil.status(change));
}
- Branch.NameKey newDest = new Branch.NameKey(project, input.destinationBranch);
+ BranchNameKey newDest = BranchNameKey.create(project, input.destinationBranch);
if (change.getDest().equals(newDest)) {
throw new ResourceConflictException("Change is already destined for the specified branch");
}
@@ -157,14 +158,14 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
u.addOp(change.getId(), op);
u.execute();
}
- return json.noOptions().format(op.getChange());
+ return Response.ok(json.noOptions().format(op.getChange()));
}
private class Op implements BatchUpdateOp {
private final MoveInput input;
private Change change;
- private Branch.NameKey newDestKey;
+ private BranchNameKey newDestKey;
Op(MoveInput input) {
this.input = input;
@@ -183,8 +184,8 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
}
Project.NameKey projectKey = change.getProject();
- newDestKey = new Branch.NameKey(projectKey, input.destinationBranch);
- Branch.NameKey changePrevDest = change.getDest();
+ newDestKey = BranchNameKey.create(projectKey, input.destinationBranch);
+ BranchNameKey changePrevDest = change.getDest();
if (changePrevDest.equals(newDestKey)) {
throw new ResourceConflictException("Change is already destined for the specified branch");
}
@@ -193,8 +194,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
try (Repository repo = repoManager.openRepository(projectKey);
RevWalk revWalk = new RevWalk(repo)) {
RevCommit currPatchsetRevCommit =
- revWalk.parseCommit(
- ObjectId.fromString(psUtil.current(ctx.getNotes()).getRevision().get()));
+ revWalk.parseCommit(psUtil.current(ctx.getNotes()).commitId());
if (currPatchsetRevCommit.getParentCount() > 1) {
throw new ResourceConflictException("Merge commit cannot be moved");
}
@@ -216,7 +216,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
if (!asChanges(queryProvider.get().byBranchKey(newDestKey, changeKey)).isEmpty()) {
throw new ResourceConflictException(
"Destination "
- + newDestKey.getShortName()
+ + newDestKey.shortName()
+ " has a different change with same change key "
+ changeKey);
}
@@ -227,16 +227,16 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
PatchSet.Id psId = change.currentPatchSetId();
ChangeUpdate update = ctx.getUpdate(psId);
- update.setBranch(newDestKey.get());
+ update.setBranch(newDestKey.branch());
change.setDest(newDestKey);
updateApprovals(ctx, update, psId, projectKey);
StringBuilder msgBuf = new StringBuilder();
msgBuf.append("Change destination moved from ");
- msgBuf.append(changePrevDest.getShortName());
+ msgBuf.append(changePrevDest.shortName());
msgBuf.append(" to ");
- msgBuf.append(newDestKey.getShortName());
+ msgBuf.append(newDestKey.shortName());
if (!Strings.isNullOrEmpty(input.message)) {
msgBuf.append("\n\n");
msgBuf.append(input.message);
@@ -262,7 +262,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
approvalsUtil.byPatchSet(
ctx.getNotes(), psId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
ProjectState projectState = projectCache.checkedGet(project);
- LabelType type = projectState.getLabelTypes(ctx.getNotes()).byLabel(psa.getLabelId());
+ LabelType type = projectState.getLabelTypes(ctx.getNotes()).byLabel(psa.labelId());
// Only keep veto votes, defined as votes where:
// 1- the label function allows minimum values to block submission.
// 2- the vote holds the minimum value.
@@ -271,12 +271,13 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
}
// Remove votes from NoteDb.
- update.removeApprovalFor(psa.getAccountId(), psa.getLabel());
+ update.removeApprovalFor(psa.accountId(), psa.label());
approvals.add(
- new PatchSetApproval(
- new PatchSetApproval.Key(psId, psa.getAccountId(), new LabelId(psa.getLabel())),
- (short) 0,
- ctx.getWhen()));
+ PatchSetApproval.builder()
+ .key(PatchSetApproval.key(psId, psa.accountId(), LabelId.create(psa.label())))
+ .value(0)
+ .granted(ctx.getWhen())
+ .build());
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PostHashtags.java b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
index da499a91ba..516dead9f5 100644
--- a/java/com/google/gerrit/server/restapi/change/PostHashtags.java
+++ b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
@@ -33,8 +33,7 @@ import com.google.inject.Singleton;
@Singleton
public class PostHashtags
- extends RetryingRestModifyView<
- ChangeResource, HashtagsInput, Response<ImmutableSortedSet<String>>>
+ extends RetryingRestModifyView<ChangeResource, HashtagsInput, ImmutableSortedSet<String>>
implements UiAction<ChangeResource> {
private final SetHashtagsOp.Factory hashtagsFactory;
diff --git a/java/com/google/gerrit/server/restapi/change/PostPrivate.java b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
index 5aa2ecc07d..f008df3589 100644
--- a/java/com/google/gerrit/server/restapi/change/PostPrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
@@ -17,13 +17,13 @@ package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.SetPrivateOp;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -39,8 +39,7 @@ import com.google.inject.Singleton;
import org.eclipse.jgit.lib.Config;
@Singleton
-public class PostPrivate
- extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, Response<String>>
+public class PostPrivate extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, String>
implements UiAction<ChangeResource> {
private final PermissionBackend permissionBackend;
private final SetPrivateOp.Factory setPrivateOpFactory;
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 8c94ba322f..e0d0a04640 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -16,7 +16,8 @@ package com.google.gerrit.server.restapi.change;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.gerrit.server.CommentsUtil.setCommentCommitId;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import static com.google.gerrit.server.permissions.LabelPermission.ForUser.ON_BEHALF_OF;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -29,15 +30,28 @@ import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
+import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.FixReplacement;
+import com.google.gerrit.entities.FixSuggestion;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -61,20 +75,11 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.extensions.validators.CommentForValidation;
+import com.google.gerrit.extensions.validators.CommentValidationFailure;
+import com.google.gerrit.extensions.validators.CommentValidator;
import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.FixReplacement;
-import com.google.gerrit.reviewdb.client.FixSuggestion;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
@@ -107,12 +112,14 @@ import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.LabelPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
+import com.google.gerrit.server.update.CommentsRejectedException;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
@@ -134,18 +141,21 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
@Singleton
public class PostReview
- extends RetryingRestModifyView<RevisionResource, ReviewInput, Response<ReviewResult>> {
+ extends RetryingRestModifyView<RevisionResource, ReviewInput, ReviewResult> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- public static final String ERROR_ADDING_REVIEWER = "error adding reviewer";
+ private static final String ERROR_ADDING_REVIEWER = "error adding reviewer";
public static final String ERROR_WIP_READY_MUTUALLY_EXCLUSIVE =
"work_in_progress and ready are mutually exclusive";
@@ -172,6 +182,7 @@ public class PostReview
private final WorkInProgressOp.Factory workInProgressOpFactory;
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
+ private final PluginSetContext<CommentValidator> commentValidators;
private final boolean strictLabels;
@Inject
@@ -194,7 +205,8 @@ public class PostReview
@GerritServerConfig Config gerritConfig,
WorkInProgressOp.Factory workInProgressOpFactory,
ProjectCache projectCache,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ PluginSetContext<CommentValidator> commentValidators) {
super(retryHelper);
this.changeResourceFactory = changeResourceFactory;
this.changeDataFactory = changeDataFactory;
@@ -214,6 +226,7 @@ public class PostReview
this.workInProgressOpFactory = workInProgressOpFactory;
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
+ this.commentValidators = commentValidators;
this.strictLabels = gerritConfig.getBoolean("change", "strictLabels", false);
}
@@ -236,7 +249,10 @@ public class PostReview
}
ProjectState projectState = projectCache.checkedGet(revision.getProject());
LabelTypes labelTypes = projectState.getLabelTypes(revision.getNotes());
+
input.drafts = firstNonNull(input.drafts, DraftHandling.KEEP);
+ logger.atFine().log("draft handling = %s", input.drafts);
+
if (input.onBehalfOf != null) {
revision = onBehalfOf(revision, labelTypes, input);
}
@@ -244,10 +260,11 @@ public class PostReview
checkLabels(revision, labelTypes, input.labels);
}
if (input.comments != null) {
- cleanUpComments(input.comments);
+ input.comments = cleanUpComments(input.comments);
checkComments(revision, input.comments);
}
if (input.robotComments != null) {
+ input.robotComments = cleanUpComments(input.robotComments);
checkRobotComments(revision, input.robotComments);
}
@@ -357,8 +374,7 @@ public class PostReview
// Add the review op.
bu.addOp(
- revision.getChange().getId(),
- new Op(projectState, revision.getPatchSet().getId(), input));
+ revision.getChange().getId(), new Op(projectState, revision.getPatchSet().id(), input));
// Notify based on ReviewInput, ignoring the notify settings from any AddReviewerInputs.
NotifyResolver.Result notify =
@@ -538,37 +554,30 @@ public class PostReview
}
}
- private static <T extends CommentInput> void cleanUpComments(
+ private static <T extends CommentInput> Map<String, List<T>> cleanUpComments(
Map<String, List<T>> commentsPerPath) {
- Iterator<List<T>> mapValueIterator = commentsPerPath.values().iterator();
- while (mapValueIterator.hasNext()) {
- List<T> comments = mapValueIterator.next();
+ Map<String, List<T>> cleanedUpCommentMap = new HashMap<>();
+ for (Map.Entry<String, List<T>> e : commentsPerPath.entrySet()) {
+ String path = e.getKey();
+ List<T> comments = e.getValue();
+
if (comments == null) {
- mapValueIterator.remove();
continue;
}
- cleanUpComments(comments);
- if (comments.isEmpty()) {
- mapValueIterator.remove();
+ List<T> cleanedUpComments = cleanUpComments(comments);
+ if (!cleanedUpComments.isEmpty()) {
+ cleanedUpCommentMap.put(path, cleanedUpComments);
}
}
+ return cleanedUpCommentMap;
}
- private static <T extends CommentInput> void cleanUpComments(List<T> comments) {
- Iterator<T> commentsIterator = comments.iterator();
- while (commentsIterator.hasNext()) {
- T comment = commentsIterator.next();
- if (comment == null) {
- commentsIterator.remove();
- continue;
- }
-
- comment.message = Strings.nullToEmpty(comment.message).trim();
- if (comment.message.isEmpty()) {
- commentsIterator.remove();
- }
- }
+ private static <T extends CommentInput> List<T> cleanUpComments(List<T> comments) {
+ return comments.stream()
+ .filter(Objects::nonNull)
+ .filter(comment -> !Strings.nullToEmpty(comment.message).trim().isEmpty())
+ .collect(toList());
}
private <T extends CommentInput> void checkComments(
@@ -577,7 +586,7 @@ public class PostReview
Set<String> revisionFilePaths = getAffectedFilePaths(revision);
for (Map.Entry<String, List<T>> entry : commentsPerPath.entrySet()) {
String path = entry.getKey();
- PatchSet.Id patchSetId = revision.getPatchSet().getId();
+ PatchSet.Id patchSetId = revision.getPatchSet().id();
ensurePathRefersToAvailableOrMagicFile(path, revisionFilePaths, patchSetId);
List<T> comments = entry.getValue();
@@ -591,7 +600,7 @@ public class PostReview
private Set<String> getAffectedFilePaths(RevisionResource revision)
throws PatchListNotAvailableException {
- ObjectId newId = ObjectId.fromString(revision.getPatchSet().getRevision().get());
+ ObjectId newId = revision.getPatchSet().commitId();
DiffSummaryKey key =
DiffSummaryKey.fromPatchListKey(
PatchListKey.againstDefaultBase(newId, Whitespace.IGNORE_NONE));
@@ -626,7 +635,6 @@ public class PostReview
private void checkRobotComments(
RevisionResource revision, Map<String, List<RobotCommentInput>> in)
throws BadRequestException, PatchListNotAvailableException {
- cleanUpComments(in);
for (Map.Entry<String, List<RobotCommentInput>> e : in.entrySet()) {
String commentPath = e.getKey();
for (RobotCommentInput c : e.getValue()) {
@@ -799,7 +807,10 @@ public class PostReview
}
}
- /** Used to compare Comments with CommentInput comments. */
+ /**
+ * Used to compare existing {@link Comment}-s with {@link CommentInput} comments by copying only
+ * the fields to compare.
+ */
@AutoValue
abstract static class CommentSetEntry {
private static CommentSetEntry create(
@@ -861,12 +872,11 @@ public class PostReview
@Override
public boolean updateChange(ChangeContext ctx)
throws ResourceConflictException, UnprocessableEntityException, IOException,
- PatchListNotAvailableException {
+ PatchListNotAvailableException, CommentsRejectedException {
user = ctx.getIdentifiedUser();
notes = ctx.getNotes();
ps = psUtil.get(ctx.getNotes(), psId);
- boolean dirty = false;
- dirty |= insertComments(ctx);
+ boolean dirty = insertComments(ctx);
dirty |= insertRobotComments(ctx);
dirty |= updateLabels(projectState, ctx);
dirty |= insertMessage(ctx);
@@ -895,14 +905,19 @@ public class PostReview
}
private boolean insertComments(ChangeContext ctx)
- throws UnprocessableEntityException, PatchListNotAvailableException {
- Map<String, List<CommentInput>> map = in.comments;
- if (map == null) {
- map = Collections.emptyMap();
+ throws UnprocessableEntityException, PatchListNotAvailableException,
+ CommentsRejectedException {
+ Map<String, List<CommentInput>> inputComments = in.comments;
+ if (inputComments == null) {
+ inputComments = Collections.emptyMap();
}
- Map<String, Comment> drafts = Collections.emptyMap();
- if (!map.isEmpty() || in.drafts != DraftHandling.KEEP) {
+ // HashMap instead of Collections.emptyMap() avoids warning about remove() on immutable
+ // object.
+ Map<String, Comment> drafts = new HashMap<>();
+ // If there are inputComments we need the deduplication loop below, so we have to read (and
+ // publish) drafts here.
+ if (!inputComments.isEmpty() || in.drafts != DraftHandling.KEEP) {
if (in.drafts == DraftHandling.PUBLISH_ALL_REVISIONS) {
drafts = changeDrafts(ctx);
} else {
@@ -910,51 +925,84 @@ public class PostReview
}
}
+ // This will be populated with Comment-s created from inputComments.
List<Comment> toPublish = new ArrayList<>();
- Set<CommentSetEntry> existingIds =
+ Set<CommentSetEntry> existingComments =
in.omitDuplicateComments ? readExistingComments(ctx) : Collections.emptySet();
- for (Map.Entry<String, List<CommentInput>> ent : map.entrySet()) {
- String path = ent.getKey();
- for (CommentInput c : ent.getValue()) {
- String parent = Url.decode(c.inReplyTo);
- Comment e = drafts.remove(Url.decode(c.id));
- if (e == null) {
- e = commentsUtil.newComment(ctx, path, psId, c.side(), c.message, c.unresolved, parent);
+ // Deduplication:
+ // - Ignore drafts with the same ID as an inputComment here. These are deleted later.
+ // - Swallow comments that already exist.
+ for (Map.Entry<String, List<CommentInput>> entry : inputComments.entrySet()) {
+ String path = entry.getKey();
+ for (CommentInput inputComment : entry.getValue()) {
+ Comment comment = drafts.remove(Url.decode(inputComment.id));
+ if (comment == null) {
+ String parent = Url.decode(inputComment.inReplyTo);
+ comment =
+ commentsUtil.newComment(
+ ctx,
+ path,
+ psId,
+ inputComment.side(),
+ inputComment.message,
+ inputComment.unresolved,
+ parent);
} else {
- e.writtenOn = ctx.getWhen();
- e.side = c.side();
- e.message = c.message;
+ // In ChangeUpdate#putComment() the draft with the same ID will be deleted.
+ comment.writtenOn = ctx.getWhen();
+ comment.side = inputComment.side();
+ comment.message = inputComment.message;
}
- setCommentRevId(e, patchListCache, ctx.getChange(), ps);
- e.setLineNbrAndRange(c.line, c.range);
- e.tag = in.tag;
+ setCommentCommitId(comment, patchListCache, ctx.getChange(), ps);
+ comment.setLineNbrAndRange(inputComment.line, inputComment.range);
+ comment.tag = in.tag;
- if (existingIds.contains(CommentSetEntry.create(e))) {
+ if (existingComments.contains(CommentSetEntry.create(comment))) {
continue;
}
- toPublish.add(e);
+ toPublish.add(comment);
}
}
switch (in.drafts) {
case PUBLISH:
case PUBLISH_ALL_REVISIONS:
+ validateComments(Streams.concat(drafts.values().stream(), toPublish.stream()));
publishCommentUtil.publish(ctx, psId, drafts.values(), in.tag);
comments.addAll(drafts.values());
break;
case KEEP:
default:
+ validateComments(toPublish.stream());
break;
}
- ChangeUpdate u = ctx.getUpdate(psId);
- commentsUtil.putComments(u, Status.PUBLISHED, toPublish);
+ ChangeUpdate changeUpdate = ctx.getUpdate(psId);
+ commentsUtil.putComments(changeUpdate, Comment.Status.PUBLISHED, toPublish);
comments.addAll(toPublish);
return !toPublish.isEmpty();
}
+ private void validateComments(Stream<Comment> comments) throws CommentsRejectedException {
+ ImmutableList<CommentForValidation> draftsForValidation =
+ comments
+ .map(
+ comment ->
+ CommentForValidation.create(
+ comment.lineNbr > 0
+ ? CommentForValidation.CommentType.INLINE_COMMENT
+ : CommentForValidation.CommentType.FILE_COMMENT,
+ comment.message))
+ .collect(toImmutableList());
+ ImmutableList<CommentValidationFailure> draftValidationFailures =
+ PublishCommentUtil.findInvalidComments(commentValidators, draftsForValidation);
+ if (!draftValidationFailures.isEmpty()) {
+ throw new CommentsRejectedException(draftValidationFailures);
+ }
+ }
+
private boolean insertRobotComments(ChangeContext ctx) throws PatchListNotAvailableException {
if (in.robotComments == null) {
return false;
@@ -1003,7 +1051,7 @@ public class PostReview
robotComment.properties = robotCommentInput.properties;
robotComment.setLineNbrAndRange(robotCommentInput.line, robotCommentInput.range);
robotComment.tag = in.tag;
- setCommentRevId(robotComment, patchListCache, ctx.getChange(), ps);
+ setCommentCommitId(robotComment, patchListCache, ctx.getChange(), ps);
robotComment.fixSuggestions = createFixSuggestionsFromInput(robotCommentInput.fixSuggestions);
return robotComment;
}
@@ -1049,27 +1097,25 @@ public class PostReview
}
private Map<String, Comment> changeDrafts(ChangeContext ctx) {
- Map<String, Comment> drafts = new HashMap<>();
- for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getNotes(), user.getAccountId())) {
- c.tag = in.tag;
- drafts.put(c.key.uuid, c);
- }
- return drafts;
+ return commentsUtil.draftByChangeAuthor(ctx.getNotes(), user.getAccountId()).stream()
+ .collect(
+ Collectors.toMap(
+ c -> c.key.uuid,
+ c -> {
+ c.tag = in.tag;
+ return c;
+ }));
}
private Map<String, Comment> patchSetDrafts(ChangeContext ctx) {
- Map<String, Comment> drafts = new HashMap<>();
- for (Comment c :
- commentsUtil.draftByPatchSetAuthor(psId, user.getAccountId(), ctx.getNotes())) {
- drafts.put(c.key.uuid, c);
- }
- return drafts;
+ return commentsUtil.draftByPatchSetAuthor(psId, user.getAccountId(), ctx.getNotes()).stream()
+ .collect(Collectors.toMap(c -> c.key.uuid, c -> c));
}
private Map<String, Short> approvalsByKey(Collection<PatchSetApproval> patchsetApprovals) {
Map<String, Short> labels = new HashMap<>();
for (PatchSetApproval psa : patchsetApprovals) {
- labels.put(psa.getLabel(), psa.getValue());
+ labels.put(psa.label(), psa.value());
}
return labels;
}
@@ -1106,15 +1152,9 @@ public class PostReview
}
private boolean isReviewer(ChangeContext ctx) {
- if (ctx.getAccountId().equals(ctx.getChange().getOwner())) {
- return true;
- }
ChangeData cd = changeDataFactory.create(ctx.getNotes());
ReviewerSet reviewers = cd.reviewers();
- if (reviewers.byState(REVIEWER).contains(ctx.getAccountId())) {
- return true;
- }
- return false;
+ return reviewers.byState(REVIEWER).contains(ctx.getAccountId());
}
private boolean updateLabels(ProjectState projectState, ChangeContext ctx)
@@ -1149,35 +1189,40 @@ public class PostReview
// User requested delete of this label.
oldApprovals.put(normName, null);
if (c != null) {
- if (c.getValue() != 0) {
+ if (c.value() != 0) {
addLabelDelta(normName, (short) 0);
oldApprovals.put(normName, previous.get(normName));
}
del.add(c);
update.putApproval(normName, (short) 0);
}
- } else if (c != null && c.getValue() != ent.getValue()) {
- c.setValue(ent.getValue());
- c.setGranted(ctx.getWhen());
- c.setTag(in.tag);
- ctx.getUser().updateRealAccountId(c::setRealAccountId);
+ } else if (c != null && c.value() != ent.getValue()) {
+ PatchSetApproval.Builder b =
+ c.toBuilder()
+ .value(ent.getValue())
+ .granted(ctx.getWhen())
+ .tag(Optional.ofNullable(in.tag));
+ ctx.getUser().updateRealAccountId(b::realAccountId);
+ c = b.build();
ups.add(c);
- addLabelDelta(normName, c.getValue());
+ addLabelDelta(normName, c.value());
oldApprovals.put(normName, previous.get(normName));
- approvals.put(normName, c.getValue());
+ approvals.put(normName, c.value());
update.putApproval(normName, ent.getValue());
- } else if (c != null && c.getValue() == ent.getValue()) {
+ } else if (c != null && c.value() == ent.getValue()) {
current.put(normName, c);
oldApprovals.put(normName, null);
- approvals.put(normName, c.getValue());
+ approvals.put(normName, c.value());
} else if (c == null) {
- c = ApprovalsUtil.newApproval(psId, user, lt.getLabelId(), ent.getValue(), ctx.getWhen());
- c.setTag(in.tag);
- c.setGranted(ctx.getWhen());
+ c =
+ ApprovalsUtil.newApproval(psId, user, lt.getLabelId(), ent.getValue(), ctx.getWhen())
+ .tag(Optional.ofNullable(in.tag))
+ .granted(ctx.getWhen())
+ .build();
ups.add(c);
- addLabelDelta(normName, c.getValue());
+ addLabelDelta(normName, c.value());
oldApprovals.put(normName, previous.get(normName));
- approvals.put(normName, c.getValue());
+ approvals.put(normName, c.value());
update.putReviewer(user.getAccountId(), REVIEWER);
update.putApproval(normName, ent.getValue());
}
@@ -1219,7 +1264,7 @@ public class PostReview
List<String> disallowed = new ArrayList<>(labelTypes.getLabelTypes().size());
for (PatchSetApproval psa : del) {
- LabelType lt = requireNonNull(labelTypes.byLabel(psa.getLabel()));
+ LabelType lt = requireNonNull(labelTypes.byLabel(psa.label()));
String normName = lt.getName();
if (!lt.allowPostSubmit()) {
disallowed.add(normName);
@@ -1231,7 +1276,7 @@ public class PostReview
}
for (PatchSetApproval psa : ups) {
- LabelType lt = requireNonNull(labelTypes.byLabel(psa.getLabel()));
+ LabelType lt = requireNonNull(labelTypes.byLabel(psa.label()));
String normName = lt.getName();
if (!lt.allowPostSubmit()) {
disallowed.add(normName);
@@ -1240,8 +1285,8 @@ public class PostReview
if (prev == null) {
continue;
}
- checkState(prev != psa.getValue()); // Should be filtered out above.
- if (prev > psa.getValue()) {
+ checkState(prev != psa.value()); // Should be filtered out above.
+ if (prev > psa.value()) {
reduced.add(psa);
}
// No need to set postSubmit bit, which is set automatically when parsing from NoteDb.
@@ -1256,7 +1301,7 @@ public class PostReview
throw new ResourceConflictException(
"Cannot reduce vote on labels for closed change: "
+ reduced.stream()
- .map(PatchSetApproval::getLabel)
+ .map(PatchSetApproval::label)
.distinct()
.sorted()
.collect(joining(", ")));
@@ -1283,18 +1328,16 @@ public class PostReview
}
LabelId labelId = labelTypes.get(0).getLabelId();
- PatchSetApproval c = ApprovalsUtil.newApproval(psId, user, labelId, 0, ctx.getWhen());
- c.setTag(in.tag);
- c.setGranted(ctx.getWhen());
- ups.add(c);
+ ups.add(
+ ApprovalsUtil.newApproval(psId, user, labelId, 0, ctx.getWhen())
+ .tag(Optional.ofNullable(in.tag))
+ .granted(ctx.getWhen())
+ .build());
} else {
// Pick a random label that is about to be deleted and keep it.
Iterator<PatchSetApproval> i = del.iterator();
- PatchSetApproval c = i.next();
- c.setValue((short) 0);
- c.setGranted(ctx.getWhen());
+ ups.add(i.next().toBuilder().value(0).granted(ctx.getWhen()).build());
i.remove();
- ups.add(c);
}
}
ctx.getUpdate(ctx.getChange().currentPatchSetId()).putReviewer(user.getAccountId(), REVIEWER);
@@ -1317,7 +1360,7 @@ public class PostReview
continue;
}
- LabelType lt = labelTypes.byLabel(a.getLabelId());
+ LabelType lt = labelTypes.byLabel(a.labelId());
if (lt != null) {
current.put(lt.getName(), a);
} else {
@@ -1327,7 +1370,7 @@ public class PostReview
return current;
}
- private boolean insertMessage(ChangeContext ctx) {
+ private boolean insertMessage(ChangeContext ctx) throws CommentsRejectedException {
String msg = Strings.nullToEmpty(in.message).trim();
StringBuilder buf = new StringBuilder();
@@ -1340,6 +1383,15 @@ public class PostReview
buf.append(String.format("\n\n(%d comments)", comments.size()));
}
if (!msg.isEmpty()) {
+ ImmutableList<CommentValidationFailure> messageValidationFailure =
+ PublishCommentUtil.findInvalidComments(
+ commentValidators,
+ ImmutableList.of(
+ CommentForValidation.create(
+ CommentForValidation.CommentType.CHANGE_MESSAGE, msg)));
+ if (!messageValidationFailure.isEmpty()) {
+ throw new CommentsRejectedException(messageValidationFailure);
+ }
buf.append("\n\n").append(msg);
} else if (in.ready) {
buf.append("\n\n" + START_REVIEW_MESSAGE);
diff --git a/java/com/google/gerrit/server/restapi/change/PostReviewers.java b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
index 8abd96402f..f74643ca6c 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
@@ -14,12 +14,13 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.ReviewerAdder;
@@ -59,7 +60,7 @@ public class PostReviewers
}
@Override
- protected AddReviewerResult applyImpl(
+ protected Response<AddReviewerResult> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AddReviewerInput input)
throws IOException, RestApiException, UpdateException, PermissionBackendException,
ConfigInvalidException {
@@ -69,7 +70,7 @@ public class PostReviewers
ReviewerAddition addition = reviewerAdder.prepare(rsrc.getNotes(), rsrc.getUser(), input, true);
if (addition.op == null) {
- return addition.result;
+ return Response.ok(addition.result);
}
try (BatchUpdate bu =
updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
@@ -81,7 +82,7 @@ public class PostReviewers
// Re-read change to take into account results of the update.
addition.gatherResults(changeDataFactory.create(rsrc.getProject(), rsrc.getId()));
- return addition.result;
+ return Response.ok(addition.result);
}
private NotifyResolver.Result resolveNotify(ChangeResource rsrc, AddReviewerInput input)
diff --git a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
index c3cee0e2c4..e6a60d517d 100644
--- a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
+++ b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
@@ -15,17 +15,18 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.PreconditionFailedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.ArchiveFormat;
@@ -80,7 +81,7 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
}
@Override
- public BinaryResult apply(RevisionResource rsrc)
+ public Response<BinaryResult> apply(RevisionResource rsrc)
throws RestApiException, UpdateException, IOException, ConfigInvalidException,
PermissionBackendException {
if (Strings.isNullOrEmpty(format)) {
@@ -105,7 +106,7 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
throw new MethodNotAllowedException("Anonymous users cannot submit");
}
- return getBundles(rsrc, f);
+ return Response.ok(getBundles(rsrc, f));
}
private BinaryResult getBundles(RevisionResource rsrc, ArchiveFormat f)
diff --git a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
index a47037ca78..44f35a0722 100644
--- a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
@@ -39,7 +39,7 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
public class PublishChangeEdit
- extends RetryingRestModifyView<ChangeResource, PublishChangeEditInput, Response<?>> {
+ extends RetryingRestModifyView<ChangeResource, PublishChangeEditInput, Object> {
private final ChangeEditUtil editUtil;
private final NotifyResolver notifyResolver;
private final ContributorAgreementsChecker contributorAgreementsChecker;
@@ -57,7 +57,7 @@ public class PublishChangeEdit
}
@Override
- protected Response<?> applyImpl(
+ protected Response<Object> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in)
throws IOException, RestApiException, UpdateException, ConfigInvalidException,
NoSuchProjectException {
diff --git a/java/com/google/gerrit/server/restapi/change/PutAssignee.java b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
index a9a6f12623..d4a14d47d3 100644
--- a/java/com/google/gerrit/server/restapi/change/PutAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
@@ -22,9 +22,12 @@ import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.change.ChangeResource;
@@ -53,6 +56,7 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
private final ReviewerAdder reviewerAdder;
private final AccountLoader.Factory accountLoaderFactory;
private final PermissionBackend permissionBackend;
+ private final ApprovalsUtil approvalsUtil;
@Inject
PutAssignee(
@@ -61,17 +65,19 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
RetryHelper retryHelper,
ReviewerAdder reviewerAdder,
AccountLoader.Factory accountLoaderFactory,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ ApprovalsUtil approvalsUtil) {
super(retryHelper);
this.accountResolver = accountResolver;
this.assigneeFactory = assigneeFactory;
this.reviewerAdder = reviewerAdder;
this.accountLoaderFactory = accountLoaderFactory;
this.permissionBackend = permissionBackend;
+ this.approvalsUtil = approvalsUtil;
}
@Override
- protected AccountInfo applyImpl(
+ protected Response<AccountInfo> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AssigneeInput input)
throws RestApiException, UpdateException, IOException, PermissionBackendException,
ConfigInvalidException {
@@ -97,12 +103,15 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
SetAssigneeOp op = assigneeFactory.create(assignee);
bu.addOp(rsrc.getId(), op);
- ReviewerAddition reviewersAddition = addAssigneeAsCC(rsrc, input.assignee);
- reviewersAddition.op.suppressEmail();
- bu.addOp(rsrc.getId(), reviewersAddition.op);
+ ReviewerSet currentReviewers = approvalsUtil.getReviewers(rsrc.getNotes());
+ if (!currentReviewers.all().contains(assignee.getAccountId())) {
+ ReviewerAddition reviewersAddition = addAssigneeAsCC(rsrc, input.assignee);
+ reviewersAddition.op.suppressEmail();
+ bu.addOp(rsrc.getId(), reviewersAddition.op);
+ }
bu.execute();
- return accountLoaderFactory.create(true).fillOne(assignee.getAccountId());
+ return Response.ok(accountLoaderFactory.create(true).fillOne(assignee.getAccountId()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PutDescription.java b/java/com/google/gerrit/server/restapi/change/PutDescription.java
index 0ec38e15f0..451d010a6b 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDescription.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.common.DescriptionInput;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.RevisionResource;
@@ -39,7 +39,7 @@ import com.google.inject.Singleton;
@Singleton
public class PutDescription
- extends RetryingRestModifyView<RevisionResource, DescriptionInput, Response<String>>
+ extends RetryingRestModifyView<RevisionResource, DescriptionInput, String>
implements UiAction<RevisionResource> {
private final ChangeMessagesUtil cmUtil;
private final PatchSetUtil psUtil;
@@ -57,7 +57,7 @@ public class PutDescription
throws UpdateException, RestApiException, PermissionBackendException {
rsrc.permissions().check(ChangePermission.EDIT_DESCRIPTION);
- Op op = new Op(input != null ? input : new DescriptionInput(), rsrc.getPatchSet().getId());
+ Op op = new Op(input != null ? input : new DescriptionInput(), rsrc.getPatchSet().id());
try (BatchUpdate u =
updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
u.addOp(rsrc.getChange().getId(), op);
@@ -84,7 +84,7 @@ public class PutDescription
public boolean updateChange(ChangeContext ctx) {
ChangeUpdate update = ctx.getUpdate(psId);
newDescription = Strings.nullToEmpty(input.description);
- oldDescription = Strings.nullToEmpty(psUtil.get(ctx.getNotes(), psId).getDescription());
+ oldDescription = psUtil.get(ctx.getNotes(), psId).description().orElse("");
if (oldDescription.equals(newDescription)) {
return false;
}
diff --git a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index f6c9abe65d..5696fcbf6e 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -14,8 +14,10 @@
package com.google.gerrit.server.restapi.change;
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentCommitId;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -23,9 +25,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.DraftCommentResource;
@@ -49,7 +48,7 @@ import java.util.Optional;
@Singleton
public class PutDraftComment
- extends RetryingRestModifyView<DraftCommentResource, DraftInput, Response<CommentInfo>> {
+ extends RetryingRestModifyView<DraftCommentResource, DraftInput, CommentInfo> {
private final DeleteDraftComment delete;
private final CommentsUtil commentsUtil;
@@ -124,7 +123,7 @@ public class PutDraftComment
// user.
ctx.getUser().updateRealAccountId(comment::setRealAuthor);
- PatchSet.Id psId = new PatchSet.Id(ctx.getChange().getId(), origComment.key.patchSetId);
+ PatchSet.Id psId = PatchSet.id(ctx.getChange().getId(), origComment.key.patchSetId);
ChangeUpdate update = ctx.getUpdate(psId);
PatchSet ps = psUtil.get(ctx.getNotes(), psId);
@@ -138,9 +137,9 @@ public class PutDraftComment
commentsUtil.deleteComments(update, Collections.singleton(origComment));
comment.key.filename = in.path;
}
- setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
+ setCommentCommitId(comment, patchListCache, ctx.getChange(), ps);
commentsUtil.putComments(
- update, Status.DRAFT, Collections.singleton(update(comment, in, ctx.getWhen())));
+ update, Comment.Status.DRAFT, Collections.singleton(update(comment, in, ctx.getWhen())));
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PutMessage.java b/java/com/google/gerrit/server/restapi/change/PutMessage.java
index c542164a32..acda54722f 100644
--- a/java/com/google/gerrit/server/restapi/change/PutMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/PutMessage.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.common.FooterConstants;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.CommitMessageInput;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -22,8 +24,6 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -61,8 +61,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@Singleton
-public class PutMessage
- extends RetryingRestModifyView<ChangeResource, CommitMessageInput, Response<?>> {
+public class PutMessage extends RetryingRestModifyView<ChangeResource, CommitMessageInput, String> {
private final GitRepositoryManager repositoryManager;
private final Provider<CurrentUser> userProvider;
@@ -111,15 +110,18 @@ public class PutMessage
String sanitizedCommitMessage = CommitMessageUtil.checkAndSanitizeCommitMessage(input.message);
ensureCanEditCommitMessage(resource.getNotes());
- ensureChangeIdIsCorrect(
- projectCache.checkedGet(resource.getProject()).is(BooleanProjectConfig.REQUIRE_CHANGE_ID),
- resource.getChange().getKey().get(),
- sanitizedCommitMessage);
+ sanitizedCommitMessage =
+ ensureChangeIdIsCorrect(
+ projectCache
+ .checkedGet(resource.getProject())
+ .is(BooleanProjectConfig.REQUIRE_CHANGE_ID),
+ resource.getChange().getKey().get(),
+ sanitizedCommitMessage);
try (Repository repository = repositoryManager.openRepository(resource.getProject());
RevWalk revWalk = new RevWalk(repository);
ObjectInserter objectInserter = repository.newObjectInserter()) {
- RevCommit patchSetCommit = revWalk.parseCommit(ObjectId.fromString(ps.getRevision().get()));
+ RevCommit patchSetCommit = revWalk.parseCommit(ps.commitId());
String currentCommitMessage = patchSetCommit.getFullMessage();
if (input.message.equals(currentCommitMessage)) {
@@ -132,7 +134,7 @@ public class PutMessage
// Ensure that BatchUpdate will update the same repo
bu.setRepository(repository, new RevWalk(objectInserter.newReader()), objectInserter);
- PatchSet.Id psId = ChangeUtil.nextPatchSetId(repository, ps.getId());
+ PatchSet.Id psId = ChangeUtil.nextPatchSetId(repository, ps.id());
ObjectId newCommit =
createCommit(objectInserter, patchSetCommit, sanitizedCommitMessage, ts);
PatchSetInserter inserter = psInserterFactory.create(resource.getNotes(), psId, newCommit);
@@ -193,7 +195,7 @@ public class PutMessage
}
}
- private static void ensureChangeIdIsCorrect(
+ private static String ensureChangeIdIsCorrect(
boolean requireChangeId, String currentChangeId, String newCommitMessage)
throws ResourceConflictException, BadRequestException {
RevCommit revCommit =
@@ -204,14 +206,21 @@ public class PutMessage
CommitMessageUtil.checkAndSanitizeCommitMessage(revCommit.getShortMessage());
List<String> changeIdFooters = revCommit.getFooterLines(FooterConstants.CHANGE_ID);
- if (requireChangeId && changeIdFooters.isEmpty()) {
- throw new ResourceConflictException("missing Change-Id footer");
- }
if (!changeIdFooters.isEmpty() && !changeIdFooters.get(0).equals(currentChangeId)) {
throw new ResourceConflictException("wrong Change-Id footer");
}
- if (changeIdFooters.size() > 1) {
+
+ if (requireChangeId && revCommit.getFooterLines().isEmpty()) {
+ // sanitization always adds '\n' at the end.
+ newCommitMessage += "\n";
+ }
+
+ if (requireChangeId && changeIdFooters.isEmpty()) {
+ newCommitMessage += FooterConstants.CHANGE_ID.getName() + ": " + currentChangeId + "\n";
+ } else if (changeIdFooters.size() > 1) {
throw new ResourceConflictException("multiple Change-Id footers");
}
+
+ return newCommitMessage;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PutTopic.java b/java/com/google/gerrit/server/restapi/change/PutTopic.java
index abfa49ba92..cfeb8845ff 100644
--- a/java/com/google/gerrit/server/restapi/change/PutTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/PutTopic.java
@@ -15,13 +15,13 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.extensions.api.changes.TopicInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.ChangeResource;
@@ -41,7 +41,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
@Singleton
-public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput, Response<String>>
+public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput, String>
implements UiAction<ChangeResource> {
private final ChangeMessagesUtil cmUtil;
private final TopicEdited topicEdited;
diff --git a/java/com/google/gerrit/server/restapi/change/QueryChanges.java b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
index e1ed8d64fa..6e5f554304 100644
--- a/java/com/google/gerrit/server/restapi/change/QueryChanges.java
+++ b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
@@ -21,6 +21,7 @@ import com.google.gerrit.extensions.client.ListOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.index.query.QueryParseException;
@@ -113,7 +114,7 @@ public class QueryChanges implements RestReadView<TopLevelResource>, DynamicOpti
}
@Override
- public List<?> apply(TopLevelResource rsrc)
+ public Response<List<?>> apply(TopLevelResource rsrc)
throws BadRequestException, AuthException, PermissionBackendException {
List<List<ChangeInfo>> out;
try {
@@ -124,7 +125,7 @@ public class QueryChanges implements RestReadView<TopLevelResource>, DynamicOpti
logger.atFine().withCause(e).log("Reject change query with 400 Bad Request: %s", queries);
throw new BadRequestException(e.getMessage(), e);
}
- return out.size() == 1 ? out.get(0) : out;
+ return Response.ok(out.size() == 1 ? out.get(0) : out);
}
private List<List<ChangeInfo>> query() throws QueryParseException, PermissionBackendException {
diff --git a/java/com/google/gerrit/server/restapi/change/Rebase.java b/java/com/google/gerrit/server/restapi/change/Rebase.java
index 782b91aa08..af8f971b0d 100644
--- a/java/com/google/gerrit/server/restapi/change/Rebase.java
+++ b/java/com/google/gerrit/server/restapi/change/Rebase.java
@@ -17,19 +17,20 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.RebaseInput;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeJson;
@@ -98,7 +99,7 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
}
@Override
- protected ChangeInfo applyImpl(
+ protected Response<ChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, RevisionResource rsrc, RebaseInput input)
throws UpdateException, RestApiException, IOException, PermissionBackendException {
// Not allowed to rebase if the current patch set is locked.
@@ -131,14 +132,14 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
.setFireRevisionCreated(true));
bu.execute();
}
- return json.create(OPTIONS).format(change.getProject(), change.getId());
+ return Response.ok(json.create(OPTIONS).format(change.getProject(), change.getId()));
}
private ObjectId findBaseRev(
Repository repo, RevWalk rw, RevisionResource rsrc, RebaseInput input)
throws RestApiException, IOException, NoSuchChangeException, AuthException,
PermissionBackendException {
- Branch.NameKey destRefKey = rsrc.getChange().getDest();
+ BranchNameKey destRefKey = rsrc.getChange().getDest();
if (input == null || input.base == null) {
return rebaseUtil.findBaseRevision(rsrc.getPatchSet(), destRefKey, repo, rw);
}
@@ -147,10 +148,10 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
String str = input.base.trim();
if (str.equals("")) {
// Remove existing dependency to other patch set.
- Ref destRef = repo.exactRef(destRefKey.get());
+ Ref destRef = repo.exactRef(destRefKey.branch());
if (destRef == null) {
throw new ResourceConflictException(
- "can't rebase onto tip of branch " + destRefKey.get() + "; branch doesn't exist");
+ "can't rebase onto tip of branch " + destRefKey.branch() + "; branch doesn't exist");
}
return destRef.getObjectId();
}
@@ -167,8 +168,8 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
String.format("Base change not found: %s", input.base), e);
}
- PatchSet.Id baseId = base.patchSet().getId();
- if (change.getId().equals(baseId.getParentKey())) {
+ PatchSet.Id baseId = base.patchSet().id();
+ if (change.getId().equals(baseId.changeId())) {
throw new ResourceConflictException("cannot rebase change onto itself");
}
@@ -189,18 +190,18 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
+ baseChange.getKey()
+ " is a descendant of the current change - recursion not allowed");
}
- return ObjectId.fromString(base.patchSet().getRevision().get());
+ return base.patchSet().commitId();
}
private boolean isMergedInto(RevWalk rw, PatchSet base, PatchSet tip) throws IOException {
- ObjectId baseId = ObjectId.fromString(base.getRevision().get());
- ObjectId tipId = ObjectId.fromString(tip.getRevision().get());
+ ObjectId baseId = base.commitId();
+ ObjectId tipId = tip.commitId();
return rw.isMergedInto(rw.parseCommit(baseId), rw.parseCommit(tipId));
}
private boolean hasOneParent(RevWalk rw, PatchSet ps) throws IOException {
// Prevent rebase of exotic changes (merge commit, no ancestor).
- RevCommit c = rw.parseCommit(ObjectId.fromString(ps.getRevision().get()));
+ RevCommit c = rw.parseCommit(ps.commitId());
return c.getParentCount() == 1;
}
@@ -238,7 +239,7 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
}
boolean enabled = false;
- try (Repository repo = repoManager.openRepository(change.getDest().getParentKey());
+ try (Repository repo = repoManager.openRepository(change.getDest().project());
RevWalk rw = new RevWalk(repo)) {
if (hasOneParent(rw, rsrc.getPatchSet())) {
enabled = rebaseUtil.canRebase(rsrc.getPatchSet(), change.getDest(), repo, rw);
@@ -272,14 +273,15 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
}
@Override
- protected ChangeInfo applyImpl(
+ protected Response<ChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, RebaseInput input)
- throws UpdateException, RestApiException, IOException, PermissionBackendException {
+ throws Exception {
PatchSet ps = psUtil.current(rsrc.getNotes());
if (ps == null) {
throw new ResourceConflictException("current revision is missing");
}
- return rebase.applyImpl(updateFactory, new RevisionResource(rsrc, ps), input);
+ return Response.ok(
+ rebase.applyImpl(updateFactory, new RevisionResource(rsrc, ps), input).value());
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
index 81294edf5c..7be8765b90 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.edit.ChangeEditModifier;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -33,7 +33,7 @@ import java.io.IOException;
import org.eclipse.jgit.lib.Repository;
@Singleton
-public class RebaseChangeEdit extends RetryingRestModifyView<ChangeResource, Input, Response<?>> {
+public class RebaseChangeEdit extends RetryingRestModifyView<ChangeResource, Input, Object> {
private final GitRepositoryManager repositoryManager;
private final ChangeEditModifier editModifier;
@@ -48,7 +48,8 @@ public class RebaseChangeEdit extends RetryingRestModifyView<ChangeResource, Inp
}
@Override
- protected Response<?> applyImpl(BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input in)
+ protected Response<Object> applyImpl(
+ BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input in)
throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
Project.NameKey project = rsrc.getProject();
try (Repository repository = repositoryManager.openRepository(project)) {
diff --git a/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
index 1421ab6774..8040847e72 100644
--- a/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
+++ b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
@@ -24,10 +24,10 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.MultimapBuilder;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -74,8 +74,8 @@ class RelatedChangesSorter {
throws IOException, PermissionBackendException {
checkArgument(!in.isEmpty(), "Input may not be empty");
// Map of all patch sets, keyed by commit SHA-1.
- Map<String, PatchSetData> byId = collectById(in);
- PatchSetData start = byId.get(startPs.getRevision().get());
+ Map<ObjectId, PatchSetData> byId = collectById(in);
+ PatchSetData start = byId.get(startPs.commitId());
checkArgument(start != null, "%s not found in %s", startPs, in);
// Map of patch set -> immediate parent.
@@ -89,12 +89,12 @@ class RelatedChangesSorter {
for (ChangeData cd : in) {
for (PatchSet ps : cd.patchSets()) {
- PatchSetData thisPsd = requireNonNull(byId.get(ps.getRevision().get()));
- if (cd.getId().equals(start.id()) && !ps.getId().equals(start.psId())) {
+ PatchSetData thisPsd = requireNonNull(byId.get(ps.commitId()));
+ if (cd.getId().equals(start.id()) && !ps.id().equals(start.psId())) {
otherPatchSetsOfStart.add(thisPsd);
}
for (RevCommit p : thisPsd.commit().getParents()) {
- PatchSetData parentPsd = byId.get(p.name());
+ PatchSetData parentPsd = byId.get(p);
if (parentPsd != null) {
parents.put(thisPsd, parentPsd);
children.put(parentPsd, thisPsd);
@@ -112,9 +112,9 @@ class RelatedChangesSorter {
return result;
}
- private Map<String, PatchSetData> collectById(List<ChangeData> in) throws IOException {
+ private Map<ObjectId, PatchSetData> collectById(List<ChangeData> in) throws IOException {
Project.NameKey project = in.get(0).change().getProject();
- Map<String, PatchSetData> result = Maps.newHashMapWithExpectedSize(in.size() * 3);
+ Map<ObjectId, PatchSetData> result = Maps.newHashMapWithExpectedSize(in.size() * 3);
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
rw.setRetainBody(true);
@@ -126,10 +126,9 @@ class RelatedChangesSorter {
project,
cd.change().getProject());
for (PatchSet ps : cd.patchSets()) {
- String id = ps.getRevision().get();
- RevCommit c = rw.parseCommit(ObjectId.fromString(id));
+ RevCommit c = rw.parseCommit(ps.commitId());
PatchSetData psd = PatchSetData.create(cd, ps, c);
- result.put(id, psd);
+ result.put(ps.commitId(), psd);
}
}
}
@@ -252,16 +251,16 @@ class RelatedChangesSorter {
abstract RevCommit commit();
PatchSet.Id psId() {
- return patchSet().getId();
+ return patchSet().id();
}
Change.Id id() {
- return psId().getParentKey();
+ return psId().changeId();
}
@Override
public final int hashCode() {
- return Objects.hash(patchSet().getId(), commit());
+ return Objects.hash(patchSet().id(), commit());
}
@Override
@@ -270,7 +269,7 @@ class RelatedChangesSorter {
return false;
}
PatchSetData o = (PatchSetData) obj;
- return Objects.equals(patchSet().getId(), o.patchSet().getId())
+ return Objects.equals(patchSet().id(), o.patchSet().id())
&& Objects.equals(commit(), o.commit());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Restore.java b/java/com/google/gerrit/server/restapi/change/Restore.java
index 5f56cdb1d4..679d4f840c 100644
--- a/java/com/google/gerrit/server/restapi/change/Restore.java
+++ b/java/com/google/gerrit/server/restapi/change/Restore.java
@@ -16,16 +16,17 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Change.Status;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.RestoreInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Status;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PatchSetUtil;
@@ -81,7 +82,7 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
}
@Override
- protected ChangeInfo applyImpl(
+ protected Response<ChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, RestoreInput input)
throws RestApiException, UpdateException, PermissionBackendException, IOException {
// Not allowed to restore if the current patch set is locked.
@@ -95,7 +96,7 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
u.addOp(rsrc.getId(), op).execute();
}
- return json.noOptions().format(op.change);
+ return Response.ok(json.noOptions().format(op.change));
}
private class Op implements BatchUpdateOp {
diff --git a/java/com/google/gerrit/server/restapi/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java
index dd51e7f28b..e196abced6 100644
--- a/java/com/google/gerrit/server/restapi/change/Revert.java
+++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -18,33 +18,32 @@ import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
-import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.RevertInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeJson;
-import com.google.gerrit.server.change.ChangeMessages;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.extensions.events.ChangeReverted;
+import com.google.gerrit.server.git.CommitUtil;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.mail.send.RevertedSender;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -66,24 +65,19 @@ import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.sql.Timestamp;
-import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.util.ChangeIdUtil;
@Singleton
public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput, ChangeInfo>
@@ -98,12 +92,12 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
private final PatchSetUtil psUtil;
private final RevertedSender.Factory revertedSenderFactory;
private final ChangeJson.Factory json;
- private final Provider<PersonIdent> serverIdent;
private final ApprovalsUtil approvalsUtil;
private final ChangeReverted changeReverted;
private final ContributorAgreementsChecker contributorAgreements;
private final ProjectCache projectCache;
private final NotifyResolver notifyResolver;
+ private final CommitUtil commitUtil;
@Inject
Revert(
@@ -116,12 +110,12 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
PatchSetUtil psUtil,
RevertedSender.Factory revertedSenderFactory,
ChangeJson.Factory json,
- @GerritPersonIdent Provider<PersonIdent> serverIdent,
ApprovalsUtil approvalsUtil,
ChangeReverted changeReverted,
ContributorAgreementsChecker contributorAgreements,
ProjectCache projectCache,
- NotifyResolver notifyResolver) {
+ NotifyResolver notifyResolver,
+ CommitUtil commitUtil) {
super(retryHelper);
this.permissionBackend = permissionBackend;
this.repoManager = repoManager;
@@ -131,16 +125,16 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
this.psUtil = psUtil;
this.revertedSenderFactory = revertedSenderFactory;
this.json = json;
- this.serverIdent = serverIdent;
this.approvalsUtil = approvalsUtil;
this.changeReverted = changeReverted;
this.contributorAgreements = contributorAgreements;
this.projectCache = projectCache;
this.notifyResolver = notifyResolver;
+ this.commitUtil = commitUtil;
}
@Override
- public ChangeInfo applyImpl(
+ public Response<ChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, RevertInput input)
throws IOException, RestApiException, UpdateException, NoSuchChangeException,
PermissionBackendException, NoSuchProjectException, ConfigInvalidException {
@@ -154,13 +148,12 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
projectCache.checkedGet(rsrc.getProject()).checkStatePermitsWrite();
Change.Id revertId = revert(updateFactory, rsrc.getNotes(), rsrc.getUser(), input);
- return json.noOptions().format(rsrc.getProject(), revertId);
+ return Response.ok(json.noOptions().format(rsrc.getProject(), revertId));
}
private Change.Id revert(
BatchUpdate.Factory updateFactory, ChangeNotes notes, CurrentUser user, RevertInput input)
throws IOException, RestApiException, UpdateException, ConfigInvalidException {
- String message = Strings.emptyToNull(input.message);
Change.Id changeIdToRevert = notes.getChangeId();
PatchSet.Id patchSetId = notes.getChange().currentPatchSetId();
PatchSet patch = psUtil.get(notes, patchSetId);
@@ -173,50 +166,25 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
RevWalk revWalk = new RevWalk(reader)) {
- RevCommit commitToRevert =
- revWalk.parseCommit(ObjectId.fromString(patch.getRevision().get()));
- if (commitToRevert.getParentCount() == 0) {
- throw new ResourceConflictException("Cannot revert initial commit");
- }
Timestamp now = TimeUtil.nowTs();
- PersonIdent committerIdent = serverIdent.get();
- PersonIdent authorIdent =
- user.asIdentifiedUser().newCommitterIdent(now, committerIdent.getTimeZone());
-
- RevCommit parentToCommitToRevert = commitToRevert.getParent(0);
- revWalk.parseHeaders(parentToCommitToRevert);
-
- CommitBuilder revertCommitBuilder = new CommitBuilder();
- revertCommitBuilder.addParentId(commitToRevert);
- revertCommitBuilder.setTreeId(parentToCommitToRevert.getTree());
- revertCommitBuilder.setAuthor(authorIdent);
- revertCommitBuilder.setCommitter(authorIdent);
-
- Change changeToRevert = notes.getChange();
- if (message == null) {
- message =
- MessageFormat.format(
- ChangeMessages.get().revertChangeDefaultMessage,
- changeToRevert.getSubject(),
- patch.getRevision().get());
- }
-
ObjectId generatedChangeId = CommitMessageUtil.generateChangeId();
- revertCommitBuilder.setMessage(ChangeIdUtil.insertId(message, generatedChangeId, true));
+ Change changeToRevert = notes.getChange();
+ ObjectId revertCommitId =
+ commitUtil.createRevertCommit(
+ input.message, notes, user, generatedChangeId, now, oi, revWalk);
- Change.Id changeId = new Change.Id(seq.nextChangeId());
- ObjectId id = oi.insert(revertCommitBuilder);
- RevCommit revertCommit = revWalk.parseCommit(id);
+ RevCommit revertCommit = revWalk.parseCommit(revertCommitId);
+ Change.Id changeId = Change.id(seq.nextChangeId());
NotifyResolver.Result notify =
notifyResolver.resolve(
firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails);
ChangeInserter ins =
changeInserterFactory
- .create(changeId, revertCommit, notes.getChange().getDest().get())
- .setTopic(changeToRevert.getTopic());
+ .create(changeId, revertCommit, notes.getChange().getDest().branch())
+ .setTopic(input.topic == null ? changeToRevert.getTopic() : input.topic.trim());
ins.setMessage("Uploaded patch set 1.");
ReviewerSet reviewerSet = approvalsUtil.getReviewers(notes);
diff --git a/java/com/google/gerrit/server/restapi/change/Reviewed.java b/java/com/google/gerrit/server/restapi/change/Reviewed.java
index 4594503520..7152799f61 100644
--- a/java/com/google/gerrit/server/restapi/change/Reviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewed.java
@@ -40,9 +40,9 @@ public class Reviewed {
accountPatchReviewStore.call(
s ->
s.markReviewed(
- resource.getPatchKey().getParentKey(),
+ resource.getPatchKey().patchSetId(),
resource.getAccountId(),
- resource.getPatchKey().getFileName()));
+ resource.getPatchKey().fileName()));
return reviewFlagUpdated ? Response.created("") : Response.ok("");
}
}
@@ -61,9 +61,9 @@ public class Reviewed {
accountPatchReviewStore.run(
s ->
s.clearReviewed(
- resource.getPatchKey().getParentKey(),
+ resource.getPatchKey().patchSetId(),
resource.getAccountId(),
- resource.getPatchKey().getFileName()));
+ resource.getPatchKey().fileName()));
return Response.none();
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
index 92f7185696..f07d8153a3 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.restapi.change;
-import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
@@ -22,18 +21,20 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.FanOutExecutor;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.change.ReviewerSuggestion;
import com.google.gerrit.server.change.SuggestedReviewer;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.plugincontext.PluginMapContext;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
@@ -44,11 +45,11 @@ import com.google.inject.Provider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
@@ -63,13 +64,6 @@ import org.eclipse.jgit.lib.Config;
public class ReviewerRecommender {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private static final double BASE_REVIEWER_WEIGHT = 10;
- private static final double BASE_OWNER_WEIGHT = 1;
- private static final double BASE_COMMENT_WEIGHT = 0.5;
- private static final double[] WEIGHTS =
- new double[] {
- BASE_REVIEWER_WEIGHT, BASE_OWNER_WEIGHT, BASE_COMMENT_WEIGHT,
- };
private static final long PLUGIN_QUERY_TIMEOUT = 500; // ms
private final ChangeQueryBuilder changeQueryBuilder;
@@ -78,6 +72,7 @@ public class ReviewerRecommender {
private final Provider<InternalChangeQuery> queryProvider;
private final ExecutorService executor;
private final ApprovalsUtil approvalsUtil;
+ private final AccountCache accountCache;
@Inject
ReviewerRecommender(
@@ -86,16 +81,19 @@ public class ReviewerRecommender {
Provider<InternalChangeQuery> queryProvider,
@FanOutExecutor ExecutorService executor,
ApprovalsUtil approvalsUtil,
- @GerritServerConfig Config config) {
+ @GerritServerConfig Config config,
+ AccountCache accountCache) {
this.changeQueryBuilder = changeQueryBuilder;
this.config = config;
this.queryProvider = queryProvider;
this.reviewerSuggestionPluginMap = reviewerSuggestionPluginMap;
this.executor = executor;
this.approvalsUtil = approvalsUtil;
+ this.accountCache = accountCache;
}
public List<Account.Id> suggestReviewers(
+ ReviewerState reviewerState,
@Nullable ChangeNotes changeNotes,
SuggestReviewers suggestReviewers,
ProjectState projectState,
@@ -109,12 +107,7 @@ public class ReviewerRecommender {
double baseWeight = config.getInt("addReviewer", "baseWeight", 1);
logger.atFine().log("base weight: %s", baseWeight);
- Map<Account.Id, MutableDouble> reviewerScores;
- if (Strings.isNullOrEmpty(query)) {
- reviewerScores = baseRankingForEmptyQuery(baseWeight);
- } else {
- reviewerScores = baseRankingForCandidateList(candidateList, projectState, baseWeight);
- }
+ Map<Account.Id, MutableDouble> reviewerScores = baseRanking(baseWeight, query, candidateList);
logger.atFine().log("Base reviewer scores: %s", reviewerScores);
// Send the query along with a candidate list to all plugins and merge the
@@ -178,7 +171,7 @@ public class ReviewerRecommender {
// Remove existing reviewers
approvalsUtil
.getReviewers(changeNotes)
- .byState(REVIEWER)
+ .byState(ReviewerStateInternal.fromReviewerState(reviewerState))
.forEach(
r -> {
if (reviewerScores.remove(r) != null) {
@@ -196,7 +189,18 @@ public class ReviewerRecommender {
return sortedSuggestions;
}
- private Map<Account.Id, MutableDouble> baseRankingForEmptyQuery(double baseWeight)
+ /**
+ * @param baseWeight The weight applied to the ordering of the reviewers.
+ * @param query Query to match. For example, it can try to match all users that start with "Ab".
+ * @param candidateList The list of candidates based on the query. If query is empty, this list is
+ * also empty.
+ * @return Map of account ids that match the query and their appropriate ranking (the better the
+ * ranking, the better it is to suggest them as reviewers).
+ * @throws IOException Can't find owner="self" account.
+ * @throws ConfigInvalidException Can't find owner="self" account.
+ */
+ private Map<Account.Id, MutableDouble> baseRanking(
+ double baseWeight, String query, List<Account.Id> candidateList)
throws IOException, ConfigInvalidException {
// Get the user's last 25 changes, check approvals
try {
@@ -206,14 +210,15 @@ public class ReviewerRecommender {
.setLimit(25)
.setRequestedFields(ChangeField.APPROVAL)
.query(changeQueryBuilder.owner("self"));
- Map<Account.Id, MutableDouble> suggestions = new HashMap<>();
+ Map<Account.Id, MutableDouble> suggestions = new LinkedHashMap<>();
+ // Put those candidates at the bottom of the list
+ candidateList.stream().forEach(id -> suggestions.put(id, new MutableDouble(0)));
+
for (ChangeData cd : result) {
for (PatchSetApproval approval : cd.currentApprovals()) {
- Account.Id id = approval.getAccountId();
- if (suggestions.containsKey(id)) {
- suggestions.get(id).add(baseWeight);
- } else {
- suggestions.put(id, new MutableDouble(baseWeight));
+ Account.Id id = approval.accountId();
+ if (Strings.isNullOrEmpty(query) || accountMatchesQuery(id, query)) {
+ suggestions.computeIfAbsent(id, (ignored) -> new MutableDouble(0)).add(baseWeight);
}
}
}
@@ -225,63 +230,15 @@ public class ReviewerRecommender {
}
}
- private Map<Account.Id, MutableDouble> baseRankingForCandidateList(
- List<Account.Id> candidates, ProjectState projectState, double baseWeight)
- throws IOException, ConfigInvalidException {
- // Get each reviewer's activity based on number of applied labels
- // (weighted 10d), number of comments (weighted 0.5d) and number of owned
- // changes (weighted 1d).
- Map<Account.Id, MutableDouble> reviewers = new LinkedHashMap<>();
- if (candidates.isEmpty()) {
- return reviewers;
- }
- List<Predicate<ChangeData>> predicates = new ArrayList<>();
- for (Account.Id id : candidates) {
- try {
- Predicate<ChangeData> projectQuery = changeQueryBuilder.project(projectState.getName());
-
- // Get all labels for this project and create a compound OR query to
- // fetch all changes where users have applied one of these labels
- List<LabelType> labelTypes = projectState.getLabelTypes().getLabelTypes();
- List<Predicate<ChangeData>> labelPredicates = new ArrayList<>(labelTypes.size());
- for (LabelType type : labelTypes) {
- labelPredicates.add(changeQueryBuilder.label(type.getName() + ",user=" + id));
- }
- Predicate<ChangeData> reviewerQuery =
- Predicate.and(projectQuery, Predicate.or(labelPredicates));
-
- Predicate<ChangeData> ownerQuery =
- Predicate.and(projectQuery, changeQueryBuilder.owner(id.toString()));
- Predicate<ChangeData> commentedByQuery =
- Predicate.and(projectQuery, changeQueryBuilder.commentby(id.toString()));
-
- predicates.add(reviewerQuery);
- predicates.add(ownerQuery);
- predicates.add(commentedByQuery);
- reviewers.put(id, new MutableDouble());
- } catch (QueryParseException e) {
- // Unhandled: If an exception is thrown, we won't increase the
- // candidates's score
- logger.atSevere().withCause(e).log("Exception while suggesting reviewers");
+ private boolean accountMatchesQuery(Account.Id id, String query) {
+ Optional<Account> account = accountCache.get(id).map(AccountState::account);
+ if (account.isPresent() && account.get().isActive()) {
+ if ((account.get().fullName() != null && account.get().fullName().startsWith(query))
+ || (account.get().preferredEmail() != null
+ && account.get().preferredEmail().startsWith(query))) {
+ return true;
}
}
-
- List<List<ChangeData>> result = queryProvider.get().setLimit(25).noFields().query(predicates);
-
- Iterator<List<ChangeData>> queryResultIterator = result.iterator();
- Iterator<Account.Id> reviewersIterator = reviewers.keySet().iterator();
-
- int i = 0;
- Account.Id currentId = null;
- while (queryResultIterator.hasNext()) {
- List<ChangeData> currentResult = queryResultIterator.next();
- if (i % WEIGHTS.length == 0) {
- currentId = reviewersIterator.next();
- }
-
- reviewers.get(currentId).add(WEIGHTS[i % WEIGHTS.length] * baseWeight * currentResult.size());
- i++;
- }
- return reviewers;
+ return false;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Reviewers.java b/java/com/google/gerrit/server/restapi/change/Reviewers.java
index 546ca0168d..b2714da7c9 100644
--- a/java/com/google/gerrit/server/restapi/change/Reviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewers.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -22,7 +23,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ReviewerResource;
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index f5907e76e6..676cc07c1f 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -24,21 +24,26 @@ import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.GroupBaseInfo;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.ResultSet;
+import com.google.gerrit.index.query.TooManyTermsInQueryException;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer0;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountDirectory.FillOptions;
@@ -49,6 +54,7 @@ import com.google.gerrit.server.account.GroupMembers;
import com.google.gerrit.server.change.ReviewerAdder;
import com.google.gerrit.server.index.account.AccountField;
import com.google.gerrit.server.index.account.AccountIndexCollection;
+import com.google.gerrit.server.index.account.AccountIndexRewriter;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.NoSuchProjectException;
@@ -113,12 +119,9 @@ public class ReviewersUtil {
}
}
- // Generate a candidate list at 3x the size of what the user wants to see to
- // give the ranking algorithm a good set of candidates it can work with
- private static final int CANDIDATE_LIST_MULTIPLIER = 3;
-
private final AccountLoader.Factory accountLoaderFactory;
private final AccountQueryBuilder accountQueryBuilder;
+ private final AccountIndexRewriter accountIndexRewriter;
private final GroupBackend groupBackend;
private final GroupMembers groupMembers;
private final ReviewerRecommender reviewerRecommender;
@@ -132,6 +135,7 @@ public class ReviewersUtil {
ReviewersUtil(
AccountLoader.Factory accountLoaderFactory,
AccountQueryBuilder accountQueryBuilder,
+ AccountIndexRewriter accountIndexRewriter,
GroupBackend groupBackend,
GroupMembers groupMembers,
ReviewerRecommender reviewerRecommender,
@@ -142,6 +146,7 @@ public class ReviewersUtil {
Provider<CurrentUser> self) {
this.accountLoaderFactory = accountLoaderFactory;
this.accountQueryBuilder = accountQueryBuilder;
+ this.accountIndexRewriter = accountIndexRewriter;
this.groupBackend = groupBackend;
this.groupMembers = groupMembers;
this.reviewerRecommender = reviewerRecommender;
@@ -157,12 +162,13 @@ public class ReviewersUtil {
}
public List<SuggestedReviewerInfo> suggestReviewers(
+ ReviewerState reviewerState,
@Nullable ChangeNotes changeNotes,
SuggestReviewers suggestReviewers,
ProjectState projectState,
VisibilityControl visibilityControl,
boolean excludeGroups)
- throws IOException, ConfigInvalidException, PermissionBackendException {
+ throws IOException, ConfigInvalidException, PermissionBackendException, BadRequestException {
CurrentUser currentUser = self.get();
if (changeNotes != null) {
logger.atFine().log(
@@ -190,7 +196,8 @@ public class ReviewersUtil {
}
List<Account.Id> sortedRecommendations =
- recommendAccounts(changeNotes, suggestReviewers, projectState, candidateList);
+ recommendAccounts(
+ reviewerState, changeNotes, suggestReviewers, projectState, candidateList);
logger.atFine().log("Sorted recommendations: %s", sortedRecommendations);
// Filter accounts by visibility and enforce limit
@@ -221,37 +228,59 @@ public class ReviewersUtil {
return suggestedReviewers;
}
- private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers) {
+ private static Account.Id fromIdField(FieldBundle f, boolean useLegacyNumericFields) {
+ if (useLegacyNumericFields) {
+ return Account.id(f.getValue(AccountField.ID).intValue());
+ }
+ return Account.id(Integer.valueOf(f.getValue(AccountField.ID_STR)));
+ }
+
+ private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers)
+ throws BadRequestException {
try (Timer0.Context ctx = metrics.queryAccountsLatency.start()) {
- try {
- // For performance reasons we don't use AccountQueryProvider as it would always load the
- // complete account from the cache (or worse, from NoteDb) even though we only need the ID
- // which we can directly get from the returned results.
- Predicate<AccountState> pred =
- Predicate.and(
- AccountPredicates.isActive(),
- accountQueryBuilder.defaultQuery(suggestReviewers.getQuery()));
- logger.atFine().log("accounts index query: %s", pred);
- ResultSet<FieldBundle> result =
- accountIndexes
- .getSearchIndex()
- .getSource(
- pred,
- QueryOptions.create(
- indexConfig,
- 0,
- suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER,
- ImmutableSet.of(AccountField.ID.getName())))
- .readRaw();
- List<Account.Id> matches =
- result.toList().stream()
- .map(f -> new Account.Id(f.getValue(AccountField.ID).intValue()))
- .collect(toList());
- logger.atFine().log("Matches: %s", matches);
- return matches;
- } catch (QueryParseException e) {
+ // For performance reasons we don't use AccountQueryProvider as it would always load the
+ // complete account from the cache (or worse, from NoteDb) even though we only need the ID
+ // which we can directly get from the returned results.
+ Predicate<AccountState> pred =
+ Predicate.and(
+ AccountPredicates.isActive(),
+ accountQueryBuilder.defaultQuery(suggestReviewers.getQuery()));
+ logger.atFine().log("accounts index query: %s", pred);
+ accountIndexRewriter.validateMaxTermsInQuery(pred);
+ boolean useLegacyNumericFields =
+ accountIndexes.getSearchIndex().getSchema().useLegacyNumericFields();
+ FieldDef<AccountState, ?> idField =
+ useLegacyNumericFields ? AccountField.ID : AccountField.ID_STR;
+ ResultSet<FieldBundle> result =
+ accountIndexes
+ .getSearchIndex()
+ .getSource(
+ pred,
+ QueryOptions.create(
+ indexConfig,
+ 0,
+ suggestReviewers.getLimit(),
+ ImmutableSet.of(idField.getName())))
+ .readRaw();
+ List<Account.Id> matches =
+ result.toList().stream()
+ .map(f -> fromIdField(f, useLegacyNumericFields))
+ .collect(toList());
+ logger.atFine().log("Matches: %s", matches);
+ return matches;
+ } catch (TooManyTermsInQueryException e) {
+ throw new BadRequestException(e.getMessage());
+ } catch (QueryParseException e) {
+ logger.atWarning().withCause(e).log("Suggesting accounts failed, return empty result.");
+ return ImmutableList.of();
+ } catch (StorageException e) {
+ if (e.getCause() instanceof TooManyTermsInQueryException) {
+ throw new BadRequestException(e.getMessage());
+ }
+ if (e.getCause() instanceof QueryParseException) {
return ImmutableList.of();
}
+ throw e;
}
}
@@ -286,6 +315,7 @@ public class ReviewersUtil {
}
private List<Account.Id> recommendAccounts(
+ ReviewerState reviewerState,
@Nullable ChangeNotes changeNotes,
SuggestReviewers suggestReviewers,
ProjectState projectState,
@@ -293,7 +323,7 @@ public class ReviewersUtil {
throws IOException, ConfigInvalidException {
try (Timer0.Context ctx = metrics.recommendAccountsLatency.start()) {
return reviewerRecommender.suggestReviewers(
- changeNotes, suggestReviewers, projectState, candidateList);
+ reviewerState, changeNotes, suggestReviewers, projectState, candidateList);
}
}
@@ -405,7 +435,7 @@ public class ReviewersUtil {
// require that at least one member in the group can see the change
for (Account account : members) {
- if (visibilityControl.isVisibleTo(account.getId())) {
+ if (visibilityControl.isVisibleTo(account.id())) {
if (needsConfirmation) {
result.allowedWithConfirmation = true;
} else {
diff --git a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
index a41143cdc6..ac0945d602 100644
--- a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -23,7 +24,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.change.RevisionResource;
diff --git a/java/com/google/gerrit/server/restapi/change/Revisions.java b/java/com/google/gerrit/server/restapi/change/Revisions.java
index c5cce4f3e5..7ca989c742 100644
--- a/java/com/google/gerrit/server/restapi/change/Revisions.java
+++ b/java/com/google/gerrit/server/restapi/change/Revisions.java
@@ -16,14 +16,15 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.RevisionResource;
@@ -36,11 +37,13 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
@Singleton
public class Revisions implements ChildCollection<ChangeResource, RevisionResource> {
@@ -121,42 +124,46 @@ public class Revisions implements ChildCollection<ChangeResource, RevisionResour
} else if (id.length() < 6 && id.matches("^[1-9][0-9]{0,4}$")) {
// Legacy patch set number syntax.
return byLegacyPatchSetId(change, id);
- } else if (id.length() < 4 || id.length() > RevId.LEN) {
+ } else if (id.length() < 4 || id.length() > ObjectIds.STR_LEN) {
// Require a minimum of 4 digits.
// Impossibly long identifier will never match.
return Collections.emptyList();
} else {
List<RevisionResource> out = new ArrayList<>();
for (PatchSet ps : psUtil.byChange(change.getNotes())) {
- if (ps.getRevision() != null && ps.getRevision().get().startsWith(id)) {
+ if (ObjectIds.matchesAbbreviation(ps.commitId(), id)) {
out.add(new RevisionResource(change, ps));
}
}
// Not an existing patch set on a change, but might be an edit.
- if (out.isEmpty() && id.length() == RevId.LEN) {
- return loadEdit(change, new RevId(id));
+ if (out.isEmpty() && ObjectId.isId(id)) {
+ return loadEdit(change, ObjectId.fromString(id));
}
return out;
}
}
private List<RevisionResource> byLegacyPatchSetId(ChangeResource change, String id) {
- PatchSet ps =
- psUtil.get(change.getNotes(), new PatchSet.Id(change.getId(), Integer.parseInt(id)));
+ PatchSet ps = psUtil.get(change.getNotes(), PatchSet.id(change.getId(), Integer.parseInt(id)));
if (ps != null) {
return Collections.singletonList(new RevisionResource(change, ps));
}
return Collections.emptyList();
}
- private List<RevisionResource> loadEdit(ChangeResource change, RevId revid)
+ private List<RevisionResource> loadEdit(ChangeResource change, @Nullable ObjectId commitId)
throws AuthException, IOException {
Optional<ChangeEdit> edit = editUtil.byChange(change.getNotes(), change.getUser());
if (edit.isPresent()) {
- PatchSet ps = new PatchSet(new PatchSet.Id(change.getId(), 0));
- RevId editRevId = new RevId(ObjectId.toString(edit.get().getEditCommit()));
- ps.setRevision(editRevId);
- if (revid == null || editRevId.equals(revid)) {
+ RevCommit editCommit = edit.get().getEditCommit();
+ PatchSet ps =
+ PatchSet.builder()
+ .id(PatchSet.id(change.getId(), 0))
+ .commitId(editCommit)
+ .uploader(change.getUser().getAccountId())
+ .createdOn(new Timestamp(editCommit.getCommitterIdent().getWhen().getTime()))
+ .build();
+ if (commitId == null || editCommit.equals(commitId)) {
return Collections.singletonList(new RevisionResource(change, ps, edit));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/RobotComments.java b/java/com/google/gerrit/server/restapi/change/RobotComments.java
index 1aa8c2ac92..9f81d0ab5a 100644
--- a/java/com/google/gerrit/server/restapi/change/RobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/RobotComments.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.RobotCommentResource;
@@ -59,7 +59,7 @@ public class RobotComments implements ChildCollection<RevisionResource, RobotCom
String uuid = id.get();
ChangeNotes notes = rev.getNotes();
- for (RobotComment c : commentsUtil.robotCommentsByPatchSet(notes, rev.getPatchSet().getId())) {
+ for (RobotComment c : commentsUtil.robotCommentsByPatchSet(notes, rev.getPatchSet().id())) {
if (uuid.equals(c.key.uuid)) {
return new RobotCommentResource(rev, c);
}
diff --git a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
index aacf58b2d6..288806cb1c 100644
--- a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
+++ b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
@@ -17,12 +17,12 @@ package com.google.gerrit.server.restapi.change;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.NotifyResolver;
@@ -39,7 +39,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
@Singleton
-public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
+public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, Input, String>
implements UiAction<ChangeResource> {
private final WorkInProgressOp.Factory opFactory;
@@ -50,7 +50,7 @@ public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, In
}
@Override
- protected Response<?> applyImpl(
+ protected Response<String> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws RestApiException, UpdateException, PermissionBackendException {
rsrc.permissions().check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
diff --git a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
index 852813e96d..3fb0295099 100644
--- a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
+++ b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
@@ -17,12 +17,12 @@ package com.google.gerrit.server.restapi.change;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.NotifyResolver;
@@ -39,7 +39,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
@Singleton
-public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
+public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, Input, String>
implements UiAction<ChangeResource> {
private final WorkInProgressOp.Factory opFactory;
@@ -50,7 +50,7 @@ public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, In
}
@Override
- protected Response<?> applyImpl(
+ protected Response<String> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws RestApiException, UpdateException, PermissionBackendException {
rsrc.permissions().check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
diff --git a/java/com/google/gerrit/server/restapi/change/Submit.java b/java/com/google/gerrit/server/restapi/change/Submit.java
index 51e2dfbe3c..2c7a83ab86 100644
--- a/java/com/google/gerrit/server/restapi/change/Submit.java
+++ b/java/com/google/gerrit/server/restapi/change/Submit.java
@@ -14,29 +14,32 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static java.util.stream.Collectors.joining;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.data.ParameterizedString;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -48,11 +51,9 @@ import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -107,7 +108,6 @@ public class Submit
private final GitRepositoryManager repoManager;
private final PermissionBackend permissionBackend;
private final ChangeData.Factory changeDataFactory;
- private final ChangeNotes.Factory changeNotesFactory;
private final Provider<MergeOp> mergeOpProvider;
private final Provider<MergeSuperSet> mergeSuperSet;
private final AccountResolver accountResolver;
@@ -127,7 +127,6 @@ public class Submit
GitRepositoryManager repoManager,
PermissionBackend permissionBackend,
ChangeData.Factory changeDataFactory,
- ChangeNotes.Factory changeNotesFactory,
Provider<MergeOp> mergeOpProvider,
Provider<MergeSuperSet> mergeSuperSet,
AccountResolver accountResolver,
@@ -138,7 +137,6 @@ public class Submit
this.repoManager = repoManager;
this.permissionBackend = permissionBackend;
this.changeDataFactory = changeDataFactory;
- this.changeNotesFactory = changeNotesFactory;
this.mergeOpProvider = mergeOpProvider;
this.mergeSuperSet = mergeSuperSet;
this.accountResolver = accountResolver;
@@ -173,7 +171,7 @@ public class Submit
}
@Override
- public Output apply(RevisionResource rsrc, SubmitInput input)
+ public Response<Output> apply(RevisionResource rsrc, SubmitInput input)
throws RestApiException, RepositoryNotFoundException, IOException, PermissionBackendException,
UpdateException, ConfigInvalidException {
input.onBehalfOf = Strings.emptyToNull(input.onBehalfOf);
@@ -186,44 +184,47 @@ public class Submit
}
projectCache.checkedGet(rsrc.getProject()).checkStatePermitsWrite();
- return new Output(mergeChange(rsrc, submitter, input));
+ return mergeChange(rsrc, submitter, input);
}
- public Change mergeChange(RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input)
- throws RestApiException, IOException, UpdateException, ConfigInvalidException,
- PermissionBackendException {
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public Response<Output> mergeChange(
+ RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input)
+ throws RestApiException, IOException {
Change change = rsrc.getChange();
if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
} else if (!ProjectUtil.branchExists(repoManager, change.getDest())) {
throw new ResourceConflictException(
- String.format("destination branch \"%s\" not found.", change.getDest().get()));
- } else if (!rsrc.getPatchSet().getId().equals(change.currentPatchSetId())) {
+ String.format("destination branch \"%s\" not found.", change.getDest().branch()));
+ } else if (!rsrc.getPatchSet().id().equals(change.currentPatchSetId())) {
// TODO Allow submitting non-current revision by changing the current.
throw new ResourceConflictException(
String.format(
- "revision %s is not current revision", rsrc.getPatchSet().getRevision().get()));
+ "revision %s is not current revision", rsrc.getPatchSet().commitId().name()));
}
try (MergeOp op = mergeOpProvider.get()) {
- op.merge(change, submitter, true, input, false);
- }
+ Change updatedChange;
- // Read the ChangeNotes only after MergeOp is fully done (including MergeOp#close) to be sure
- // to have the correct state of the repo.
- try {
- change = changeNotesFactory.createChecked(change.getProject(), change.getId()).getChange();
- } catch (NoSuchChangeException e) {
- throw new ResourceConflictException("change is deleted", e);
- }
+ try {
+ updatedChange = op.merge(change, submitter, true, input, false);
+ } catch (Exception e) {
+ Throwables.throwIfInstanceOf(e, RestApiException.class);
+ return Response.<Output>internalServerError(e).traceId(op.getTraceId().orElse(null));
+ }
- if (change.isMerged()) {
- return change;
- }
- if (change.isNew()) {
- throw new RestApiException("change unexpectedly had status NEW after submit attempt");
+ if (updatedChange.isMerged()) {
+ return Response.ok(new Output(updatedChange));
+ }
+
+ String msg =
+ String.format(
+ "change %s of project %s unexpectedly had status %s after submit attempt",
+ updatedChange.getId(), updatedChange.getProject(), updatedChange.getStatus());
+ logger.atWarning().log(msg);
+ throw new RestApiException(msg);
}
- throw new ResourceConflictException("change is " + ChangeUtil.status(change));
}
/**
@@ -356,12 +357,11 @@ public class Submit
.setVisible(true)
.setEnabled(Boolean.TRUE.equals(enabled));
}
- RevId revId = resource.getPatchSet().getRevision();
Map<String, String> params =
ImmutableMap.of(
- "patchSet", String.valueOf(resource.getPatchSet().getPatchSetId()),
- "branch", change.getDest().getShortName(),
- "commit", ObjectId.fromString(revId.get()).abbreviate(7).name(),
+ "patchSet", String.valueOf(resource.getPatchSet().number()),
+ "branch", change.getDest().shortName(),
+ "commit", abbreviateName(resource.getPatchSet().commitId()),
"submitSize", String.valueOf(cs.size()));
ParameterizedString tp = cs.size() > 1 ? titlePatternWithAncestors : titlePattern;
return new UiAction.Description()
@@ -377,10 +377,10 @@ public class Submit
mergeabilityMap.add(change);
}
- ListMultimap<Branch.NameKey, ChangeData> cbb = cs.changesByBranch();
- for (Branch.NameKey branch : cbb.keySet()) {
+ ListMultimap<BranchNameKey, ChangeData> cbb = cs.changesByBranch();
+ for (BranchNameKey branch : cbb.keySet()) {
Collection<ChangeData> targetBranch = cbb.get(branch);
- HashMap<Change.Id, RevCommit> commits = findCommits(targetBranch, branch.getParentKey());
+ HashMap<Change.Id, RevCommit> commits = findCommits(targetBranch, branch.project());
Set<ObjectId> allParents = Sets.newHashSetWithExpectedSize(cs.size());
for (RevCommit commit : commits.values()) {
@@ -425,9 +425,7 @@ public class Submit
try (Repository repo = repoManager.openRepository(project);
RevWalk walk = new RevWalk(repo)) {
for (ChangeData change : changes) {
- RevCommit commit =
- walk.parseCommit(
- ObjectId.fromString(psUtil.current(change.notes()).getRevision().get()));
+ RevCommit commit = walk.parseCommit(psUtil.current(change.notes()).commitId());
commits.put(change.getId(), commit);
}
}
@@ -466,16 +464,20 @@ public class Submit
}
@Override
- public ChangeInfo apply(ChangeResource rsrc, SubmitInput input)
- throws RestApiException, RepositoryNotFoundException, IOException,
- PermissionBackendException, UpdateException, ConfigInvalidException {
+ public Response<ChangeInfo> apply(ChangeResource rsrc, SubmitInput input) throws Exception {
PatchSet ps = psUtil.current(rsrc.getNotes());
if (ps == null) {
throw new ResourceConflictException("current revision is missing");
}
- Output out = submit.apply(new RevisionResource(rsrc, ps), input);
- return json.noOptions().format(out.change);
+ Response<Output> response = submit.apply(new RevisionResource(rsrc, ps), input);
+ if (response instanceof Response.InternalServerError) {
+ Response.InternalServerError<?> ise = (Response.InternalServerError<?>) response;
+ return Response.<ChangeInfo>internalServerError(ise.cause())
+ .traceId(ise.traceId().orElse(null));
+ }
+
+ return Response.ok(json.noOptions().format(response.value().change));
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
index cefbfdb748..214a0015a1 100644
--- a/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
+++ b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
@@ -19,6 +19,7 @@ import static java.util.Collections.reverseOrder;
import static java.util.stream.Collectors.toList;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
import com.google.gerrit.extensions.api.changes.SubmittedTogetherOption;
@@ -26,8 +27,8 @@ import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.WalkSorter;
@@ -58,7 +59,8 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.SUBMITTABLE);
private static final Comparator<ChangeData> COMPARATOR =
- Comparator.comparing(ChangeData::project).thenComparing(cd -> cd.getId().id, reverseOrder());
+ Comparator.comparing(ChangeData::project)
+ .thenComparing(cd -> cd.getId().get(), reverseOrder());
private final ChangeJson.Factory json;
private final Provider<InternalChangeQuery> queryProvider;
@@ -107,14 +109,14 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
}
@Override
- public Object apply(ChangeResource resource)
+ public Response<Object> apply(ChangeResource resource)
throws AuthException, BadRequestException, ResourceConflictException, IOException,
PermissionBackendException {
SubmittedTogetherInfo info = applyInfo(resource);
if (options.isEmpty()) {
- return info.changes;
+ return Response.ok(info.changes);
}
- return info;
+ return Response.ok(info);
}
public SubmittedTogetherInfo applyInfo(ChangeResource resource)
diff --git a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
index f5a27516bc..d247b5b1d9 100644
--- a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
@@ -14,10 +14,12 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.AccountVisibility;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeResource;
@@ -38,15 +40,31 @@ import org.kohsuke.args4j.Option;
public class SuggestChangeReviewers extends SuggestReviewers
implements RestReadView<ChangeResource> {
+ private final PermissionBackend permissionBackend;
+ private final Provider<CurrentUser> self;
+ private final ProjectCache projectCache;
+
+ private boolean excludeGroups;
+ private ReviewerState reviewerState = ReviewerState.REVIEWER;
+
@Option(
name = "--exclude-groups",
aliases = {"-e"},
usage = "exclude groups from query")
- boolean excludeGroups;
+ public SuggestChangeReviewers setExcludeGroups(boolean excludeGroups) {
+ this.excludeGroups = excludeGroups;
+ return this;
+ }
- private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> self;
- private final ProjectCache projectCache;
+ @Option(
+ name = "--reviewer-state",
+ usage =
+ "The type of reviewers that should be suggested"
+ + " (can be 'REVIEWER' or 'CC', default is 'REVIEWER')")
+ public SuggestChangeReviewers setReviewerState(ReviewerState reviewerState) {
+ this.reviewerState = reviewerState;
+ return this;
+ }
@Inject
SuggestChangeReviewers(
@@ -63,18 +81,24 @@ public class SuggestChangeReviewers extends SuggestReviewers
}
@Override
- public List<SuggestedReviewerInfo> apply(ChangeResource rsrc)
+ public Response<List<SuggestedReviewerInfo>> apply(ChangeResource rsrc)
throws AuthException, BadRequestException, IOException, ConfigInvalidException,
PermissionBackendException {
if (!self.get().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
- return reviewersUtil.suggestReviewers(
- rsrc.getNotes(),
- this,
- projectCache.checkedGet(rsrc.getProject()),
- getVisibility(rsrc),
- excludeGroups);
+ if (reviewerState.equals(ReviewerState.REMOVED)) {
+ throw new BadRequestException(
+ String.format("Unsupported reviewer state: %s", ReviewerState.REMOVED));
+ }
+ return Response.ok(
+ reviewersUtil.suggestReviewers(
+ reviewerState,
+ rsrc.getNotes(),
+ this,
+ projectCache.checkedGet(rsrc.getProject()),
+ getVisibility(rsrc),
+ excludeGroups));
}
private VisibilityControl getVisibility(ChangeResource rsrc) {
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
index 370bdb25e7..bae2e52e59 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
@@ -15,9 +15,6 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.TestSubmitRuleInfo;
@@ -25,30 +22,26 @@ import com.google.gerrit.extensions.common.TestSubmitRuleInput;
import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.rules.DefaultSubmitRule;
+import com.google.gerrit.server.rules.PrologOptions;
import com.google.gerrit.server.rules.PrologRule;
import com.google.gerrit.server.rules.RulesCache;
import com.google.inject.Inject;
import java.util.LinkedHashMap;
-import java.util.List;
import org.kohsuke.args4j.Option;
public class TestSubmitRule implements RestModifyView<RevisionResource, TestSubmitRuleInput> {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
private final ChangeData.Factory changeDataFactory;
private final RulesCache rules;
private final AccountLoader.Factory accountInfoFactory;
private final ProjectCache projectCache;
- private final DefaultSubmitRule defaultSubmitRule;
private final PrologRule prologRule;
@Option(name = "--filters", usage = "impact of filters in parent projects")
@@ -60,56 +53,41 @@ public class TestSubmitRule implements RestModifyView<RevisionResource, TestSubm
RulesCache rules,
AccountLoader.Factory infoFactory,
ProjectCache projectCache,
- DefaultSubmitRule defaultSubmitRule,
PrologRule prologRule) {
this.changeDataFactory = changeDataFactory;
this.rules = rules;
this.accountInfoFactory = infoFactory;
this.projectCache = projectCache;
- this.defaultSubmitRule = defaultSubmitRule;
this.prologRule = prologRule;
}
@Override
- public List<TestSubmitRuleInfo> apply(RevisionResource rsrc, TestSubmitRuleInput input)
+ public Response<TestSubmitRuleInfo> apply(RevisionResource rsrc, TestSubmitRuleInput input)
throws AuthException, PermissionBackendException, BadRequestException {
if (input == null) {
input = new TestSubmitRuleInput();
}
- if (input.rule != null && !rules.isProjectRulesEnabled()) {
+ if (input.rule == null) {
+ throw new BadRequestException("rule is required");
+ }
+ if (!rules.isProjectRulesEnabled()) {
throw new AuthException("project rules are disabled");
}
input.filters = MoreObjects.firstNonNull(input.filters, filters);
- SubmitRuleOptions opts =
- SubmitRuleOptions.builder()
- .skipFilters(input.filters == Filters.SKIP)
- .rule(input.rule)
- .logErrors(logger.atFine().isEnabled())
- .build();
-
ProjectState projectState = projectCache.get(rsrc.getProject());
if (projectState == null) {
throw new BadRequestException("project not found");
}
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
- List<SubmitRecord> records;
- if (projectState.hasPrologRules() || input.rule != null) {
- records = ImmutableList.copyOf(prologRule.evaluate(cd, opts));
- } else {
- // No rules were provided as input and we have no rules.pl. This means we are supposed to run
- // the default rules. Nowadays, the default rules are implemented in Java, not Prolog.
- // Therefore, we call the DefaultRuleEvaluator instead.
- records = ImmutableList.copyOf(defaultSubmitRule.evaluate(cd, opts));
- }
+ SubmitRecord record =
+ prologRule.evaluate(
+ cd, PrologOptions.dryRunOptions(input.rule, input.filters == Filters.SKIP));
- List<TestSubmitRuleInfo> out = Lists.newArrayListWithCapacity(records.size());
AccountLoader accounts = accountInfoFactory.create(true);
- for (SubmitRecord r : records) {
- out.add(newSubmitRuleInfo(r, accounts));
- }
+ TestSubmitRuleInfo out = newSubmitRuleInfo(record, accounts);
accounts.fill();
- return out;
+ return Response.ok(out);
}
private static TestSubmitRuleInfo newSubmitRuleInfo(SubmitRecord r, AccountLoader accounts) {
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
index 4c7fad2011..cb52fcba3c 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
@@ -15,83 +15,92 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.base.MoreObjects;
-import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.TestSubmitRuleInput;
import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.rules.PrologOptions;
+import com.google.gerrit.server.rules.PrologRule;
import com.google.gerrit.server.rules.RulesCache;
import com.google.inject.Inject;
import org.kohsuke.args4j.Option;
public class TestSubmitType implements RestModifyView<RevisionResource, TestSubmitRuleInput> {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
private final ChangeData.Factory changeDataFactory;
private final RulesCache rules;
- private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
+ private final PrologRule prologRule;
@Option(name = "--filters", usage = "impact of filters in parent projects")
private Filters filters = Filters.RUN;
@Inject
- TestSubmitType(
- ChangeData.Factory changeDataFactory,
- RulesCache rules,
- SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
+ TestSubmitType(ChangeData.Factory changeDataFactory, RulesCache rules, PrologRule prologRule) {
this.changeDataFactory = changeDataFactory;
this.rules = rules;
- this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
+ this.prologRule = prologRule;
}
@Override
- public SubmitType apply(RevisionResource rsrc, TestSubmitRuleInput input)
+ public Response<SubmitType> apply(RevisionResource rsrc, TestSubmitRuleInput input)
throws AuthException, BadRequestException {
if (input == null) {
input = new TestSubmitRuleInput();
}
- if (input.rule != null && !rules.isProjectRulesEnabled()) {
+ if (input.rule == null) {
+ throw new BadRequestException("rule is required");
+ }
+ if (!rules.isProjectRulesEnabled()) {
throw new AuthException("project rules are disabled");
}
input.filters = MoreObjects.firstNonNull(input.filters, filters);
- SubmitRuleOptions opts =
- SubmitRuleOptions.builder()
- .logErrors(logger.atFine().isEnabled())
- .skipFilters(input.filters == Filters.SKIP)
- .rule(input.rule)
- .build();
-
- SubmitRuleEvaluator evaluator = submitRuleEvaluatorFactory.create(opts);
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
- SubmitTypeRecord rec = evaluator.getSubmitType(cd);
+ SubmitTypeRecord rec =
+ prologRule.getSubmitType(
+ cd, PrologOptions.dryRunOptions(input.rule, input.filters == Filters.SKIP));
if (rec.status != SubmitTypeRecord.Status.OK) {
throw new BadRequestException(String.format("rule produced invalid result: %s", rec));
}
- return rec.type;
+ return Response.ok(rec.type);
}
public static class Get implements RestReadView<RevisionResource> {
- private final TestSubmitType test;
+ private final ChangeData.Factory changeDataFactory;
+ private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
@Inject
- Get(TestSubmitType test) {
- this.test = test;
+ Get(
+ ChangeData.Factory changeDataFactory,
+ SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
+ this.changeDataFactory = changeDataFactory;
+ this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
}
@Override
- public SubmitType apply(RevisionResource resource) throws AuthException, BadRequestException {
- return test.apply(resource, null);
+ public Response<SubmitType> apply(RevisionResource resource)
+ throws AuthException, ResourceConflictException {
+ SubmitRuleEvaluator evaluator =
+ submitRuleEvaluatorFactory.create(SubmitRuleOptions.defaults());
+ ChangeData cd = changeDataFactory.create(resource.getNotes());
+ SubmitTypeRecord rec = evaluator.getSubmitType(cd);
+
+ if (rec.status != SubmitTypeRecord.Status.OK) {
+ throw new ResourceConflictException(String.format("rule produced invalid result: %s", rec));
+ }
+
+ return Response.ok(rec.type);
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Votes.java b/java/com/google/gerrit/server/restapi/change/Votes.java
index 31efe545fd..d8990024b6 100644
--- a/java/com/google/gerrit/server/restapi/change/Votes.java
+++ b/java/com/google/gerrit/server/restapi/change/Votes.java
@@ -14,15 +14,16 @@
package com.google.gerrit.server.restapi.change;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.change.VoteResource;
@@ -71,7 +72,8 @@ public class Votes implements ChildCollection<ReviewerResource, VoteResource> {
}
@Override
- public Map<String, Short> apply(ReviewerResource rsrc) throws MethodNotAllowedException {
+ public Response<Map<String, Short>> apply(ReviewerResource rsrc)
+ throws MethodNotAllowedException {
if (rsrc.getRevisionResource() != null && !rsrc.getRevisionResource().isCurrent()) {
throw new MethodNotAllowedException("Cannot list votes on non-current patch set");
}
@@ -85,9 +87,9 @@ public class Votes implements ChildCollection<ReviewerResource, VoteResource> {
null,
null);
for (PatchSetApproval psa : byPatchSetUser) {
- votes.put(psa.getLabel(), psa.getValue());
+ votes.put(psa.label(), psa.value());
}
- return votes;
+ return Response.ok(votes);
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
index 61d5c796b2..50e774aeb1 100644
--- a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
+++ b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
@@ -20,6 +20,7 @@ import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.CheckAccount
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.CheckGroupsResultInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.account.AccountsConsistencyChecker;
@@ -54,7 +55,7 @@ public class CheckConsistency implements RestModifyView<ConfigResource, Consiste
}
@Override
- public ConsistencyCheckInfo apply(ConfigResource resource, ConsistencyCheckInput input)
+ public Response<ConsistencyCheckInfo> apply(ConfigResource resource, ConsistencyCheckInput input)
throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
@@ -80,6 +81,6 @@ public class CheckConsistency implements RestModifyView<ConfigResource, Consiste
new CheckGroupsResultInfo(groupsConsistencyChecker.check());
}
- return consistencyCheckInfo;
+ return Response.ok(consistencyCheckInfo);
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java b/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
index b6dcd35cfb..b56f1b88c8 100644
--- a/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
+++ b/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.restapi.config;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
diff --git a/java/com/google/gerrit/server/restapi/config/GetCache.java b/java/com/google/gerrit/server/restapi/config/GetCache.java
index 5abaf1e397..93600ea582 100644
--- a/java/com/google/gerrit/server/restapi/config/GetCache.java
+++ b/java/com/google/gerrit/server/restapi/config/GetCache.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.config;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.CacheResource;
import com.google.inject.Singleton;
@@ -22,7 +23,7 @@ import com.google.inject.Singleton;
public class GetCache implements RestReadView<CacheResource> {
@Override
- public ListCaches.CacheInfo apply(CacheResource rsrc) {
- return new ListCaches.CacheInfo(rsrc.getName(), rsrc.getCache());
+ public Response<ListCaches.CacheInfo> apply(CacheResource rsrc) {
+ return Response.ok(new ListCaches.CacheInfo(rsrc.getName(), rsrc.getCache()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java b/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
index 13c28189ed..44c71b374e 100644
--- a/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetDiffPreferences.java
@@ -17,8 +17,9 @@ package com.google.gerrit.server.restapi.config;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.account.Preferences;
+import com.google.gerrit.server.account.StoredPreferences;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -41,10 +42,10 @@ public class GetDiffPreferences implements RestReadView<ConfigResource> {
}
@Override
- public DiffPreferencesInfo apply(ConfigResource configResource)
+ public Response<DiffPreferencesInfo> apply(ConfigResource configResource)
throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
try (Repository git = gitManager.openRepository(allUsersName)) {
- return Preferences.readDefaultDiffPreferences(allUsersName, git);
+ return Response.ok(StoredPreferences.readDefaultDiffPreferences(allUsersName, git));
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java b/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
index 2ec547b1e1..a5ab967c36 100644
--- a/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetEditPreferences.java
@@ -17,8 +17,9 @@ package com.google.gerrit.server.restapi.config;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.account.Preferences;
+import com.google.gerrit.server.account.StoredPreferences;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -40,10 +41,10 @@ public class GetEditPreferences implements RestReadView<ConfigResource> {
}
@Override
- public EditPreferencesInfo apply(ConfigResource configResource)
+ public Response<EditPreferencesInfo> apply(ConfigResource configResource)
throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
try (Repository git = gitManager.openRepository(allUsersName)) {
- return Preferences.readDefaultEditPreferences(allUsersName, git);
+ return Response.ok(StoredPreferences.readDefaultEditPreferences(allUsersName, git));
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/GetPreferences.java b/java/com/google/gerrit/server/restapi/config/GetPreferences.java
index 4dbbc8cf37..8da9134a0d 100644
--- a/java/com/google/gerrit/server/restapi/config/GetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/GetPreferences.java
@@ -15,8 +15,9 @@
package com.google.gerrit.server.restapi.config;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.account.Preferences;
+import com.google.gerrit.server.account.StoredPreferences;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -38,10 +39,10 @@ public class GetPreferences implements RestReadView<ConfigResource> {
}
@Override
- public GeneralPreferencesInfo apply(ConfigResource rsrc)
+ public Response<GeneralPreferencesInfo> apply(ConfigResource rsrc)
throws IOException, ConfigInvalidException {
try (Repository git = gitMgr.openRepository(allUsersName)) {
- return Preferences.readDefaultGeneralPreferences(allUsersName, git);
+ return Response.ok(StoredPreferences.readDefaultGeneralPreferences(allUsersName, git));
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
index aa0e350481..2d504c7347 100644
--- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
+++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
@@ -35,6 +35,7 @@ import com.google.gerrit.extensions.common.UserConfigInfo;
import com.google.gerrit.extensions.config.CloneCommand;
import com.google.gerrit.extensions.config.DownloadCommand;
import com.google.gerrit.extensions.config.DownloadScheme;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.webui.WebUiPlugin;
import com.google.gerrit.server.EnableSignedPush;
@@ -133,7 +134,7 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
}
@Override
- public ServerInfo apply(ConfigResource rsrc) throws PermissionBackendException {
+ public Response<ServerInfo> apply(ConfigResource rsrc) throws PermissionBackendException {
ServerInfo info = new ServerInfo();
info.accounts = getAccountsInfo();
info.auth = getAuthInfo();
@@ -148,7 +149,7 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
info.user = getUserInfo();
info.receive = getReceiveInfo();
- return info;
+ return Response.ok(info);
}
private AccountsInfo getAccountsInfo() {
@@ -225,7 +226,9 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
+ "\u2026";
info.updateDelay =
(int) ConfigUtil.getTimeUnit(config, "change", null, "updateDelay", 300, TimeUnit.SECONDS);
- info.submitWholeTopic = MergeSuperSet.wholeTopicEnabled(config);
+ info.submitWholeTopic = toBoolean(MergeSuperSet.wholeTopicEnabled(config));
+ info.excludeMergeableInChangeInfo =
+ toBoolean(this.config.getBoolean("change", "api", "excludeMergeableInChangeInfo", false));
info.disablePrivateChanges =
toBoolean(this.config.getBoolean("change", null, "disablePrivateChanges", false));
return info;
diff --git a/java/com/google/gerrit/server/restapi/config/GetSummary.java b/java/com/google/gerrit/server/restapi/config/GetSummary.java
index 1d8da63b3d..d0a1498bfb 100644
--- a/java/com/google/gerrit/server/restapi/config/GetSummary.java
+++ b/java/com/google/gerrit/server/restapi/config/GetSummary.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.config;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.SitePath;
@@ -69,7 +70,7 @@ public class GetSummary implements RestReadView<ConfigResource> {
}
@Override
- public SummaryInfo apply(ConfigResource rsrc) {
+ public Response<SummaryInfo> apply(ConfigResource rsrc) {
if (gc) {
System.gc();
System.runFinalization();
@@ -83,7 +84,7 @@ public class GetSummary implements RestReadView<ConfigResource> {
if (jvm) {
summary.jvmSummary = getJvmSummary();
}
- return summary;
+ return Response.ok(summary);
}
private TaskSummaryInfo getTaskSummary() {
diff --git a/java/com/google/gerrit/server/restapi/config/GetTask.java b/java/com/google/gerrit/server/restapi/config/GetTask.java
index a32f3bacb5..513c99a016 100644
--- a/java/com/google/gerrit/server/restapi/config/GetTask.java
+++ b/java/com/google/gerrit/server/restapi/config/GetTask.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.config;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.TaskResource;
import com.google.inject.Singleton;
@@ -22,7 +23,7 @@ import com.google.inject.Singleton;
public class GetTask implements RestReadView<TaskResource> {
@Override
- public ListTasks.TaskInfo apply(TaskResource rsrc) {
- return new ListTasks.TaskInfo(rsrc.getTask());
+ public Response<ListTasks.TaskInfo> apply(TaskResource rsrc) {
+ return Response.ok(new ListTasks.TaskInfo(rsrc.getTask()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/IndexChanges.java b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
index 6ba93e8436..904c44f809 100644
--- a/java/com/google/gerrit/server/restapi/config/IndexChanges.java
+++ b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
@@ -51,7 +51,7 @@ public class IndexChanges implements RestModifyView<ConfigResource, Input> {
}
@Override
- public Object apply(ConfigResource resource, Input input) {
+ public Response<String> apply(ConfigResource resource, Input input) {
if (input == null || input.changes == null) {
return Response.ok("Nothing to index");
}
diff --git a/java/com/google/gerrit/server/restapi/config/ListCaches.java b/java/com/google/gerrit/server/restapi/config/ListCaches.java
index f310ed7e71..ccafbe89b1 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCaches.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCaches.java
@@ -28,6 +28,7 @@ import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.BinaryResult;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.cache.PersistentCache;
import com.google.gerrit.server.config.ConfigResource;
@@ -69,21 +70,22 @@ public class ListCaches implements RestReadView<ConfigResource> {
}
@Override
- public Object apply(ConfigResource rsrc) {
+ public Response<Object> apply(ConfigResource rsrc) {
if (format == null) {
- return getCacheInfos();
+ return Response.ok(getCacheInfos());
}
Stream<String> cacheNames =
Streams.stream(cacheMap)
.map(e -> cacheNameOf(e.getPluginName(), e.getExportName()))
.sorted();
if (OutputFormat.TEXT_LIST.equals(format)) {
- return BinaryResult.create(cacheNames.collect(joining("\n")))
- .base64()
- .setContentType("text/plain")
- .setCharacterEncoding(UTF_8);
+ return Response.ok(
+ BinaryResult.create(cacheNames.collect(joining("\n")))
+ .base64()
+ .setContentType("text/plain")
+ .setCharacterEncoding(UTF_8));
}
- return cacheNames.collect(toImmutableList());
+ return Response.ok(cacheNames.collect(toImmutableList()));
}
public enum CacheType {
diff --git a/java/com/google/gerrit/server/restapi/config/ListCapabilities.java b/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
index cacbbf590a..6c8bf7421c 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
@@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.CapabilityConstants;
import com.google.gerrit.server.config.ConfigResource;
@@ -43,13 +44,14 @@ public class ListCapabilities implements RestReadView<ConfigResource> {
}
@Override
- public Map<String, CapabilityInfo> apply(ConfigResource resource)
+ public Response<Map<String, CapabilityInfo>> apply(ConfigResource resource)
throws ResourceNotFoundException, IllegalAccessException, NoSuchFieldException {
permissionBackend.checkUsesDefaultCapabilities();
- return ImmutableMap.<String, CapabilityInfo>builder()
- .putAll(collectCoreCapabilities())
- .putAll(collectPluginCapabilities())
- .build();
+ return Response.ok(
+ ImmutableMap.<String, CapabilityInfo>builder()
+ .putAll(collectCoreCapabilities())
+ .putAll(collectPluginCapabilities())
+ .build());
}
public Map<String, CapabilityInfo> collectPluginCapabilities() {
diff --git a/java/com/google/gerrit/server/restapi/config/ListTasks.java b/java/com/google/gerrit/server/restapi/config/ListTasks.java
index 7402c15b21..6a3ca421fa 100644
--- a/java/com/google/gerrit/server/restapi/config/ListTasks.java
+++ b/java/com/google/gerrit/server/restapi/config/ListTasks.java
@@ -17,9 +17,10 @@ package com.google.gerrit.server.restapi.config;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.git.WorkQueue;
@@ -62,7 +63,7 @@ public class ListTasks implements RestReadView<ConfigResource> {
}
@Override
- public List<TaskInfo> apply(ConfigResource resource)
+ public Response<List<TaskInfo>> apply(ConfigResource resource)
throws AuthException, PermissionBackendException {
CurrentUser user = self.get();
if (!user.isIdentifiedUser()) {
@@ -72,7 +73,7 @@ public class ListTasks implements RestReadView<ConfigResource> {
List<TaskInfo> allTasks = getTasks();
try {
permissionBackend.user(user).check(GlobalPermission.VIEW_QUEUE);
- return allTasks;
+ return Response.ok(allTasks);
} catch (AuthException e) {
// Fall through to filter tasks.
}
@@ -83,7 +84,7 @@ public class ListTasks implements RestReadView<ConfigResource> {
if (task.projectName != null) {
Boolean visible = visibilityCache.get(task.projectName);
if (visible == null) {
- Project.NameKey nameKey = new Project.NameKey(task.projectName);
+ Project.NameKey nameKey = Project.nameKey(task.projectName);
ProjectState state = projectCache.get(nameKey);
if (state == null || !state.statePermitsRead()) {
visible = false;
@@ -102,7 +103,7 @@ public class ListTasks implements RestReadView<ConfigResource> {
}
}
}
- return visibleTasks;
+ return Response.ok(visibleTasks);
}
private List<TaskInfo> getTasks() {
diff --git a/java/com/google/gerrit/server/restapi/config/ReloadConfig.java b/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
index 0685a5862e..9ce7ffd8b0 100644
--- a/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
+++ b/java/com/google/gerrit/server/restapi/config/ReloadConfig.java
@@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.Multimap;
import com.google.gerrit.extensions.api.config.ConfigUpdateEntryInfo;
import com.google.gerrit.extensions.common.Input;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.config.ConfigResource;
@@ -47,17 +48,18 @@ public class ReloadConfig implements RestModifyView<ConfigResource, Input> {
}
@Override
- public Map<String, List<ConfigUpdateEntryInfo>> apply(ConfigResource resource, Input input)
- throws RestApiException, PermissionBackendException {
+ public Response<Map<String, List<ConfigUpdateEntryInfo>>> apply(
+ ConfigResource resource, Input input) throws RestApiException, PermissionBackendException {
permissions.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
Multimap<UpdateResult, ConfigUpdateEntry> updates = config.reloadConfig();
if (updates.isEmpty()) {
- return Collections.emptyMap();
+ return Response.ok(Collections.emptyMap());
}
- return updates.asMap().entrySet().stream()
- .collect(
- Collectors.toMap(
- e -> e.getKey().name().toLowerCase(), e -> toEntryInfos(e.getValue())));
+ return Response.ok(
+ updates.asMap().entrySet().stream()
+ .collect(
+ Collectors.toMap(
+ e -> e.getKey().name().toLowerCase(), e -> toEntryInfos(e.getValue()))));
}
private static List<ConfigUpdateEntryInfo> toEntryInfos(
diff --git a/java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java
index 068f3326c5..96654a9c94 100644
--- a/java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/SetDiffPreferences.java
@@ -21,9 +21,10 @@ import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.Preferences;
+import com.google.gerrit.server.account.StoredPreferences;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
@@ -54,7 +55,8 @@ public class SetDiffPreferences implements RestModifyView<ConfigResource, DiffPr
}
@Override
- public DiffPreferencesInfo apply(ConfigResource configResource, DiffPreferencesInfo input)
+ public Response<DiffPreferencesInfo> apply(
+ ConfigResource configResource, DiffPreferencesInfo input)
throws BadRequestException, IOException, ConfigInvalidException {
if (input == null) {
throw new BadRequestException("input must be provided");
@@ -64,9 +66,9 @@ public class SetDiffPreferences implements RestModifyView<ConfigResource, DiffPr
}
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
- DiffPreferencesInfo updatedPrefs = Preferences.updateDefaultDiffPreferences(md, input);
+ DiffPreferencesInfo updatedPrefs = StoredPreferences.updateDefaultDiffPreferences(md, input);
accountCache.evictAll();
- return updatedPrefs;
+ return Response.ok(updatedPrefs);
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/config/SetEditPreferences.java
index daca734a58..4bb420b570 100644
--- a/java/com/google/gerrit/server/restapi/config/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/SetEditPreferences.java
@@ -21,9 +21,10 @@ import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.Preferences;
+import com.google.gerrit.server.account.StoredPreferences;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
@@ -54,7 +55,8 @@ public class SetEditPreferences implements RestModifyView<ConfigResource, EditPr
}
@Override
- public EditPreferencesInfo apply(ConfigResource configResource, EditPreferencesInfo input)
+ public Response<EditPreferencesInfo> apply(
+ ConfigResource configResource, EditPreferencesInfo input)
throws BadRequestException, IOException, ConfigInvalidException {
if (input == null) {
throw new BadRequestException("input must be provided");
@@ -64,9 +66,9 @@ public class SetEditPreferences implements RestModifyView<ConfigResource, EditPr
}
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
- EditPreferencesInfo updatedPrefs = Preferences.updateDefaultEditPreferences(md, input);
+ EditPreferencesInfo updatedPrefs = StoredPreferences.updateDefaultEditPreferences(md, input);
accountCache.evictAll();
- return updatedPrefs;
+ return Response.ok(updatedPrefs);
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/SetPreferences.java b/java/com/google/gerrit/server/restapi/config/SetPreferences.java
index 6a0c22b531..c88c11197d 100644
--- a/java/com/google/gerrit/server/restapi/config/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/config/SetPreferences.java
@@ -21,9 +21,10 @@ import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.Preferences;
+import com.google.gerrit.server.account.StoredPreferences;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
@@ -54,16 +55,17 @@ public class SetPreferences implements RestModifyView<ConfigResource, GeneralPre
}
@Override
- public GeneralPreferencesInfo apply(ConfigResource rsrc, GeneralPreferencesInfo input)
+ public Response<GeneralPreferencesInfo> apply(ConfigResource rsrc, GeneralPreferencesInfo input)
throws BadRequestException, IOException, ConfigInvalidException {
if (!hasSetFields(input)) {
throw new BadRequestException("unsupported option");
}
- Preferences.validateMy(input.my);
+ StoredPreferences.validateMy(input.my);
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
- GeneralPreferencesInfo updatedPrefs = Preferences.updateDefaultGeneralPreferences(md, input);
+ GeneralPreferencesInfo updatedPrefs =
+ StoredPreferences.updateDefaultGeneralPreferences(md, input);
accountCache.evictAll();
- return updatedPrefs;
+ return Response.ok(updatedPrefs);
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/TasksCollection.java b/java/com/google/gerrit/server/restapi/config/TasksCollection.java
index 9a7aa3a3dd..837d07167c 100644
--- a/java/com/google/gerrit/server/restapi/config/TasksCollection.java
+++ b/java/com/google/gerrit/server/restapi/config/TasksCollection.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.config;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -21,7 +22,6 @@ import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.TaskResource;
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index b2b14a1a5b..caff2065ca 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -18,19 +18,19 @@ import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException;
@@ -111,7 +111,7 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
}
@Override
- public List<AccountInfo> apply(GroupResource resource, Input input)
+ public Response<List<AccountInfo>> apply(GroupResource resource, Input input)
throws AuthException, NotInternalGroupException, UnprocessableEntityException, IOException,
ConfigInvalidException, ResourceNotFoundException, PermissionBackendException {
GroupDescription.Internal internalGroup =
@@ -130,7 +130,7 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
throw new UnprocessableEntityException(
String.format("Account Inactive: %s", nameOrEmailOrId));
}
- newMemberIds.add(a.getId());
+ newMemberIds.add(a.id());
}
AccountGroup.UUID groupUuid = internalGroup.getGroupUUID();
@@ -139,14 +139,14 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
} catch (NoSuchGroupException e) {
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
}
- return toAccountInfoList(newMemberIds);
+ return Response.ok(toAccountInfoList(newMemberIds));
}
Account findAccount(String nameOrEmailOrId)
throws UnprocessableEntityException, IOException, ConfigInvalidException {
AccountResolver.Result result = accountResolver.resolve(nameOrEmailOrId);
try {
- return result.asUnique().getAccount();
+ return result.asUnique().account();
} catch (UnresolvableAccountException e) {
switch (authType) {
case HTTP_LDAP:
@@ -193,7 +193,7 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
req.setSkipAuthentication(true);
return accountCache
.get(accountManager.authenticate(req).getAccountId())
- .map(AccountState::getAccount);
+ .map(AccountState::account);
} catch (AccountException e) {
return Optional.empty();
}
@@ -221,15 +221,14 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
}
@Override
- public AccountInfo apply(GroupResource resource, IdString id, Input input)
- throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ public Response<AccountInfo> apply(GroupResource resource, IdString id, Input input)
+ throws Exception {
AddMembers.Input in = new AddMembers.Input();
in._oneMember = id.get();
try {
- List<AccountInfo> list = put.apply(resource, in);
+ List<AccountInfo> list = put.apply(resource, in).value();
if (list.size() == 1) {
- return list.get(0);
+ return Response.created(list.get(0));
}
throw new IllegalStateException();
} catch (UnprocessableEntityException e) {
@@ -248,7 +247,7 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
}
@Override
- public AccountInfo apply(MemberResource resource, Input input)
+ public Response<AccountInfo> apply(MemberResource resource, Input input)
throws PermissionBackendException {
// Do nothing, the user is already a member.
return get.apply(resource);
diff --git a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
index 5c879e721d..3fd3f2948c 100644
--- a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
@@ -19,17 +19,17 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResolver;
@@ -91,7 +91,7 @@ public class AddSubgroups implements RestModifyView<GroupResource, Input> {
}
@Override
- public List<GroupInfo> apply(GroupResource resource, Input input)
+ public Response<List<GroupInfo>> apply(GroupResource resource, Input input)
throws NotInternalGroupException, AuthException, UnprocessableEntityException,
ResourceNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
@@ -118,7 +118,7 @@ public class AddSubgroups implements RestModifyView<GroupResource, Input> {
} catch (NoSuchGroupException e) {
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
}
- return result;
+ return Response.ok(result);
}
private void addSubgroups(
@@ -142,15 +142,14 @@ public class AddSubgroups implements RestModifyView<GroupResource, Input> {
}
@Override
- public GroupInfo apply(GroupResource resource, IdString id, Input input)
- throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ public Response<GroupInfo> apply(GroupResource resource, IdString id, Input input)
+ throws Exception {
AddSubgroups.Input in = new AddSubgroups.Input();
in.groups = ImmutableList.of(id.get());
try {
- List<GroupInfo> list = addSubgroups.apply(resource, in);
+ List<GroupInfo> list = addSubgroups.apply(resource, in).value();
if (list.size() == 1) {
- return list.get(0);
+ return Response.created(list.get(0));
}
throw new IllegalStateException();
} catch (UnprocessableEntityException e) {
@@ -169,7 +168,7 @@ public class AddSubgroups implements RestModifyView<GroupResource, Input> {
}
@Override
- public GroupInfo apply(SubgroupResource resource, Input input)
+ public Response<GroupInfo> apply(SubgroupResource resource, Input input)
throws PermissionBackendException {
// Do nothing, the group is already included.
return get.get().apply(resource);
diff --git a/java/com/google/gerrit/server/restapi/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
index eaedcd7a18..7c5e7ed38b 100644
--- a/java/com/google/gerrit/server/restapi/group/CreateGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
@@ -19,6 +19,8 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.groups.GroupInput;
@@ -29,12 +31,11 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.UserInitiated;
@@ -122,7 +123,7 @@ public class CreateGroup
}
@Override
- public GroupInfo apply(TopLevelResource resource, IdString id, GroupInput input)
+ public Response<GroupInfo> apply(TopLevelResource resource, IdString id, GroupInput input)
throws AuthException, BadRequestException, UnprocessableEntityException,
ResourceConflictException, IOException, ConfigInvalidException, ResourceNotFoundException,
PermissionBackendException {
@@ -137,6 +138,16 @@ public class CreateGroup
AccountGroup.UUID ownerUuid = owner(input);
CreateGroupArgs args = new CreateGroupArgs();
args.setGroupName(name);
+ args.uuid = Strings.isNullOrEmpty(input.uuid) ? null : AccountGroup.UUID.parse(input.uuid);
+ if (args.uuid != null) {
+ if (!AccountGroup.isInternalGroup(args.uuid)) {
+ throw new BadRequestException(String.format("invalid group UUID '%s'", args.uuid.get()));
+ }
+ if (groupCache.get(args.uuid).isPresent()) {
+ throw new ResourceConflictException(
+ String.format("group with UUID '%s' already exists", args.uuid.get()));
+ }
+ }
args.groupDescription = Strings.emptyToNull(input.description);
args.visibleToAll = MoreObjects.firstNonNull(input.visibleToAll, defaultVisibleToAll);
args.ownerGroupUuid = ownerUuid;
@@ -148,7 +159,7 @@ public class CreateGroup
throw new UnprocessableEntityException(
String.format("Account Inactive: %s", nameOrEmailOrId));
}
- members.add(a.getId());
+ members.add(a.id());
}
args.initialMembers = members;
} else {
@@ -165,7 +176,7 @@ public class CreateGroup
throw new ResourceConflictException(e.getMessage(), e);
}
- return json.format(new InternalGroupDescription(createGroup(args)));
+ return Response.created(json.format(new InternalGroupDescription(createGroup(args))));
}
private AccountGroup.UUID owner(GroupInput input) throws UnprocessableEntityException {
@@ -193,11 +204,13 @@ public class CreateGroup
}
}
- AccountGroup.Id groupId = new AccountGroup.Id(sequences.nextGroupId());
+ AccountGroup.Id groupId = AccountGroup.id(sequences.nextGroupId());
AccountGroup.UUID uuid =
- GroupUUID.make(
- createGroupArgs.getGroupName(),
- self.get().newCommitterIdent(serverIdent.getWhen(), serverIdent.getTimeZone()));
+ MoreObjects.firstNonNull(
+ createGroupArgs.uuid,
+ GroupUUID.make(
+ createGroupArgs.getGroupName(),
+ self.get().newCommitterIdent(serverIdent.getWhen(), serverIdent.getTimeZone())));
InternalGroupCreation groupCreation =
InternalGroupCreation.builder()
.setGroupUUID(uuid)
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index b5771a34c3..b9fd00004c 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -16,6 +16,8 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -23,8 +25,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.GroupControl;
@@ -68,7 +68,7 @@ public class DeleteMembers implements RestModifyView<GroupResource, Input> {
Set<Account.Id> membersToRemove = new HashSet<>();
for (String nameOrEmail : input.members) {
- membersToRemove.add(accountResolver.resolve(nameOrEmail).asUnique().getAccount().getId());
+ membersToRemove.add(accountResolver.resolve(nameOrEmail).asUnique().account().id());
}
AccountGroup.UUID groupUuid = internalGroup.getGroupUUID();
try {
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
index 9ecfa1f696..b9d6ca80a9 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -24,7 +25,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResolver;
diff --git a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index 1a781d989d..508547d45e 100644
--- a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -17,15 +17,16 @@ package com.google.gerrit.server.restapi.group;
import static java.util.Comparator.comparing;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.AccountGroupByIdAudit;
+import com.google.gerrit.entities.AccountGroupMemberAudit;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.GroupAuditEventInfo;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupCache;
@@ -74,7 +75,7 @@ public class GetAuditLog implements RestReadView<GroupResource> {
}
@Override
- public List<? extends GroupAuditEventInfo> apply(GroupResource rsrc)
+ public Response<List<? extends GroupAuditEventInfo>> apply(GroupResource rsrc)
throws AuthException, NotInternalGroupException, IOException, ConfigInvalidException,
PermissionBackendException {
GroupDescription.Internal group =
@@ -90,22 +91,24 @@ public class GetAuditLog implements RestReadView<GroupResource> {
try (Repository allUsersRepo = repoManager.openRepository(allUsers)) {
for (AccountGroupMemberAudit auditEvent :
groups.getMembersAudit(allUsersRepo, group.getGroupUUID())) {
- AccountInfo member = accountLoader.get(auditEvent.getMemberId());
+ AccountInfo member = accountLoader.get(auditEvent.memberId());
auditEvents.add(
GroupAuditEventInfo.createAddUserEvent(
- accountLoader.get(auditEvent.getAddedBy()), auditEvent.getAddedOn(), member));
+ accountLoader.get(auditEvent.addedBy()), auditEvent.addedOn(), member));
if (!auditEvent.isActive()) {
auditEvents.add(
GroupAuditEventInfo.createRemoveUserEvent(
- accountLoader.get(auditEvent.getRemovedBy()), auditEvent.getRemovedOn(), member));
+ accountLoader.get(auditEvent.removedBy().orElse(null)),
+ auditEvent.removedOn(),
+ member));
}
}
- for (AccountGroupByIdAud auditEvent :
+ for (AccountGroupByIdAudit auditEvent :
groups.getSubgroupsAudit(allUsersRepo, group.getGroupUUID())) {
- AccountGroup.UUID includedGroupUUID = auditEvent.getIncludeUUID();
+ AccountGroup.UUID includedGroupUUID = auditEvent.includeUuid();
Optional<InternalGroup> includedGroup = groupCache.get(includedGroupUUID);
GroupInfo member;
if (includedGroup.isPresent()) {
@@ -121,14 +124,14 @@ public class GetAuditLog implements RestReadView<GroupResource> {
auditEvents.add(
GroupAuditEventInfo.createAddGroupEvent(
- accountLoader.get(auditEvent.getAddedBy()),
- auditEvent.getKey().getAddedOn(),
- member));
+ accountLoader.get(auditEvent.addedBy()), auditEvent.addedOn(), member));
if (!auditEvent.isActive()) {
auditEvents.add(
GroupAuditEventInfo.createRemoveGroupEvent(
- accountLoader.get(auditEvent.getRemovedBy()), auditEvent.getRemovedOn(), member));
+ accountLoader.get(auditEvent.removedBy().orElse(null)),
+ auditEvent.removedOn(),
+ member));
}
}
}
@@ -137,6 +140,6 @@ public class GetAuditLog implements RestReadView<GroupResource> {
// sort by date and then reverse so that the newest audit event comes first
auditEvents.sort(comparing((GroupAuditEventInfo a) -> a.date).reversed());
- return auditEvents;
+ return Response.ok(auditEvents);
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/GetDescription.java b/java/com/google/gerrit/server/restapi/group/GetDescription.java
index c34fda77f9..b77028118d 100644
--- a/java/com/google/gerrit/server/restapi/group/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDescription.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.GroupResource;
import com.google.inject.Singleton;
@@ -23,9 +24,9 @@ import com.google.inject.Singleton;
@Singleton
public class GetDescription implements RestReadView<GroupResource> {
@Override
- public String apply(GroupResource resource) throws NotInternalGroupException {
+ public Response<String> apply(GroupResource resource) throws NotInternalGroupException {
GroupDescription.Internal group =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
- return Strings.nullToEmpty(group.getDescription());
+ return Response.ok(Strings.nullToEmpty(group.getDescription()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/GetDetail.java b/java/com/google/gerrit/server/restapi/group/GetDetail.java
index c7573836a2..f6b89304f7 100644
--- a/java/com/google/gerrit/server/restapi/group/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDetail.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.group;
import com.google.gerrit.extensions.client.ListGroupsOption;
import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -32,7 +33,7 @@ public class GetDetail implements RestReadView<GroupResource> {
}
@Override
- public GroupInfo apply(GroupResource rsrc) throws PermissionBackendException {
- return json.format(rsrc);
+ public Response<GroupInfo> apply(GroupResource rsrc) throws PermissionBackendException {
+ return Response.ok(json.format(rsrc));
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/GetGroup.java b/java/com/google/gerrit/server/restapi/group/GetGroup.java
index 3ae447bf6e..4785d25f54 100644
--- a/java/com/google/gerrit/server/restapi/group/GetGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetGroup.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.group;
import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -31,7 +32,7 @@ public class GetGroup implements RestReadView<GroupResource> {
}
@Override
- public GroupInfo apply(GroupResource resource) throws PermissionBackendException {
- return json.format(resource.getGroup());
+ public Response<GroupInfo> apply(GroupResource resource) throws PermissionBackendException {
+ return Response.ok(json.format(resource.getGroup()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/GetMember.java b/java/com/google/gerrit/server/restapi/group/GetMember.java
index 63a8a1bba3..8dbcd27d5c 100644
--- a/java/com/google/gerrit/server/restapi/group/GetMember.java
+++ b/java/com/google/gerrit/server/restapi/group/GetMember.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.group;
import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.group.MemberResource;
@@ -32,10 +33,10 @@ public class GetMember implements RestReadView<MemberResource> {
}
@Override
- public AccountInfo apply(MemberResource rsrc) throws PermissionBackendException {
+ public Response<AccountInfo> apply(MemberResource rsrc) throws PermissionBackendException {
AccountLoader loader = infoFactory.create(true);
AccountInfo info = loader.get(rsrc.getMember().getAccountId());
loader.fill();
- return info;
+ return Response.ok(info);
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/GetName.java b/java/com/google/gerrit/server/restapi/group/GetName.java
index 8cc1fe0c2f..131dbe41c9 100644
--- a/java/com/google/gerrit/server/restapi/group/GetName.java
+++ b/java/com/google/gerrit/server/restapi/group/GetName.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.group;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.GroupResource;
import com.google.inject.Singleton;
@@ -22,7 +23,7 @@ import com.google.inject.Singleton;
public class GetName implements RestReadView<GroupResource> {
@Override
- public String apply(GroupResource resource) {
- return resource.getName();
+ public Response<String> apply(GroupResource resource) {
+ return Response.ok(resource.getName());
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/GetOptions.java b/java/com/google/gerrit/server/restapi/group/GetOptions.java
index e5bfe30100..5d8ba0251c 100644
--- a/java/com/google/gerrit/server/restapi/group/GetOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOptions.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.group;
import com.google.gerrit.extensions.common.GroupOptionsInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.GroupResource;
import com.google.inject.Singleton;
@@ -23,7 +24,7 @@ import com.google.inject.Singleton;
public class GetOptions implements RestReadView<GroupResource> {
@Override
- public GroupOptionsInfo apply(GroupResource resource) {
- return GroupJson.createOptions(resource.getGroup());
+ public Response<GroupOptionsInfo> apply(GroupResource resource) {
+ return Response.ok(GroupJson.createOptions(resource.getGroup()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/GetOwner.java b/java/com/google/gerrit/server/restapi/group/GetOwner.java
index 10e1b23dc1..e8bdfaae2b 100644
--- a/java/com/google/gerrit/server/restapi/group/GetOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOwner.java
@@ -18,6 +18,7 @@ import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
@@ -38,13 +39,13 @@ public class GetOwner implements RestReadView<GroupResource> {
}
@Override
- public GroupInfo apply(GroupResource resource)
+ public Response<GroupInfo> apply(GroupResource resource)
throws NotInternalGroupException, ResourceNotFoundException, PermissionBackendException {
GroupDescription.Internal group =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
try {
GroupControl c = controlFactory.validateFor(group.getOwnerGroupUUID());
- return json.format(c.getGroup());
+ return Response.ok(json.format(c.getGroup()));
} catch (NoSuchGroupException e) {
throw new ResourceNotFoundException(group.getOwnerGroupUUID().get(), e);
}
diff --git a/java/com/google/gerrit/server/restapi/group/GetSubgroup.java b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
index 446618009c..c209511f6c 100644
--- a/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.group;
import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.SubgroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -31,7 +32,7 @@ public class GetSubgroup implements RestReadView<SubgroupResource> {
}
@Override
- public GroupInfo apply(SubgroupResource rsrc) throws PermissionBackendException {
- return json.format(rsrc.getMemberDescription());
+ public Response<GroupInfo> apply(SubgroupResource rsrc) throws PermissionBackendException {
+ return Response.ok(json.format(rsrc.getMemberDescription()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/GroupJson.java b/java/com/google/gerrit/server/restapi/group/GroupJson.java
index 12b9d613a7..99c9df7152 100644
--- a/java/com/google/gerrit/server/restapi/group/GroupJson.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupJson.java
@@ -20,11 +20,11 @@ import static com.google.gerrit.extensions.client.ListGroupsOption.MEMBERS;
import com.google.common.base.Strings;
import com.google.common.base.Suppliers;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.client.ListGroupsOption;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.GroupOptionsInfo;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
diff --git a/java/com/google/gerrit/server/restapi/group/Index.java b/java/com/google/gerrit/server/restapi/group/Index.java
index 1267f7a836..d64669f3c5 100644
--- a/java/com/google/gerrit/server/restapi/group/Index.java
+++ b/java/com/google/gerrit/server/restapi/group/Index.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.restapi.group;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/restapi/group/ListGroups.java b/java/com/google/gerrit/server/restapi/group/ListGroups.java
index 7c52300e12..1802ea636f 100644
--- a/java/com/google/gerrit/server/restapi/group/ListGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListGroups.java
@@ -23,17 +23,18 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.client.ListGroupsOption;
import com.google.gerrit.extensions.client.ListOption;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
@@ -250,18 +251,16 @@ public class ListGroups implements RestReadView<TopLevelResource> {
}
@Override
- public SortedMap<String, GroupInfo> apply(TopLevelResource resource)
- throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
+ public Response<SortedMap<String, GroupInfo>> apply(TopLevelResource resource) throws Exception {
SortedMap<String, GroupInfo> output = new TreeMap<>();
for (GroupInfo info : get()) {
output.put(MoreObjects.firstNonNull(info.name, "Group " + Url.decode(info.id)), info);
info.name = null;
}
- return output;
+ return Response.ok(output);
}
- public List<GroupInfo> get()
- throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
+ public List<GroupInfo> get() throws Exception {
if (!Strings.isNullOrEmpty(suggest)) {
return suggestGroups();
}
@@ -279,7 +278,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
}
if (user != null) {
- return accountGetGroups.apply(new AccountResource(userFactory.create(user)));
+ return accountGetGroups.apply(new AccountResource(userFactory.create(user))).value();
}
return getAllGroups();
diff --git a/java/com/google/gerrit/server/restapi/group/ListMembers.java b/java/com/google/gerrit/server/restapi/group/ListMembers.java
index 75be44cbee..23f0aa7ace 100644
--- a/java/com/google/gerrit/server/restapi/group/ListMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/ListMembers.java
@@ -21,10 +21,11 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AccountInfoComparator;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.GroupCache;
@@ -65,14 +66,14 @@ public class ListMembers implements RestReadView<GroupResource> {
}
@Override
- public List<AccountInfo> apply(GroupResource resource)
+ public Response<List<AccountInfo>> apply(GroupResource resource)
throws NotInternalGroupException, PermissionBackendException {
GroupDescription.Internal group =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
if (recursive) {
- return getTransitiveMembers(group, resource.getControl());
+ return Response.ok(getTransitiveMembers(group, resource.getControl()));
}
- return getDirectMembers(group, resource.getControl());
+ return Response.ok(getDirectMembers(group, resource.getControl()));
}
public List<AccountInfo> getTransitiveMembers(AccountGroup.UUID groupUuid)
diff --git a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
index bb72a10d0b..540718ff7b 100644
--- a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
@@ -19,10 +19,11 @@ import static java.util.Comparator.comparing;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -45,12 +46,12 @@ public class ListSubgroups implements RestReadView<GroupResource> {
}
@Override
- public List<GroupInfo> apply(GroupResource rsrc)
+ public Response<List<GroupInfo>> apply(GroupResource rsrc)
throws NotInternalGroupException, PermissionBackendException {
GroupDescription.Internal group =
rsrc.asInternalGroup().orElseThrow(NotInternalGroupException::new);
- return getDirectSubgroups(group, rsrc.getControl());
+ return Response.ok(getDirectSubgroups(group, rsrc.getControl()));
}
public List<GroupInfo> getDirectSubgroups(
diff --git a/java/com/google/gerrit/server/restapi/group/PutDescription.java b/java/com/google/gerrit/server/restapi/group/PutDescription.java
index c4e6f09cfa..8fe4b20585 100644
--- a/java/com/google/gerrit/server/restapi/group/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/PutDescription.java
@@ -16,13 +16,13 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.DescriptionInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
diff --git a/java/com/google/gerrit/server/restapi/group/PutName.java b/java/com/google/gerrit/server/restapi/group/PutName.java
index adc20d523f..9a3c87d5d7 100644
--- a/java/com/google/gerrit/server/restapi/group/PutName.java
+++ b/java/com/google/gerrit/server/restapi/group/PutName.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.NameInput;
@@ -23,8 +24,8 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
@@ -45,7 +46,7 @@ public class PutName implements RestModifyView<GroupResource, NameInput> {
}
@Override
- public String apply(GroupResource rsrc, NameInput input)
+ public Response<String> apply(GroupResource rsrc, NameInput input)
throws NotInternalGroupException, AuthException, BadRequestException,
ResourceConflictException, ResourceNotFoundException, IOException,
ConfigInvalidException {
@@ -62,11 +63,11 @@ public class PutName implements RestModifyView<GroupResource, NameInput> {
}
if (internalGroup.getName().equals(newName)) {
- return newName;
+ return Response.ok(newName);
}
renameGroup(internalGroup, newName);
- return newName;
+ return Response.ok(newName);
}
private void renameGroup(GroupDescription.Internal group, String newName)
@@ -74,7 +75,7 @@ public class PutName implements RestModifyView<GroupResource, NameInput> {
ConfigInvalidException {
AccountGroup.UUID groupUuid = group.getGroupUUID();
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey(newName)).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey(newName)).build();
try {
groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
} catch (NoSuchGroupException e) {
diff --git a/java/com/google/gerrit/server/restapi/group/PutOptions.java b/java/com/google/gerrit/server/restapi/group/PutOptions.java
index 747d899a9b..53bf5715d8 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOptions.java
@@ -15,13 +15,14 @@
package com.google.gerrit.server.restapi.group;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.GroupOptionsInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
@@ -42,7 +43,7 @@ public class PutOptions implements RestModifyView<GroupResource, GroupOptionsInf
}
@Override
- public GroupOptionsInfo apply(GroupResource resource, GroupOptionsInfo input)
+ public Response<GroupOptionsInfo> apply(GroupResource resource, GroupOptionsInfo input)
throws NotInternalGroupException, AuthException, BadRequestException,
ResourceNotFoundException, IOException, ConfigInvalidException {
GroupDescription.Internal internalGroup =
@@ -73,6 +74,6 @@ public class PutOptions implements RestModifyView<GroupResource, GroupOptionsInf
if (input.visibleToAll) {
options.visibleToAll = true;
}
- return options;
+ return Response.ok(options);
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/PutOwner.java b/java/com/google/gerrit/server/restapi/group/PutOwner.java
index f766a84c1d..04129af65f 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOwner.java
@@ -16,15 +16,16 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.api.groups.OwnerInput;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResolver;
import com.google.gerrit.server.group.GroupResource;
@@ -54,7 +55,7 @@ public class PutOwner implements RestModifyView<GroupResource, OwnerInput> {
}
@Override
- public GroupInfo apply(GroupResource resource, OwnerInput input)
+ public Response<GroupInfo> apply(GroupResource resource, OwnerInput input)
throws ResourceNotFoundException, NotInternalGroupException, AuthException,
BadRequestException, UnprocessableEntityException, IOException, ConfigInvalidException,
PermissionBackendException {
@@ -79,6 +80,6 @@ public class PutOwner implements RestModifyView<GroupResource, OwnerInput> {
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
}
}
- return json.format(owner);
+ return Response.ok(json.format(owner));
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/QueryGroups.java b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
index b8dd28d7f3..a233111963 100644
--- a/java/com/google/gerrit/server/restapi/group/QueryGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
@@ -21,6 +21,7 @@ import com.google.gerrit.extensions.client.ListOption;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.index.query.QueryParseException;
@@ -97,7 +98,7 @@ public class QueryGroups implements RestReadView<TopLevelResource> {
}
@Override
- public List<GroupInfo> apply(TopLevelResource resource)
+ public Response<List<GroupInfo>> apply(TopLevelResource resource)
throws BadRequestException, MethodNotAllowedException, PermissionBackendException {
if (Strings.isNullOrEmpty(query)) {
throw new BadRequestException("missing query field");
@@ -129,7 +130,7 @@ public class QueryGroups implements RestReadView<TopLevelResource> {
if (!groupInfos.isEmpty() && result.more()) {
groupInfos.get(groupInfos.size() - 1)._moreGroups = true;
}
- return groupInfos;
+ return Response.ok(groupInfos);
} catch (QueryParseException e) {
throw new BadRequestException(e.getMessage());
}
diff --git a/java/com/google/gerrit/server/restapi/project/BanCommit.java b/java/com/google/gerrit/server/restapi/project/BanCommit.java
index 3d101b2484..64e38b0176 100644
--- a/java/com/google/gerrit/server/restapi/project/BanCommit.java
+++ b/java/com/google/gerrit/server/restapi/project/BanCommit.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.api.projects.BanCommitInput;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.server.git.BanCommitResult;
@@ -45,7 +46,7 @@ public class BanCommit
}
@Override
- protected BanResultInfo applyImpl(
+ protected Response<BanResultInfo> applyImpl(
BatchUpdate.Factory updateFactory, ProjectResource rsrc, BanCommitInput input)
throws RestApiException, UpdateException, IOException, PermissionBackendException {
BanResultInfo r = new BanResultInfo();
@@ -65,7 +66,7 @@ public class BanCommit
r.alreadyBanned = transformCommits(result.getAlreadyBannedCommits());
r.ignored = transformCommits(result.getIgnoredObjectIds());
}
- return r;
+ return Response.ok(r);
}
private static List<String> transformCommits(List<ObjectId> commits) {
diff --git a/java/com/google/gerrit/server/restapi/project/BranchesCollection.java b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
index 5fbb4f8b35..2d78bb05a5 100644
--- a/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.project;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
@@ -21,8 +23,6 @@ import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
diff --git a/java/com/google/gerrit/server/restapi/project/Check.java b/java/com/google/gerrit/server/restapi/project/Check.java
index a6fd7646ae..66a2df4bb6 100644
--- a/java/com/google/gerrit/server/restapi/project/Check.java
+++ b/java/com/google/gerrit/server/restapi/project/Check.java
@@ -19,6 +19,7 @@ import com.google.gerrit.extensions.api.projects.CheckProjectResultInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -40,9 +41,9 @@ public class Check implements RestModifyView<ProjectResource, CheckProjectInput>
}
@Override
- public CheckProjectResultInfo apply(ProjectResource rsrc, CheckProjectInput input)
+ public Response<CheckProjectResultInfo> apply(ProjectResource rsrc, CheckProjectInput input)
throws AuthException, BadRequestException, ResourceConflictException, Exception {
permissionBackend.user(rsrc.getUser()).check(GlobalPermission.ADMINISTRATE_SERVER);
- return projectsConsistencyChecker.check(rsrc.getNameKey(), input);
+ return Response.ok(projectsConsistencyChecker.check(rsrc.getNameKey(), input));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccess.java b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
index 67b68b578f..037a95396c 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
@@ -14,17 +14,18 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_HEADS;
+import static com.google.gerrit.entities.RefNames.REFS_HEADS;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.config.AccessCheckInfo;
import com.google.gerrit.extensions.api.config.AccessCheckInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.DefaultPermissionMappings;
@@ -59,7 +60,7 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
}
@Override
- public AccessCheckInfo apply(ProjectResource rsrc, AccessCheckInput input)
+ public Response<AccessCheckInfo> apply(ProjectResource rsrc, AccessCheckInput input)
throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
permissionBackend.user(rsrc.getUser()).check(GlobalPermission.VIEW_ACCESS);
@@ -72,7 +73,7 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
throw new BadRequestException("input requires 'account'");
}
- Account.Id match = accountResolver.resolve(input.account).asUnique().getAccount().getId();
+ Account.Id match = accountResolver.resolve(input.account).asUnique().account().id();
AccessCheckInfo info = new AccessCheckInfo();
try {
@@ -83,7 +84,7 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
} catch (AuthException e) {
info.message = String.format("user %s cannot see project %s", match, rsrc.getName());
info.status = HttpServletResponse.SC_FORBIDDEN;
- return info;
+ return Response.ok(info);
}
RefPermission refPerm;
@@ -106,7 +107,7 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
try {
permissionBackend
.absentUser(match)
- .ref(new Branch.NameKey(rsrc.getNameKey(), input.ref))
+ .ref(BranchNameKey.create(rsrc.getNameKey(), input.ref))
.check(refPerm);
} catch (AuthException e) {
info.status = HttpServletResponse.SC_FORBIDDEN;
@@ -114,7 +115,7 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
String.format(
"user %s lacks permission %s for %s in project %s",
match, input.permission, input.ref, rsrc.getName());
- return info;
+ return Response.ok(info);
}
} else {
// We say access is okay if there are no refs, but this warrants a warning,
@@ -126,6 +127,6 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
}
}
info.status = HttpServletResponse.SC_OK;
- return info;
+ return Response.ok(info);
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java b/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
index 770e8c3e97..6aaa6783fe 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.api.config.AccessCheckInfo;
import com.google.gerrit.extensions.api.config.AccessCheckInput;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -48,7 +49,7 @@ public class CheckAccessReadView implements RestReadView<ProjectResource> {
}
@Override
- public AccessCheckInfo apply(ProjectResource rsrc)
+ public Response<AccessCheckInfo> apply(ProjectResource rsrc)
throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
AccessCheckInput input = new AccessCheckInput();
diff --git a/java/com/google/gerrit/server/restapi/project/CheckMergeability.java b/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
index de2ac6458d..4864fdef28 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
@@ -14,10 +14,12 @@
package com.google.gerrit.server.restapi.project;
+import com.google.gerrit.exceptions.InvalidMergeStrategyException;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.MergeableInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -75,7 +77,7 @@ public class CheckMergeability implements RestReadView<BranchResource> {
}
@Override
- public MergeableInfo apply(BranchResource resource)
+ public Response<MergeableInfo> apply(BranchResource resource)
throws IOException, BadRequestException, ResourceNotFoundException {
if (!(submitType.equals(SubmitType.MERGE_ALWAYS)
|| submitType.equals(SubmitType.MERGE_IF_NECESSARY))) {
@@ -106,7 +108,7 @@ public class CheckMergeability implements RestReadView<BranchResource> {
result.mergeable = true;
result.commitMerged = true;
result.contentMerged = true;
- return result;
+ return Response.ok(result);
}
if (m.merge(false, targetCommit, sourceCommit)) {
@@ -119,9 +121,9 @@ public class CheckMergeability implements RestReadView<BranchResource> {
result.conflicts = ((ResolveMerger) m).getUnmergedPaths();
}
}
- } catch (IllegalArgumentException e) {
+ } catch (InvalidMergeStrategyException e) {
throw new BadRequestException(e.getMessage());
}
- return result;
+ return Response.ok(result);
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
index 8cc8298a02..a4a82ce2d7 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
@@ -14,10 +14,11 @@
package com.google.gerrit.server.restapi.project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.IncludedIn;
import com.google.gerrit.server.project.CommitResource;
import com.google.inject.Inject;
@@ -35,9 +36,9 @@ public class CommitIncludedIn implements RestReadView<CommitResource> {
}
@Override
- public IncludedInInfo apply(CommitResource rsrc) throws RestApiException, IOException {
+ public Response<IncludedInInfo> apply(CommitResource rsrc) throws RestApiException, IOException {
RevCommit commit = rsrc.getCommit();
Project.NameKey project = rsrc.getProjectState().getNameKey();
- return includedIn.apply(project, commit.getId().getName());
+ return Response.ok(includedIn.apply(project, commit.getId().getName()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
index d6c44691cc..d5380c67ae 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
@@ -16,14 +16,16 @@ package com.google.gerrit.server.restapi.project;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import com.google.common.base.Throwables;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.project.CommitResource;
@@ -32,6 +34,9 @@ import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.Reachable;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.RetryHelper.Action;
+import com.google.gerrit.server.update.RetryHelper.ActionType;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -49,6 +54,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
public class CommitsCollection implements ChildCollection<ProjectResource, CommitResource> {
private final DynamicMap<RestView<CommitResource>> views;
private final GitRepositoryManager repoManager;
+ private final RetryHelper retryHelper;
private final ChangeIndexCollection indexes;
private final Provider<InternalChangeQuery> queryProvider;
private final Reachable reachable;
@@ -57,11 +63,13 @@ public class CommitsCollection implements ChildCollection<ProjectResource, Commi
public CommitsCollection(
DynamicMap<RestView<CommitResource>> views,
GitRepositoryManager repoManager,
+ RetryHelper retryHelper,
ChangeIndexCollection indexes,
Provider<InternalChangeQuery> queryProvider,
Reachable reachable) {
this.views = views;
this.repoManager = repoManager;
+ this.retryHelper = retryHelper;
this.indexes = indexes;
this.queryProvider = queryProvider;
this.reachable = reachable;
@@ -115,7 +123,13 @@ public class CommitsCollection implements ChildCollection<ProjectResource, Commi
// Check first if any change references the commit in question. This is much cheaper than ref
// visibility filtering and reachability computation.
List<ChangeData> changes =
- queryProvider.get().enforceVisibility(true).setLimit(1).byProjectCommit(project, commit);
+ executeIndexQuery(
+ () ->
+ queryProvider
+ .get()
+ .enforceVisibility(true)
+ .setLimit(1)
+ .byProjectCommit(project, commit));
if (!changes.isEmpty()) {
return true;
}
@@ -128,4 +142,14 @@ public class CommitsCollection implements ChildCollection<ProjectResource, Commi
.collect(toImmutableList());
return reachable.fromRefs(project, repo, commit, refs);
}
+
+ private <T> T executeIndexQuery(Action<T> action) {
+ try {
+ return retryHelper.execute(
+ ActionType.INDEX_QUERY, action, StorageException.class::isInstance);
+ } catch (Exception e) {
+ Throwables.throwIfUnchecked(e);
+ throw new StorageException(e);
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
index 37bc265dee..5deace9ba4 100644
--- a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
+++ b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
@@ -17,6 +17,8 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
@@ -25,8 +27,6 @@ import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.PluginConfig;
diff --git a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
index 0741377759..fe48301825 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
@@ -16,6 +16,10 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.InvalidNameException;
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -24,10 +28,6 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeJson;
@@ -112,7 +112,7 @@ public class CreateAccessChange implements RestModifyView<ProjectResource, Proje
List<AccessSection> additions = setAccess.getAccessSections(input.add);
Project.NameKey newParentProjectName =
- input.parent == null ? null : new Project.NameKey(input.parent);
+ input.parent == null ? null : Project.nameKey(input.parent);
try (MetaDataUpdate md = metaDataUpdateUser.create(rsrc.getNameKey())) {
ProjectConfig config = projectConfigFactory.read(md);
@@ -134,11 +134,10 @@ public class CreateAccessChange implements RestModifyView<ProjectResource, Proje
md.setMessage("Review access change");
md.setInsertChangeId(true);
- Change.Id changeId = new Change.Id(seq.nextChangeId());
+ Change.Id changeId = Change.id(seq.nextChangeId());
RevCommit commit =
- config.commitToNewRef(
- md, new PatchSet.Id(changeId, Change.INITIAL_PATCH_SET_ID).toRefName());
+ config.commitToNewRef(md, PatchSet.id(changeId, Change.INITIAL_PATCH_SET_ID).toRefName());
if (commit.name().equals(oldCommitSha1)) {
throw new BadRequestException("no change");
diff --git a/java/com/google/gerrit/server/restapi/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
index 01e0cdfc41..fd6e024732 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
@@ -14,19 +14,20 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static com.google.gerrit.entities.RefNames.isConfigRef;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -82,7 +83,7 @@ public class CreateBranch
}
@Override
- public BranchInfo apply(ProjectResource rsrc, IdString id, BranchInput input)
+ public Response<BranchInfo> apply(ProjectResource rsrc, IdString id, BranchInput input)
throws BadRequestException, AuthException, ResourceConflictException, IOException,
PermissionBackendException, NoSuchProjectException {
String ref = id.get();
@@ -112,7 +113,7 @@ public class CreateBranch
+ "\"");
}
- final Branch.NameKey name = new Branch.NameKey(rsrc.getNameKey(), ref);
+ final BranchNameKey name = BranchNameKey.create(rsrc.getNameKey(), ref);
try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
ObjectId revid = RefUtil.parseBaseRevision(repo, rsrc.getNameKey(), input.revision);
RevWalk rw = RefUtil.verifyConnected(repo, revid);
@@ -144,7 +145,7 @@ public class CreateBranch
case NEW:
case NO_CHANGE:
referenceUpdated.fire(
- name.getParentKey(), u, ReceiveCommand.Type.CREATE, identifiedUser.get().state());
+ name.project(), u, ReceiveCommand.Type.CREATE, identifiedUser.get().state());
break;
case LOCK_FAILURE:
if (repo.getRefDatabase().exactRef(ref) != null) {
@@ -182,7 +183,7 @@ public class CreateBranch
info.ref = ref;
info.revision = revid.getName();
- if (isConfigRef(name.get())) {
+ if (isConfigRef(name.branch())) {
// Never allow to delete the meta config branch.
info.canDelete = null;
} else {
@@ -192,7 +193,7 @@ public class CreateBranch
? true
: null;
}
- return info;
+ return Response.created(info);
} catch (IOException err) {
logger.atSevere().withCause(err).log("Cannot create branch \"%s\"", name);
throw err;
diff --git a/java/com/google/gerrit/server/restapi/project/CreateDashboard.java b/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
index e8b6236e89..314df73238 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateDashboard.java
@@ -19,15 +19,12 @@ import com.google.gerrit.extensions.api.projects.SetDashboardInput;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.DashboardResource;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.IOException;
import org.kohsuke.args4j.Option;
@Singleton
@@ -45,14 +42,16 @@ public class CreateDashboard
@Override
public Response<DashboardInfo> apply(ProjectResource parent, IdString id, SetDashboardInput input)
- throws RestApiException, IOException, PermissionBackendException {
+ throws Exception {
parent.getProjectState().checkStatePermitsWrite();
if (!DashboardsCollection.isDefaultDashboard(id)) {
throw new ResourceNotFoundException(id);
}
SetDefaultDashboard set = setDefault.get();
set.inherited = inherited;
- return set.apply(
- DashboardResource.projectDefault(parent.getProjectState(), parent.getUser()), input);
+ return Response.created(
+ set.apply(
+ DashboardResource.projectDefault(parent.getProjectState(), parent.getUser()), input)
+ .value());
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java
index 8b81f10286..a5a00346bd 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateProject.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java
@@ -20,6 +20,7 @@ import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
@@ -33,7 +34,6 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ProjectUtil;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
diff --git a/java/com/google/gerrit/server/restapi/project/CreateTag.java b/java/com/google/gerrit/server/restapi/project/CreateTag.java
index 6a04e0dac3..5cfb118d28 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateTag.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateTag.java
@@ -25,6 +25,7 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
import com.google.gerrit.server.WebLinks;
@@ -78,7 +79,7 @@ public class CreateTag implements RestCollectionCreateView<ProjectResource, TagR
}
@Override
- public TagInfo apply(ProjectResource resource, IdString id, TagInput input)
+ public Response<TagInfo> apply(ProjectResource resource, IdString id, TagInput input)
throws RestApiException, IOException, PermissionBackendException, NoSuchProjectException {
String ref = id.get();
if (input == null) {
@@ -146,7 +147,8 @@ public class CreateTag implements RestCollectionCreateView<ProjectResource, TagR
result.getObjectId(),
resource.getUser().asIdentifiedUser().state());
try (RevWalk w = new RevWalk(repo)) {
- return ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links);
+ return Response.created(
+ ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links));
}
}
} catch (InvalidRevisionException e) {
diff --git a/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java b/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
index 4dc83e87c1..ca48109065 100644
--- a/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/DashboardsCollection.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
+import static com.google.gerrit.entities.RefNames.REFS_DASHBOARDS;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
@@ -22,6 +22,7 @@ import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.DashboardInfo;
import com.google.gerrit.extensions.api.projects.DashboardSectionInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
@@ -33,7 +34,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.UrlEncoded;
import com.google.gerrit.server.git.GitRepositoryManager;
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
index b94fb51bf7..6248a61cd9 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
@@ -14,16 +14,16 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static com.google.gerrit.entities.RefNames.isConfigRef;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.BranchResource;
import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -47,12 +47,12 @@ public class DeleteBranch implements RestModifyView<BranchResource, Input> {
@Override
public Response<?> apply(BranchResource rsrc, Input input)
throws RestApiException, IOException, PermissionBackendException {
- if (RefNames.HEAD.equals(rsrc.getBranchKey().get())) {
+ if (RefNames.HEAD.equals(rsrc.getBranchKey().branch())) {
throw new MethodNotAllowedException("not allowed to delete HEAD");
- } else if (isConfigRef(rsrc.getBranchKey().get())) {
+ } else if (isConfigRef(rsrc.getBranchKey().branch())) {
// Never allow to delete the meta config branch.
throw new MethodNotAllowedException(
- "not allowed to delete branch " + rsrc.getBranchKey().get());
+ "not allowed to delete branch " + rsrc.getBranchKey().branch());
}
if (!queryProvider.get().setLimit(1).byBranchOpen(rsrc.getBranchKey()).isEmpty()) {
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranches.java b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
index ba25e3394e..ca5962e1b5 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
@@ -17,13 +17,13 @@ package com.google.gerrit.server.restapi.project;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.DeleteBranchesInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java b/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
index 2702d58803..9d9e5f5f39 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteDashboard.java
@@ -18,14 +18,11 @@ import com.google.gerrit.extensions.api.projects.DashboardInfo;
import com.google.gerrit.extensions.api.projects.SetDashboardInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.DashboardResource;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.IOException;
@Singleton
public class DeleteDashboard implements RestModifyView<DashboardResource, SetDashboardInput> {
@@ -38,7 +35,7 @@ public class DeleteDashboard implements RestModifyView<DashboardResource, SetDas
@Override
public Response<DashboardInfo> apply(DashboardResource resource, SetDashboardInput input)
- throws RestApiException, IOException, PermissionBackendException {
+ throws Exception {
if (resource.isProjectDefault()) {
SetDashboardInput in = new SetDashboardInput();
in.commitMessage = input != null ? input.commitMessage : null;
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteRef.java b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
index dae759ae79..1979d611ed 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteRef.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.restapi.project;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static com.google.gerrit.entities.RefNames.isConfigRef;
import static java.lang.String.format;
import static org.eclipse.jgit.lib.Constants.R_REFS;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -25,11 +25,11 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -260,7 +260,7 @@ public class DeleteRef {
}
if (!refName.startsWith(R_TAGS)) {
- Branch.NameKey branchKey = new Branch.NameKey(projectState.getNameKey(), ref.getName());
+ BranchNameKey branchKey = BranchNameKey.create(projectState.getNameKey(), ref.getName());
if (!queryProvider.get().setLimit(1).byBranchOpen(branchKey).isEmpty()) {
command.setResult(Result.REJECTED_OTHER_REASON, "it has open changes");
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteTag.java b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
index 33955eec04..545b75270f 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteTag.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static com.google.gerrit.entities.RefNames.isConfigRef;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
diff --git a/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java b/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
index 09f973b50f..0ee82795ea 100644
--- a/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
@@ -14,14 +14,16 @@
package com.google.gerrit.server.restapi.project;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.server.change.FileInfoJson;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListKey;
@@ -32,6 +34,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
+import java.util.Map;
import org.eclipse.jgit.revwalk.RevCommit;
import org.kohsuke.args4j.Option;
@@ -82,7 +85,8 @@ public class FilesInCommitCollection implements ChildCollection<CommitResource,
}
@Override
- public Object apply(CommitResource resource) throws PatchListNotAvailableException {
+ public Response<Map<String, FileInfo>> apply(CommitResource resource)
+ throws PatchListNotAvailableException {
RevCommit commit = resource.getCommit();
PatchListKey key;
@@ -94,7 +98,7 @@ public class FilesInCommitCollection implements ChildCollection<CommitResource,
key = PatchListKey.againstCommit(null, commit, DiffPreferencesInfo.Whitespace.IGNORE_NONE);
}
- return fileInfoJson.toFileInfoMap(resource.getProjectState().getNameKey(), key);
+ return Response.ok(fileInfoJson.toFileInfoMap(resource.getProjectState().getNameKey(), key));
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GarbageCollect.java b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
index 23115ded26..25a2c90d7e 100644
--- a/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
+++ b/java/com/google/gerrit/server/restapi/project/GarbageCollect.java
@@ -19,13 +19,13 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.common.data.GarbageCollectionResult;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.UrlFormatter;
import com.google.gerrit.server.git.GarbageCollection;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -71,12 +71,12 @@ public class GarbageCollect
}
@Override
- public Object apply(ProjectResource rsrc, Input input) {
+ public Response<?> apply(ProjectResource rsrc, Input input) {
Project.NameKey project = rsrc.getNameKey();
if (input.async) {
return applyAsync(project, input);
}
- return applySync(project, input);
+ return Response.ok(applySync(project, input));
}
private Response.Accepted applyAsync(Project.NameKey project, Input input) {
diff --git a/java/com/google/gerrit/server/restapi/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
index d6c07ddf0e..a9a94032c3 100644
--- a/java/com/google/gerrit/server/restapi/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -29,6 +29,9 @@ import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.access.AccessSectionInfo;
import com.google.gerrit.extensions.api.access.PermissionInfo;
import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
@@ -37,10 +40,8 @@ import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.account.GroupBackend;
@@ -116,18 +117,16 @@ public class GetAccess implements RestReadView<ProjectResource> {
this.projectConfigFactory = projectConfigFactory;
}
- public ProjectAccessInfo apply(Project.NameKey nameKey)
- throws ResourceNotFoundException, ResourceConflictException, IOException,
- PermissionBackendException {
+ public ProjectAccessInfo apply(Project.NameKey nameKey) throws Exception {
ProjectState state = projectCache.checkedGet(nameKey);
if (state == null) {
throw new ResourceNotFoundException(nameKey.get());
}
- return apply(new ProjectResource(state, user.get()));
+ return apply(new ProjectResource(state, user.get())).value();
}
@Override
- public ProjectAccessInfo apply(ProjectResource rsrc)
+ public Response<ProjectAccessInfo> apply(ProjectResource rsrc)
throws ResourceNotFoundException, ResourceConflictException, IOException,
PermissionBackendException {
// Load the current configuration from the repository, ensuring it's the most
@@ -275,7 +274,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
.filter(e -> e.getValue() != null)
.collect(toMap(e -> e.getKey().get(), Map.Entry::getValue));
- return info;
+ return Response.ok(info);
}
private void loadGroup(Map<AccountGroup.UUID, GroupInfo> groups, AccountGroup.UUID id) {
diff --git a/java/com/google/gerrit/server/restapi/project/GetBranch.java b/java/com/google/gerrit/server/restapi/project/GetBranch.java
index 7d32f3da86..52a47a4493 100644
--- a/java/com/google/gerrit/server/restapi/project/GetBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/GetBranch.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.BranchResource;
@@ -34,8 +35,8 @@ public class GetBranch implements RestReadView<BranchResource> {
}
@Override
- public BranchInfo apply(BranchResource rsrc)
+ public Response<BranchInfo> apply(BranchResource rsrc)
throws ResourceNotFoundException, IOException, PermissionBackendException {
- return list.get().toBranchInfo(rsrc);
+ return Response.ok(list.get().toBranchInfo(rsrc));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetChildProject.java b/java/com/google/gerrit/server/restapi/project/GetChildProject.java
index e69907eaa5..b90f6ee9d1 100644
--- a/java/com/google/gerrit/server/restapi/project/GetChildProject.java
+++ b/java/com/google/gerrit/server/restapi/project/GetChildProject.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.project.ChildProjectResource;
import com.google.gerrit.server.project.ProjectJson;
@@ -37,9 +38,9 @@ public class GetChildProject implements RestReadView<ChildProjectResource> {
}
@Override
- public ProjectInfo apply(ChildProjectResource rsrc) throws ResourceNotFoundException {
+ public Response<ProjectInfo> apply(ChildProjectResource rsrc) throws ResourceNotFoundException {
if (recursive || rsrc.isDirectChild()) {
- return json.format(rsrc.getChild().getProject());
+ return Response.ok(json.format(rsrc.getChild().getProject()));
}
throw new ResourceNotFoundException(rsrc.getChild().getName());
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetCommit.java b/java/com/google/gerrit/server/restapi/project/GetCommit.java
index 1c1ae907cd..cca6a1a5de 100644
--- a/java/com/google/gerrit/server/restapi/project/GetCommit.java
+++ b/java/com/google/gerrit/server/restapi/project/GetCommit.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.git.CommitUtil;
import com.google.gerrit.server.project.CommitResource;
@@ -25,7 +26,7 @@ import java.io.IOException;
public class GetCommit implements RestReadView<CommitResource> {
@Override
- public CommitInfo apply(CommitResource rsrc) throws IOException {
- return CommitUtil.toCommitInfo(rsrc.getCommit());
+ public Response<CommitInfo> apply(CommitResource rsrc) throws IOException {
+ return Response.ok(CommitUtil.toCommitInfo(rsrc.getCommit()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetConfig.java b/java/com/google/gerrit/server/restapi/project/GetConfig.java
index b3ad96233a..ce45e7de8d 100644
--- a/java/com/google/gerrit/server/restapi/project/GetConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/GetConfig.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.EnableSignedPush;
@@ -53,15 +54,16 @@ public class GetConfig implements RestReadView<ProjectResource> {
}
@Override
- public ConfigInfo apply(ProjectResource resource) {
- return new ConfigInfoImpl(
- serverEnableSignedPush,
- resource.getProjectState(),
- resource.getUser(),
- pluginConfigEntries,
- cfgFactory,
- allProjects,
- uiActions,
- views);
+ public Response<ConfigInfo> apply(ProjectResource resource) {
+ return Response.ok(
+ new ConfigInfoImpl(
+ serverEnableSignedPush,
+ resource.getProjectState(),
+ resource.getUser(),
+ pluginConfigEntries,
+ cfgFactory,
+ allProjects,
+ uiActions,
+ views));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetContent.java b/java/com/google/gerrit/server/restapi/project/GetContent.java
index 132b644b4c..4e3fc8eeeb 100644
--- a/java/com/google/gerrit/server/restapi/project/GetContent.java
+++ b/java/com/google/gerrit/server/restapi/project/GetContent.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.FileContentUtil;
import com.google.gerrit.server.project.FileResource;
@@ -34,8 +35,9 @@ public class GetContent implements RestReadView<FileResource> {
}
@Override
- public BinaryResult apply(FileResource rsrc)
+ public Response<BinaryResult> apply(FileResource rsrc)
throws ResourceNotFoundException, BadRequestException, IOException {
- return fileContentUtil.getContent(rsrc.getProjectState(), rsrc.getRev(), rsrc.getPath(), null);
+ return Response.ok(
+ fileContentUtil.getContent(rsrc.getProjectState(), rsrc.getRev(), rsrc.getPath(), null));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetDashboard.java b/java/com/google/gerrit/server/restapi/project/GetDashboard.java
index 2ec67e72a7..928a36fc09 100644
--- a/java/com/google/gerrit/server/restapi/project/GetDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/GetDashboard.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
+import static com.google.gerrit.entities.RefNames.REFS_DASHBOARDS;
import static com.google.gerrit.server.restapi.project.DashboardsCollection.isDefaultDashboard;
import com.google.common.base.Splitter;
@@ -25,6 +25,7 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.Url;
@@ -56,7 +57,7 @@ public class GetDashboard implements RestReadView<DashboardResource> {
}
@Override
- public DashboardInfo apply(DashboardResource rsrc)
+ public Response<DashboardInfo> apply(DashboardResource rsrc)
throws RestApiException, IOException, PermissionBackendException {
if (inherited && !rsrc.isProjectDefault()) {
throw new BadRequestException("inherited flag can only be used with default");
@@ -71,13 +72,14 @@ public class GetDashboard implements RestReadView<DashboardResource> {
}
}
- return DashboardsCollection.parse(
- rsrc.getProjectState().getProject(),
- rsrc.getRefName().substring(REFS_DASHBOARDS.length()),
- rsrc.getPathName(),
- rsrc.getConfig(),
- rsrc.getProjectState().getName(),
- true);
+ return Response.ok(
+ DashboardsCollection.parse(
+ rsrc.getProjectState().getProject(),
+ rsrc.getRefName().substring(REFS_DASHBOARDS.length()),
+ rsrc.getPathName(),
+ rsrc.getConfig(),
+ rsrc.getProjectState().getName(),
+ true));
}
private DashboardResource defaultOf(ProjectState projectState, CurrentUser user)
diff --git a/java/com/google/gerrit/server/restapi/project/GetDescription.java b/java/com/google/gerrit/server/restapi/project/GetDescription.java
index d387ff1980..2561b916a0 100644
--- a/java/com/google/gerrit/server/restapi/project/GetDescription.java
+++ b/java/com/google/gerrit/server/restapi/project/GetDescription.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Singleton;
@@ -22,7 +23,7 @@ import com.google.inject.Singleton;
@Singleton
public class GetDescription implements RestReadView<ProjectResource> {
@Override
- public String apply(ProjectResource rsrc) {
- return Strings.nullToEmpty(rsrc.getProjectState().getProject().getDescription());
+ public Response<String> apply(ProjectResource rsrc) {
+ return Response.ok(Strings.nullToEmpty(rsrc.getProjectState().getProject().getDescription()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetHead.java b/java/com/google/gerrit/server/restapi/project/GetHead.java
index 043991fc59..4e0a144799 100644
--- a/java/com/google/gerrit/server/restapi/project/GetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/GetHead.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -52,7 +53,7 @@ public class GetHead implements RestReadView<ProjectResource> {
}
@Override
- public String apply(ProjectResource rsrc)
+ public Response<String> apply(ProjectResource rsrc)
throws AuthException, ResourceNotFoundException, IOException, PermissionBackendException {
rsrc.getProjectState().statePermitsRead();
try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
@@ -66,12 +67,12 @@ public class GetHead implements RestReadView<ProjectResource> {
.project(rsrc.getNameKey())
.ref(n)
.check(RefPermission.READ);
- return n;
+ return Response.ok(n);
} else if (head.getObjectId() != null) {
try (RevWalk rw = new RevWalk(repo)) {
RevCommit commit = rw.parseCommit(head.getObjectId());
if (commits.canRead(rsrc.getProjectState(), repo, commit)) {
- return head.getObjectId().name();
+ return Response.ok(head.getObjectId().name());
}
throw new AuthException("not allowed to see HEAD");
} catch (MissingObjectException | IncorrectObjectTypeException e) {
diff --git a/java/com/google/gerrit/server/restapi/project/GetParent.java b/java/com/google/gerrit/server/restapi/project/GetParent.java
index a4942e3e70..9b93d5bb3b 100644
--- a/java/com/google/gerrit/server/restapi/project/GetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/GetParent.java
@@ -14,8 +14,9 @@
package com.google.gerrit.server.restapi.project;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
@@ -31,9 +32,9 @@ public class GetParent implements RestReadView<ProjectResource> {
}
@Override
- public String apply(ProjectResource resource) {
+ public Response<String> apply(ProjectResource resource) {
Project project = resource.getProjectState().getProject();
Project.NameKey parentName = project.getParent(allProjectsName);
- return parentName != null ? parentName.get() : "";
+ return Response.ok(parentName != null ? parentName.get() : "");
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetProject.java b/java/com/google/gerrit/server/restapi/project/GetProject.java
index 26159e428e..2f7d370950 100644
--- a/java/com/google/gerrit/server/restapi/project/GetProject.java
+++ b/java/com/google/gerrit/server/restapi/project/GetProject.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.project.ProjectJson;
import com.google.gerrit.server.project.ProjectResource;
@@ -32,7 +33,7 @@ public class GetProject implements RestReadView<ProjectResource> {
}
@Override
- public ProjectInfo apply(ProjectResource rsrc) {
- return json.format(rsrc.getProjectState());
+ public Response<ProjectInfo> apply(ProjectResource rsrc) {
+ return Response.ok(json.format(rsrc.getProjectState()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetReflog.java b/java/com/google/gerrit/server/restapi/project/GetReflog.java
index a690042186..f9c6fd9bb6 100644
--- a/java/com/google/gerrit/server/restapi/project/GetReflog.java
+++ b/java/com/google/gerrit/server/restapi/project/GetReflog.java
@@ -19,6 +19,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.api.projects.ReflogEntryInfo;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CommonConverters;
@@ -89,7 +90,7 @@ public class GetReflog implements RestReadView<BranchResource> {
}
@Override
- public List<ReflogEntryInfo> apply(BranchResource rsrc)
+ public Response<List<ReflogEntryInfo>> apply(BranchResource rsrc)
throws RestApiException, IOException, PermissionBackendException {
permissionBackend
.user(rsrc.getUser())
@@ -123,7 +124,7 @@ public class GetReflog implements RestReadView<BranchResource> {
}
}
}
- return Lists.transform(entries, this::newReflogEntryInfo);
+ return Response.ok(Lists.transform(entries, this::newReflogEntryInfo));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetStatistics.java b/java/com/google/gerrit/server/restapi/project/GetStatistics.java
index a40806227e..db97855f52 100644
--- a/java/com/google/gerrit/server/restapi/project/GetStatistics.java
+++ b/java/com/google/gerrit/server/restapi/project/GetStatistics.java
@@ -18,6 +18,7 @@ import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ProjectResource;
@@ -42,11 +43,11 @@ public class GetStatistics implements RestReadView<ProjectResource> {
}
@Override
- public RepositoryStatistics apply(ProjectResource rsrc)
+ public Response<RepositoryStatistics> apply(ProjectResource rsrc)
throws ResourceNotFoundException, ResourceConflictException {
try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
GarbageCollectCommand gc = Git.wrap(repo).gc();
- return new RepositoryStatistics(gc.getStatistics());
+ return Response.ok(new RepositoryStatistics(gc.getStatistics()));
} catch (GitAPIException | JGitInternalException e) {
throw new ResourceConflictException(e.getMessage());
} catch (IOException e) {
diff --git a/java/com/google/gerrit/server/restapi/project/GetTag.java b/java/com/google/gerrit/server/restapi/project/GetTag.java
index 6d5a510ba9..6ab2f8bbc4 100644
--- a/java/com/google/gerrit/server/restapi/project/GetTag.java
+++ b/java/com/google/gerrit/server/restapi/project/GetTag.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.api.projects.TagInfo;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.project.TagResource;
import com.google.inject.Singleton;
@@ -23,7 +24,7 @@ import com.google.inject.Singleton;
public class GetTag implements RestReadView<TagResource> {
@Override
- public TagInfo apply(TagResource resource) {
- return resource.getTagInfo();
+ public Response<TagInfo> apply(TagResource resource) {
+ return Response.ok(resource.getTagInfo());
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/Index.java b/java/com/google/gerrit/server/restapi/project/Index.java
index bc58b23db0..b14380aca6 100644
--- a/java/com/google/gerrit/server/restapi/project/Index.java
+++ b/java/com/google/gerrit/server/restapi/project/Index.java
@@ -18,21 +18,18 @@ import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.projects.IndexProjectInput;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.index.project.ProjectIndexer;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.index.IndexExecutor;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.IOException;
import java.util.concurrent.Future;
@RequiresCapability(GlobalCapability.MAINTAIN_SERVER)
@@ -53,14 +50,14 @@ public class Index implements RestModifyView<ProjectResource, IndexProjectInput>
}
@Override
- public Response.Accepted apply(ProjectResource rsrc, IndexProjectInput input)
- throws IOException, PermissionBackendException, RestApiException {
+ public Response.Accepted apply(ProjectResource rsrc, IndexProjectInput input) throws Exception {
String response = "Project " + rsrc.getName() + " submitted for reindexing";
reindex(rsrc.getNameKey(), input.async);
if (Boolean.TRUE.equals(input.indexChildren)) {
- for (ProjectInfo child : listChildProjectsProvider.get().withRecursive(true).apply(rsrc)) {
- reindex(new Project.NameKey(child.name), input.async);
+ for (ProjectInfo child :
+ listChildProjectsProvider.get().withRecursive(true).apply(rsrc).value()) {
+ reindex(Project.nameKey(child.name), input.async);
}
response += " (indexing children recursively)";
diff --git a/java/com/google/gerrit/server/restapi/project/IndexChanges.java b/java/com/google/gerrit/server/restapi/project/IndexChanges.java
index b6b3d6b101..45a6616111 100644
--- a/java/com/google/gerrit/server/restapi/project/IndexChanges.java
+++ b/java/com/google/gerrit/server/restapi/project/IndexChanges.java
@@ -19,11 +19,11 @@ import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
import com.google.common.io.ByteStreams;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.MultiProgressMonitor;
import com.google.gerrit.server.git.MultiProgressMonitor.Task;
import com.google.gerrit.server.index.IndexExecutor;
diff --git a/java/com/google/gerrit/server/restapi/project/ListBranches.java b/java/com/google/gerrit/server/restapi/project/ListBranches.java
index ae9ef28d2b..fecdc8e255 100644
--- a/java/com/google/gerrit/server/restapi/project/ListBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/ListBranches.java
@@ -14,11 +14,12 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static com.google.gerrit.entities.RefNames.isConfigRef;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
import com.google.gerrit.extensions.common.ActionInfo;
@@ -26,11 +27,11 @@ import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.extensions.webui.UiActions;
@@ -127,15 +128,16 @@ public class ListBranches implements RestReadView<ProjectResource> {
}
@Override
- public List<BranchInfo> apply(ProjectResource rsrc)
+ public Response<ImmutableList<BranchInfo>> apply(ProjectResource rsrc)
throws RestApiException, IOException, PermissionBackendException {
rsrc.getProjectState().checkStatePermitsRead();
- return new RefFilter<BranchInfo>(Constants.R_HEADS)
- .subString(matchSubstring)
- .regex(matchRegex)
- .start(start)
- .limit(limit)
- .filter(allBranches(rsrc));
+ return Response.ok(
+ new RefFilter<BranchInfo>(Constants.R_HEADS)
+ .subString(matchSubstring)
+ .regex(matchRegex)
+ .start(start)
+ .limit(limit)
+ .filter(allBranches(rsrc)));
}
BranchInfo toBranchInfo(BranchResource rsrc)
@@ -278,7 +280,8 @@ public class ListBranches implements RestReadView<ProjectResource> {
info.actions.put(d.getId(), new ActionInfo(d));
}
- List<WebLinkInfo> links = webLinks.getBranchLinks(projectState.getName(), ref.getName());
+ ImmutableList<WebLinkInfo> links =
+ webLinks.getBranchLinks(projectState.getName(), ref.getName());
info.webLinks = links.isEmpty() ? null : links;
return info;
}
diff --git a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
index 387972032e..0bd053e27e 100644
--- a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
@@ -16,12 +16,13 @@ package com.google.gerrit.server.restapi.project;
import static java.util.stream.Collectors.toList;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
@@ -65,7 +66,7 @@ public class ListChildProjects implements RestReadView<ProjectResource> {
}
@Override
- public List<ProjectInfo> apply(ProjectResource rsrc)
+ public Response<List<ProjectInfo>> apply(ProjectResource rsrc)
throws PermissionBackendException, RestApiException {
if (limit < 0) {
throw new BadRequestException("limit must be a positive number");
@@ -75,20 +76,17 @@ public class ListChildProjects implements RestReadView<ProjectResource> {
}
rsrc.getProjectState().checkStatePermitsRead();
if (recursive) {
- return childProjects.list(rsrc.getNameKey());
+ return Response.ok(childProjects.list(rsrc.getNameKey()));
}
- return directChildProjects(rsrc.getNameKey());
+ return Response.ok(directChildProjects(rsrc.getNameKey()));
}
private List<ProjectInfo> directChildProjects(Project.NameKey parent) throws RestApiException {
PermissionBackend.WithUser currentUser = permissionBackend.currentUser();
return queryProvider.get().withQuery("parent:" + parent.get()).withLimit(limit).apply().stream()
.filter(
- p ->
- currentUser
- .project(new Project.NameKey(p.name))
- .testOrFalse(ProjectPermission.ACCESS))
+ p -> currentUser.project(Project.nameKey(p.name)).testOrFalse(ProjectPermission.ACCESS))
.collect(toList());
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/ListDashboards.java b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
index fd8668ad10..440671934b 100644
--- a/java/com/google/gerrit/server/restapi/project/ListDashboards.java
+++ b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
@@ -14,15 +14,16 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
+import static com.google.gerrit.entities.RefNames.REFS_DASHBOARDS;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.DashboardInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -63,11 +64,11 @@ public class ListDashboards implements RestReadView<ProjectResource> {
}
@Override
- public List<?> apply(ProjectResource rsrc)
+ public Response<List<?>> apply(ProjectResource rsrc)
throws ResourceNotFoundException, IOException, PermissionBackendException {
String project = rsrc.getName();
if (!inherited) {
- return scan(rsrc.getProjectState(), project, true);
+ return Response.ok(scan(rsrc.getProjectState(), project, true));
}
List<List<DashboardInfo>> all = new ArrayList<>();
@@ -83,7 +84,7 @@ public class ListDashboards implements RestReadView<ProjectResource> {
all.add(list);
}
}
- return all;
+ return Response.ok(all);
}
private Collection<ProjectState> tree(ProjectResource rsrc) throws PermissionBackendException {
diff --git a/java/com/google/gerrit/server/restapi/project/ListProjects.java b/java/com/google/gerrit/server/restapi/project/ListProjects.java
index c583923e87..63842827f3 100644
--- a/java/com/google/gerrit/server/restapi/project/ListProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListProjects.java
@@ -27,6 +27,9 @@ import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.ProjectInfo;
@@ -35,13 +38,11 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.json.OutputFormat;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.account.GroupControl;
@@ -303,16 +304,17 @@ public class ListProjects implements RestReadView<TopLevelResource> {
}
@Override
- public Object apply(TopLevelResource resource)
+ public Response<Object> apply(TopLevelResource resource)
throws BadRequestException, PermissionBackendException {
if (format == OutputFormat.TEXT) {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
displayToStream(buf);
- return BinaryResult.create(buf.toByteArray())
- .setContentType("text/plain")
- .setCharacterEncoding(UTF_8);
+ return Response.ok(
+ BinaryResult.create(buf.toByteArray())
+ .setContentType("text/plain")
+ .setCharacterEncoding(UTF_8));
}
- return apply();
+ return Response.ok(apply());
}
public SortedMap<String, ProjectInfo> apply()
@@ -505,7 +507,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
continue;
}
- List<WebLinkInfo> links = webLinks.getProjectLinks(projectName.get());
+ ImmutableList<WebLinkInfo> links = webLinks.getProjectLinks(projectName.get());
info.webLinks = links.isEmpty() ? null : links;
if (stdout == null || format.isJson()) {
diff --git a/java/com/google/gerrit/server/restapi/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
index 08b9b84a5e..ff6d30e533 100644
--- a/java/com/google/gerrit/server/restapi/project/ListTags.java
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -14,18 +14,19 @@
package com.google.gerrit.server.restapi.project;
-import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static com.google.gerrit.entities.RefNames.isConfigRef;
import static java.util.Comparator.comparing;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
import com.google.gerrit.extensions.api.projects.TagInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CommonConverters;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -116,7 +117,7 @@ public class ListTags implements RestReadView<ProjectResource> {
}
@Override
- public List<TagInfo> apply(ProjectResource resource)
+ public Response<ImmutableList<TagInfo>> apply(ProjectResource resource)
throws IOException, ResourceNotFoundException, RestApiException, PermissionBackendException {
resource.getProjectState().checkStatePermitsRead();
@@ -137,12 +138,13 @@ public class ListTags implements RestReadView<ProjectResource> {
tags.sort(comparing(t -> t.ref));
- return new RefFilter<TagInfo>(Constants.R_TAGS)
- .start(start)
- .limit(limit)
- .subString(matchSubstring)
- .regex(matchRegex)
- .filter(tags);
+ return Response.ok(
+ new RefFilter<TagInfo>(Constants.R_TAGS)
+ .start(start)
+ .limit(limit)
+ .subString(matchSubstring)
+ .regex(matchRegex)
+ .filter(tags));
}
public TagInfo get(ProjectResource resource, IdString id)
@@ -182,7 +184,7 @@ public class ListTags implements RestReadView<ProjectResource> {
perm.testOrFalse(RefPermission.DELETE) && projectState.statePermitsWrite() ? true : null;
}
- List<WebLinkInfo> webLinks = links.getTagLinks(projectState.getName(), ref.getName());
+ ImmutableList<WebLinkInfo> webLinks = links.getTagLinks(projectState.getName(), ref.getName());
if (object instanceof RevTag) {
// Annotated or signed tag
RevTag tag = (RevTag) object;
diff --git a/java/com/google/gerrit/server/restapi/project/ProjectNode.java b/java/com/google/gerrit/server/restapi/project/ProjectNode.java
index c83e473361..1e6200c728 100644
--- a/java/com/google/gerrit/server/restapi/project/ProjectNode.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectNode.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.restapi.project;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.util.TreeFormatter.TreeNode;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
index 31c90e5afa..7d6f7ef67a 100644
--- a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -30,7 +31,6 @@ import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.json.OutputFormat;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.ProjectUtil;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -138,7 +138,7 @@ public class ProjectsCollection
throws IOException, PermissionBackendException, ResourceConflictException {
id = ProjectUtil.sanitizeProjectName(id);
- Project.NameKey nameKey = new Project.NameKey(id);
+ Project.NameKey nameKey = Project.nameKey(id);
ProjectState state = projectCache.checkedGet(nameKey);
if (state == null) {
return null;
diff --git a/java/com/google/gerrit/server/restapi/project/PutBranch.java b/java/com/google/gerrit/server/restapi/project/PutBranch.java
index fec8abf4e9..02fc6689c1 100644
--- a/java/com/google/gerrit/server/restapi/project/PutBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/PutBranch.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.project.BranchResource;
import com.google.inject.Singleton;
@@ -25,7 +26,8 @@ import com.google.inject.Singleton;
public class PutBranch implements RestModifyView<BranchResource, BranchInput> {
@Override
- public BranchInfo apply(BranchResource rsrc, BranchInput input) throws ResourceConflictException {
+ public Response<BranchInfo> apply(BranchResource rsrc, BranchInput input)
+ throws ResourceConflictException {
throw new ResourceConflictException("Branch \"" + rsrc.getRef() + "\" already exists");
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/PutConfig.java b/java/com/google/gerrit/server/restapi/project/PutConfig.java
index 56ddbb4a99..4aecb4aa46 100644
--- a/java/com/google/gerrit/server/restapi/project/PutConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/PutConfig.java
@@ -17,6 +17,8 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.api.projects.ConfigValue;
@@ -26,11 +28,10 @@ import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.EnableSignedPush;
import com.google.gerrit.server.config.AllProjectsName;
@@ -108,13 +109,13 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
}
@Override
- public ConfigInfo apply(ProjectResource rsrc, ConfigInput input)
+ public Response<ConfigInfo> apply(ProjectResource rsrc, ConfigInput input)
throws RestApiException, PermissionBackendException {
permissionBackend
.currentUser()
.project(rsrc.getNameKey())
.check(ProjectPermission.WRITE_CONFIG);
- return apply(rsrc.getProjectState(), input);
+ return Response.ok(apply(rsrc.getProjectState(), input));
}
public ConfigInfo apply(ProjectState projectState, ConfigInput input)
diff --git a/java/com/google/gerrit/server/restapi/project/PutDescription.java b/java/com/google/gerrit/server/restapi/project/PutDescription.java
index c3a063de68..a0b9feb908 100644
--- a/java/com/google/gerrit/server/restapi/project/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/project/PutDescription.java
@@ -16,13 +16,13 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.DescriptionInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.permissions.PermissionBackend;
diff --git a/java/com/google/gerrit/server/restapi/project/PutTag.java b/java/com/google/gerrit/server/restapi/project/PutTag.java
index 06c5157368..b6f3f24b92 100644
--- a/java/com/google/gerrit/server/restapi/project/PutTag.java
+++ b/java/com/google/gerrit/server/restapi/project/PutTag.java
@@ -17,13 +17,15 @@ package com.google.gerrit.server.restapi.project;
import com.google.gerrit.extensions.api.projects.TagInfo;
import com.google.gerrit.extensions.api.projects.TagInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.project.TagResource;
public class PutTag implements RestModifyView<TagResource, TagInput> {
@Override
- public TagInfo apply(TagResource resource, TagInput input) throws ResourceConflictException {
+ public Response<TagInfo> apply(TagResource resource, TagInput input)
+ throws ResourceConflictException {
throw new ResourceConflictException("Tag \"" + resource.getTagInfo().ref + "\" already exists");
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/QueryProjects.java b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
index 8727df33e3..7066d9a0a5 100644
--- a/java/com/google/gerrit/server/restapi/project/QueryProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
@@ -19,6 +19,7 @@ import com.google.common.collect.Lists;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.index.project.ProjectData;
@@ -86,9 +87,9 @@ public class QueryProjects implements RestReadView<TopLevelResource> {
}
@Override
- public List<ProjectInfo> apply(TopLevelResource resource)
+ public Response<List<ProjectInfo>> apply(TopLevelResource resource)
throws BadRequestException, MethodNotAllowedException {
- return apply();
+ return Response.ok(apply());
}
public List<ProjectInfo> apply() throws BadRequestException, MethodNotAllowedException {
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccess.java b/java/com/google/gerrit/server/restapi/project/SetAccess.java
index 95bc75f7f0..02c1b545fe 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccess.java
@@ -17,23 +17,20 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.InvalidNameException;
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
-import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CreateGroupPermissionSyncer;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
@@ -41,7 +38,6 @@ import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.IOException;
import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -80,9 +76,8 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
}
@Override
- public ProjectAccessInfo apply(ProjectResource rsrc, ProjectAccessInput input)
- throws ResourceNotFoundException, ResourceConflictException, IOException, AuthException,
- BadRequestException, UnprocessableEntityException, PermissionBackendException {
+ public Response<ProjectAccessInfo> apply(ProjectResource rsrc, ProjectAccessInput input)
+ throws Exception {
MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
ProjectConfig config;
@@ -117,7 +112,7 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
identifiedUser.get(),
config,
rsrc.getNameKey(),
- input.parent == null ? null : new Project.NameKey(input.parent),
+ input.parent == null ? null : Project.nameKey(input.parent),
!checkedAdmin);
if (!Strings.isNullOrEmpty(input.message)) {
@@ -138,6 +133,6 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
throw new ResourceConflictException(rsrc.getName(), e);
}
- return getAccess.apply(rsrc.getNameKey());
+ return Response.ok(getAccess.apply(rsrc.getNameKey()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
index e206319191..ea29fb3008 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
@@ -21,6 +21,7 @@ import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.InvalidNameException;
import com.google.gerrit.extensions.api.access.AccessSectionInfo;
import com.google.gerrit.extensions.api.access.PermissionInfo;
@@ -29,7 +30,6 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.group.GroupResolver;
diff --git a/java/com/google/gerrit/server/restapi/project/SetDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDashboard.java
index 2804b7c858..e8e0c0d01a 100644
--- a/java/com/google/gerrit/server/restapi/project/SetDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/SetDashboard.java
@@ -18,14 +18,11 @@ import com.google.gerrit.extensions.api.projects.DashboardInfo;
import com.google.gerrit.extensions.api.projects.SetDashboardInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.DashboardResource;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.IOException;
@Singleton
public class SetDashboard implements RestModifyView<DashboardResource, SetDashboardInput> {
@@ -38,7 +35,7 @@ public class SetDashboard implements RestModifyView<DashboardResource, SetDashbo
@Override
public Response<DashboardInfo> apply(DashboardResource resource, SetDashboardInput input)
- throws RestApiException, IOException, PermissionBackendException {
+ throws Exception {
if (resource.isProjectDefault()) {
return defaultSetter.get().apply(resource, input);
}
diff --git a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
index 3917bee62b..49b5cab537 100644
--- a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.DashboardInfo;
import com.google.gerrit.extensions.api.projects.SetDashboardInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -23,12 +24,9 @@ import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.DashboardResource;
import com.google.gerrit.server.project.ProjectCache;
@@ -36,7 +34,6 @@ import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.kohsuke.args4j.Option;
@@ -70,7 +67,7 @@ class SetDefaultDashboard implements RestModifyView<DashboardResource, SetDashbo
@Override
public Response<DashboardInfo> apply(DashboardResource rsrc, SetDashboardInput input)
- throws RestApiException, IOException, PermissionBackendException {
+ throws Exception {
if (input == null) {
input = new SetDashboardInput(); // Delete would set input to null.
}
@@ -119,9 +116,9 @@ class SetDefaultDashboard implements RestModifyView<DashboardResource, SetDashbo
cache.evict(rsrc.getProjectState().getProject());
if (target != null) {
- DashboardInfo info = get.get().apply(target);
- info.isDefault = true;
- return Response.ok(info);
+ Response<DashboardInfo> response = get.get().apply(target);
+ response.value().isDefault = true;
+ return response;
}
return Response.none();
} catch (RepositoryNotFoundException notFound) {
diff --git a/java/com/google/gerrit/server/restapi/project/SetHead.java b/java/com/google/gerrit/server/restapi/project/SetHead.java
index 430d709888..0afea5c86f 100644
--- a/java/com/google/gerrit/server/restapi/project/SetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/SetHead.java
@@ -15,15 +15,16 @@
package com.google.gerrit.server.restapi.project;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.HeadInput;
import com.google.gerrit.extensions.events.HeadUpdatedListener;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.AbstractNoNotifyEvent;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -63,7 +64,7 @@ public class SetHead implements RestModifyView<ProjectResource, HeadInput> {
}
@Override
- public String apply(ProjectResource rsrc, HeadInput input)
+ public Response<String> apply(ProjectResource rsrc, HeadInput input)
throws AuthException, ResourceNotFoundException, BadRequestException,
UnprocessableEntityException, IOException, PermissionBackendException {
if (input == null || Strings.isNullOrEmpty(input.ref)) {
@@ -109,7 +110,7 @@ public class SetHead implements RestModifyView<ProjectResource, HeadInput> {
fire(rsrc.getNameKey(), oldHead, newHead);
}
- return ref;
+ return Response.ok(ref);
} catch (RepositoryNotFoundException e) {
throw new ResourceNotFoundException(rsrc.getName(), e);
}
diff --git a/java/com/google/gerrit/server/restapi/project/SetParent.java b/java/com/google/gerrit/server/restapi/project/SetParent.java
index 8e0363fbb0..a610dd42b3 100644
--- a/java/com/google/gerrit/server/restapi/project/SetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/SetParent.java
@@ -20,14 +20,15 @@ import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.ParentInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
@@ -85,11 +86,11 @@ public class SetParent
}
@Override
- public String apply(ProjectResource rsrc, ParentInput input)
+ public Response<String> apply(ProjectResource rsrc, ParentInput input)
throws AuthException, ResourceConflictException, ResourceNotFoundException,
UnprocessableEntityException, IOException, PermissionBackendException,
BadRequestException {
- return apply(rsrc, input, true);
+ return Response.ok(apply(rsrc, input, true));
}
public String apply(ProjectResource rsrc, ParentInput input, boolean checkIfAdmin)
@@ -155,7 +156,7 @@ public class SetParent
newParent = Strings.emptyToNull(newParent);
if (newParent != null) {
- ProjectState parent = cache.get(new Project.NameKey(newParent));
+ ProjectState parent = cache.get(Project.nameKey(newParent));
if (parent == null) {
throw new UnprocessableEntityException("parent project " + newParent + " not found");
}
diff --git a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
index 2be6c19b47..32aec59b76 100644
--- a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
+++ b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
@@ -20,20 +20,19 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
+import java.util.Optional;
/**
* Java implementation of Gerrit's default pre-submit rules behavior: check if the labels have the
@@ -63,13 +62,13 @@ public final class DefaultSubmitRule implements SubmitRule {
}
@Override
- public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions options) {
+ public Optional<SubmitRecord> evaluate(ChangeData cd) {
ProjectState projectState = projectCache.get(cd.project());
// In case at least one project has a rules.pl file, we let Prolog handle it.
// The Prolog rules engine will also handle the labels for us.
if (projectState == null || projectState.hasPrologRules()) {
- return Collections.emptyList();
+ return Optional.empty();
}
SubmitRecord submitRecord = new SubmitRecord();
@@ -86,7 +85,7 @@ public final class DefaultSubmitRule implements SubmitRule {
submitRecord.errorMessage = "Unable to fetch labels and approvals for the change";
submitRecord.status = SubmitRecord.Status.RULE_ERROR;
- return Collections.singletonList(submitRecord);
+ return Optional.of(submitRecord);
}
submitRecord.labels = new ArrayList<>(labelTypes.size());
@@ -99,7 +98,7 @@ public final class DefaultSubmitRule implements SubmitRule {
submitRecord.errorMessage = "Unable to find the LabelFunction for label " + t.getName();
submitRecord.status = SubmitRecord.Status.RULE_ERROR;
- return Collections.singletonList(submitRecord);
+ return Optional.of(submitRecord);
}
Collection<PatchSetApproval> approvalsForLabel = getApprovalsForLabel(approvals, t);
@@ -119,13 +118,13 @@ public final class DefaultSubmitRule implements SubmitRule {
}
}
- return Collections.singletonList(submitRecord);
+ return Optional.of(submitRecord);
}
private static List<PatchSetApproval> getApprovalsForLabel(
List<PatchSetApproval> approvals, LabelType t) {
return approvals.stream()
- .filter(input -> input.getLabel().equals(t.getLabelId().get()))
+ .filter(input -> input.label().equals(t.getLabelId().get()))
.collect(toImmutableList());
}
}
diff --git a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
index 759be39f44..54915fb519 100644
--- a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
+++ b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
@@ -17,23 +17,22 @@ package com.google.gerrit.server.rules;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Optional;
/**
* Rule to require an approval from a user that did not upload the current patch set or block
@@ -56,7 +55,7 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
}
@Override
- public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions options) {
+ public Optional<SubmitRecord> evaluate(ChangeData cd) {
List<LabelType> labelTypes;
List<PatchSetApproval> approvals;
try {
@@ -64,21 +63,21 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
approvals = cd.currentApprovals();
} catch (StorageException e) {
logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_LABELS);
- return singletonRuleError(E_UNABLE_TO_FETCH_LABELS);
+ return ruleError(E_UNABLE_TO_FETCH_LABELS);
}
boolean shouldIgnoreSelfApproval = labelTypes.stream().anyMatch(LabelType::ignoreSelfApproval);
if (!shouldIgnoreSelfApproval) {
// Shortcut to avoid further processing if no label should ignore uploader approvals
- return ImmutableList.of();
+ return Optional.empty();
}
Account.Id uploader;
try {
- uploader = cd.currentPatchSet().getUploader();
+ uploader = cd.currentPatchSet().uploader();
} catch (StorageException e) {
logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_UPLOADER);
- return singletonRuleError(E_UNABLE_TO_FETCH_UPLOADER);
+ return ruleError(E_UNABLE_TO_FETCH_UPLOADER);
}
SubmitRecord submitRecord = new SubmitRecord();
@@ -120,10 +119,10 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
}
if (submitRecord.labels.isEmpty()) {
- return ImmutableList.of();
+ return Optional.empty();
}
- return ImmutableList.of(submitRecord);
+ return Optional.of(submitRecord);
}
private static boolean labelCheckPassed(SubmitRecord.Label label) {
@@ -140,18 +139,18 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
return false;
}
- private static Collection<SubmitRecord> singletonRuleError(String reason) {
+ private static Optional<SubmitRecord> ruleError(String reason) {
SubmitRecord submitRecord = new SubmitRecord();
submitRecord.errorMessage = reason;
submitRecord.status = SubmitRecord.Status.RULE_ERROR;
- return ImmutableList.of(submitRecord);
+ return Optional.of(submitRecord);
}
@VisibleForTesting
static Collection<PatchSetApproval> filterOutPositiveApprovalsOfUser(
Collection<PatchSetApproval> approvals, Account.Id user) {
return approvals.stream()
- .filter(input -> input.getValue() < 0 || !input.getAccountId().equals(user))
+ .filter(input -> input.value() < 0 || !input.accountId().equals(user))
.collect(toImmutableList());
}
@@ -159,7 +158,7 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
static Collection<PatchSetApproval> filterApprovalsByLabel(
Collection<PatchSetApproval> approvals, LabelType t) {
return approvals.stream()
- .filter(input -> input.getLabelId().get().equals(t.getLabelId().get()))
+ .filter(input -> input.labelId().get().equals(t.getLabelId().get()))
.collect(toImmutableList());
}
}
diff --git a/java/com/google/gerrit/server/rules/PrologOptions.java b/java/com/google/gerrit/server/rules/PrologOptions.java
new file mode 100644
index 0000000000..a176f0429e
--- /dev/null
+++ b/java/com/google/gerrit/server/rules/PrologOptions.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.rules;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import java.util.Optional;
+
+@AutoValue
+public abstract class PrologOptions {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ public static PrologOptions defaultOptions() {
+ return new AutoValue_PrologOptions.Builder().logErrors(true).skipFilters(false).build();
+ }
+
+ public static PrologOptions dryRunOptions(String ruleToTest, boolean skipFilters) {
+ return new AutoValue_PrologOptions.Builder()
+ .logErrors(logger.atFine().isEnabled())
+ .skipFilters(skipFilters)
+ .rule(ruleToTest)
+ .build();
+ }
+
+ /** Whether errors should be logged. */
+ abstract boolean logErrors();
+
+ /** Whether Prolog filters from parent projects should be skipped. */
+ abstract boolean skipFilters();
+
+ /**
+ * Prolog rule that should be run. If not given, the Prolog rule that is configured for the
+ * project is used (the rule from rules.pl in refs/meta/config).
+ */
+ abstract Optional<String> rule();
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract PrologOptions.Builder logErrors(boolean logErrors);
+
+ abstract PrologOptions.Builder skipFilters(boolean skipFilters);
+
+ abstract PrologOptions.Builder rule(@Nullable String rule);
+
+ abstract PrologOptions build();
+ }
+}
diff --git a/java/com/google/gerrit/server/rules/PrologRule.java b/java/com/google/gerrit/server/rules/PrologRule.java
index 0c54f40b28..bf1d545834 100644
--- a/java/com/google/gerrit/server/rules/PrologRule.java
+++ b/java/com/google/gerrit/server/rules/PrologRule.java
@@ -18,12 +18,10 @@ import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import java.util.Collection;
-import java.util.Collections;
+import java.util.Optional;
@Singleton
public class PrologRule implements SubmitRule {
@@ -37,21 +35,29 @@ public class PrologRule implements SubmitRule {
}
@Override
- public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions opts) {
+ public Optional<SubmitRecord> evaluate(ChangeData cd) {
ProjectState projectState = projectCache.get(cd.project());
// We only want to run the Prolog engine if we have at least one rules.pl file to use.
- if ((projectState == null || !projectState.hasPrologRules()) && opts.rule() == null) {
- return Collections.emptyList();
+ if ((projectState == null || !projectState.hasPrologRules())) {
+ return Optional.empty();
}
+ return Optional.of(evaluate(cd, PrologOptions.defaultOptions()));
+ }
+
+ public SubmitRecord evaluate(ChangeData cd, PrologOptions opts) {
return getEvaluator(cd, opts).evaluate();
}
- private PrologRuleEvaluator getEvaluator(ChangeData cd, SubmitRuleOptions opts) {
- return factory.create(cd, opts);
+ public SubmitTypeRecord getSubmitType(ChangeData cd) {
+ return getSubmitType(cd, PrologOptions.defaultOptions());
}
- public SubmitTypeRecord getSubmitType(ChangeData cd, SubmitRuleOptions opts) {
+ public SubmitTypeRecord getSubmitType(ChangeData cd, PrologOptions opts) {
return getEvaluator(cd, opts).getSubmitType();
}
+
+ private PrologRuleEvaluator getEvaluator(ChangeData cd, PrologOptions opts) {
+ return factory.create(cd, opts);
+ }
}
diff --git a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
index 9cde54c9a9..72dc46a310 100644
--- a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
+++ b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.rules;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.project.SubmitRuleEvaluator.createRuleError;
import static com.google.gerrit.server.project.SubmitRuleEvaluator.defaultRuleError;
import static com.google.gerrit.server.project.SubmitRuleEvaluator.defaultTypeError;
@@ -24,10 +25,10 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
import com.google.gerrit.server.account.Emails;
@@ -35,7 +36,6 @@ import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RuleEvalException;
-import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -51,7 +51,6 @@ import com.googlecode.prolog_cafe.lang.Term;
import com.googlecode.prolog_cafe.lang.VariableTerm;
import java.io.StringReader;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -73,7 +72,7 @@ public class PrologRuleEvaluator {
public interface Factory {
/** Returns a new {@link PrologRuleEvaluator} with the specified options */
- PrologRuleEvaluator create(ChangeData cd, SubmitRuleOptions options);
+ PrologRuleEvaluator create(ChangeData cd, PrologOptions options);
}
/**
@@ -95,7 +94,7 @@ public class PrologRuleEvaluator {
private final PrologEnvironment.Factory envFactory;
private final ChangeData cd;
private final ProjectState projectState;
- private final SubmitRuleOptions opts;
+ private final PrologOptions opts;
private Term submitRule;
@AssistedInject
@@ -107,7 +106,7 @@ public class PrologRuleEvaluator {
PrologEnvironment.Factory envFactory,
ProjectCache projectCache,
@Assisted ChangeData cd,
- @Assisted SubmitRuleOptions options) {
+ @Assisted PrologOptions options) {
this.accountCache = accountCache;
this.accounts = accounts;
this.emails = emails;
@@ -141,10 +140,9 @@ public class PrologRuleEvaluator {
/**
* Evaluate the submit rules.
*
- * @return List of {@link SubmitRecord} objects returned from the evaluated rules, including any
- * errors.
+ * @return {@link SubmitRecord} returned from the evaluated rules. Can include errors.
*/
- public Collection<SubmitRecord> evaluate() {
+ public SubmitRecord evaluate() {
Change change;
try {
change = cd.change();
@@ -159,12 +157,6 @@ public class PrologRuleEvaluator {
return ruleError("Error looking up change " + cd.getId(), e);
}
- if (!opts.allowClosed() && change.isClosed()) {
- SubmitRecord rec = new SubmitRecord();
- rec.status = SubmitRecord.Status.CLOSED;
- return Collections.singletonList(rec);
- }
-
List<Term> results;
try {
results =
@@ -200,28 +192,32 @@ public class PrologRuleEvaluator {
* output. Later after the loop the out collection is reversed to restore it to the original
* ordering.
*/
- public List<SubmitRecord> resultsToSubmitRecord(Term submitRule, List<Term> results) {
- boolean foundOk = false;
- List<SubmitRecord> out = new ArrayList<>(results.size());
+ public SubmitRecord resultsToSubmitRecord(Term submitRule, List<Term> results) {
+ checkState(!results.isEmpty(), "the list of Prolog terms must not be empty");
+
+ SubmitRecord resultSubmitRecord = new SubmitRecord();
+ resultSubmitRecord.labels = new ArrayList<>();
for (int resultIdx = results.size() - 1; 0 <= resultIdx; resultIdx--) {
Term submitRecord = results.get(resultIdx);
- SubmitRecord rec = new SubmitRecord();
- out.add(rec);
if (!(submitRecord instanceof StructureTerm) || 1 != submitRecord.arity()) {
return invalidResult(submitRule, submitRecord);
}
- if ("ok".equals(submitRecord.name())) {
- rec.status = SubmitRecord.Status.OK;
-
- } else if ("not_ready".equals(submitRecord.name())) {
- rec.status = SubmitRecord.Status.NOT_READY;
-
- } else {
+ if (!"ok".equals(submitRecord.name()) && !"not_ready".equals(submitRecord.name())) {
return invalidResult(submitRule, submitRecord);
}
+ // This transformation is required to adapt Prolog's behavior to the way Gerrit handles
+ // SubmitRecords, as defined in the SubmitRecord#allRecordsOK method.
+ // When several rules are defined in Prolog, they are all matched to a SubmitRecord. We want
+ // the change to be submittable when at least one result is OK.
+ if ("ok".equals(submitRecord.name())) {
+ resultSubmitRecord.status = SubmitRecord.Status.OK;
+ } else if ("not_ready".equals(submitRecord.name()) && resultSubmitRecord.status == null) {
+ resultSubmitRecord.status = SubmitRecord.Status.NOT_READY;
+ }
+
// Unpack the one argument. This should also be a structure with one
// argument per label that needs to be reported on to the caller.
//
@@ -231,8 +227,6 @@ public class PrologRuleEvaluator {
return invalidResult(submitRule, submitRecord);
}
- rec.labels = new ArrayList<>(submitRecord.arity());
-
for (Term state : ((StructureTerm) submitRecord).args()) {
if (!(state instanceof StructureTerm)
|| 2 != state.arity()
@@ -241,7 +235,7 @@ public class PrologRuleEvaluator {
}
SubmitRecord.Label lbl = new SubmitRecord.Label();
- rec.labels.add(lbl);
+ resultSubmitRecord.labels.add(lbl);
lbl.label = checkLabelName(state.arg(0).name());
Term status = state.arg(1);
@@ -272,24 +266,12 @@ public class PrologRuleEvaluator {
}
}
- if (rec.status == SubmitRecord.Status.OK) {
- foundOk = true;
+ if (resultSubmitRecord.status == SubmitRecord.Status.OK) {
break;
}
}
- Collections.reverse(out);
-
- // This transformation is required to adapt Prolog's behavior to the way Gerrit handles
- // SubmitRecords, as defined in the SubmitRecord#allRecordsOK method.
- // When several rules are defined in Prolog, they are all matched to a SubmitRecord. We want
- // the change to be submittable when at least one result is OK.
- if (foundOk) {
- for (SubmitRecord record : out) {
- record.status = SubmitRecord.Status.OK;
- }
- }
-
- return out;
+ Collections.reverse(resultSubmitRecord.labels);
+ return resultSubmitRecord;
}
@VisibleForTesting
@@ -306,7 +288,7 @@ public class PrologRuleEvaluator {
return VALID_LABEL_MATCHER.retainFrom(name);
}
- private List<SubmitRecord> invalidResult(Term rule, Term record, String reason) {
+ private SubmitRecord invalidResult(Term rule, Term record, String reason) {
return ruleError(
String.format(
"Submit rule %s for change %s of %s output invalid result: %s%s",
@@ -317,15 +299,15 @@ public class PrologRuleEvaluator {
(reason == null ? "" : ". Reason: " + reason)));
}
- private List<SubmitRecord> invalidResult(Term rule, Term record) {
+ private SubmitRecord invalidResult(Term rule, Term record) {
return invalidResult(rule, record, null);
}
- private List<SubmitRecord> ruleError(String err) {
+ private SubmitRecord ruleError(String err) {
return ruleError(err, null);
}
- private List<SubmitRecord> ruleError(String err, Exception e) {
+ private SubmitRecord ruleError(String err, Exception e) {
if (opts.logErrors()) {
logger.atSevere().withCause(e).log(err);
return defaultRuleError();
@@ -465,22 +447,22 @@ public class PrologRuleEvaluator {
PrologEnvironment env;
try {
PrologMachineCopy pmc;
- if (opts.rule() == null) {
+ if (opts.rule().isPresent()) {
+ pmc = rulesCache.loadMachine("stdin", new StringReader(opts.rule().get()));
+ } else {
pmc =
rulesCache.loadMachine(
projectState.getNameKey(), projectState.getConfig().getRulesId());
- } else {
- pmc = rulesCache.loadMachine("stdin", new StringReader(opts.rule()));
}
env = envFactory.create(pmc);
} catch (CompileException err) {
String msg;
- if (opts.rule() == null) {
+ if (opts.rule().isPresent()) {
+ msg = err.getMessage();
+ } else {
msg =
String.format(
"Cannot load rules.pl for %s: %s", projectState.getName(), err.getMessage());
- } else {
- msg = err.getMessage();
}
throw new RuleEvalException(msg, err);
}
@@ -540,7 +522,7 @@ public class PrologRuleEvaluator {
if (status instanceof StructureTerm && status.arity() == 1) {
Term who = status.arg(0);
if (isUser(who)) {
- label.appliedBy = new Account.Id(((IntegerTerm) who.arg(0)).intValue());
+ label.appliedBy = Account.id(((IntegerTerm) who.arg(0)).intValue());
} else {
throw new UserTermExpected(label);
}
diff --git a/java/com/google/gerrit/server/rules/RulesCache.java b/java/com/google/gerrit/server/rules/RulesCache.java
index d4e90f9dc7..0cd21a40f9 100644
--- a/java/com/google/gerrit/server/rules/RulesCache.java
+++ b/java/com/google/gerrit/server/rules/RulesCache.java
@@ -19,8 +19,8 @@ import static com.googlecode.prolog_cafe.lang.PrologMachineCopy.save;
import com.google.common.base.Joiner;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
diff --git a/java/com/google/gerrit/server/rules/StoredValues.java b/java/com/google/gerrit/server/rules/StoredValues.java
index 30d329dca1..1e08a240e3 100644
--- a/java/com/google/gerrit/server/rules/StoredValues.java
+++ b/java/com/google/gerrit/server/rules/StoredValues.java
@@ -16,12 +16,12 @@ package com.google.gerrit.server.rules;
import static com.google.gerrit.server.rules.StoredValue.create;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
@@ -42,7 +42,6 @@ import com.googlecode.prolog_cafe.lang.Prolog;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -97,9 +96,8 @@ public final class StoredValues {
PatchListCache plCache = env.getArgs().getPatchListCache();
Change change = getChange(engine);
Project.NameKey project = change.getProject();
- ObjectId b = ObjectId.fromString(ps.getRevision().get());
Whitespace ws = Whitespace.IGNORE_NONE;
- PatchListKey plKey = PatchListKey.againstDefaultBase(b, ws);
+ PatchListKey plKey = PatchListKey.againstDefaultBase(ps.commitId(), ws);
PatchList patchList;
try {
patchList = plCache.get(plKey, project);
diff --git a/java/com/google/gerrit/server/rules/SubmitRule.java b/java/com/google/gerrit/server/rules/SubmitRule.java
index 2a68683db5..b221117f86 100644
--- a/java/com/google/gerrit/server/rules/SubmitRule.java
+++ b/java/com/google/gerrit/server/rules/SubmitRule.java
@@ -15,15 +15,14 @@ package com.google.gerrit.server.rules;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
-import java.util.Collection;
+import java.util.Optional;
/**
* Allows plugins to decide whether a change is ready to be submitted or not.
*
- * <p>For a given {@link ChangeData}, each plugin is called and returns a {@link Collection} of
- * {@link SubmitRecord}. This collection can be empty, or contain one or several values.
+ * <p>For a given {@link ChangeData}, each plugin is called and returns a {@link Optional} of {@link
+ * SubmitRecord}.
*
* <p>A Change can only be submitted if all the plugins give their consent.
*
@@ -39,6 +38,9 @@ import java.util.Collection;
*/
@ExtensionPoint
public interface SubmitRule {
- /** Returns a {@link Collection} of {@link SubmitRecord} status for the change. */
- Collection<SubmitRecord> evaluate(ChangeData changeData, SubmitRuleOptions options);
+ /**
+ * Returns a {@link Optional} of {@link SubmitRecord} status for the change. {@code
+ * Optional#empty()} if the SubmitRule was a no-op.
+ */
+ Optional<SubmitRecord> evaluate(ChangeData changeData);
}
diff --git a/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 9446b7c908..c15efbaa00 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.schema;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_SEQUENCES;
+import static com.google.gerrit.entities.RefNames.REFS_SEQUENCES;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
@@ -30,8 +30,8 @@ import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
diff --git a/java/com/google/gerrit/server/schema/AllProjectsInput.java b/java/com/google/gerrit/server/schema/AllProjectsInput.java
index 7231b1858a..6e11a5d66a 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsInput.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsInput.java
@@ -21,8 +21,8 @@ import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
import com.google.gerrit.server.notedb.Sequences;
import java.util.Optional;
diff --git a/java/com/google/gerrit/server/schema/AllUsersCreator.java b/java/com/google/gerrit/server/schema/AllUsersCreator.java
index d2f5ef183c..4904028409 100644
--- a/java/com/google/gerrit/server/schema/AllUsersCreator.java
+++ b/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -26,8 +26,8 @@ import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
diff --git a/java/com/google/gerrit/server/schema/BUILD b/java/com/google/gerrit/server/schema/BUILD
index ee99c67c71..cee086280b 100644
--- a/java/com/google/gerrit/server/schema/BUILD
+++ b/java/com/google/gerrit/server/schema/BUILD
@@ -9,23 +9,23 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/git",
"//java/com/google/gerrit/gpg",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
- "//java/com/google/gerrit/server/util/time",
+ "//java/com/google/gerrit/server/logging",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-archive",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/commons:dbcp",
"//lib/flogger:api",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit.archive:jgit-archive",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/log:jsonevent-layout",
"//lib/log:log4j",
],
diff --git a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
index 0612bb983b..49dcc46aad 100644
--- a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
@@ -21,19 +21,22 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.change.AccountPatchReviewStore;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.ThreadSettingsConfig;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -212,14 +215,22 @@ public abstract class JdbcAccountPatchReviewStore
@Override
public boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
- try (Connection con = ds.getConnection();
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Mark file as reviewed",
+ Metadata.builder()
+ .patchSetId(psId.get())
+ .accountId(accountId.get())
+ .filePath(path)
+ .build());
+ Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
"INSERT INTO account_patch_reviews "
+ "(account_id, change_id, patch_set_id, file_name) VALUES "
+ "(?, ?, ?, ?)")) {
stmt.setInt(1, accountId.get());
- stmt.setInt(2, psId.getParentKey().get());
+ stmt.setInt(2, psId.changeId().get());
stmt.setInt(3, psId.get());
stmt.setString(4, path);
stmt.executeUpdate();
@@ -239,7 +250,15 @@ public abstract class JdbcAccountPatchReviewStore
return;
}
- try (Connection con = ds.getConnection();
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Mark files as reviewed",
+ Metadata.builder()
+ .patchSetId(psId.get())
+ .accountId(accountId.get())
+ .resourceCount(paths.size())
+ .build());
+ Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
"INSERT INTO account_patch_reviews "
@@ -247,7 +266,7 @@ public abstract class JdbcAccountPatchReviewStore
+ "(?, ?, ?, ?)")) {
for (String path : paths) {
stmt.setInt(1, accountId.get());
- stmt.setInt(2, psId.getParentKey().get());
+ stmt.setInt(2, psId.changeId().get());
stmt.setInt(3, psId.get());
stmt.setString(4, path);
stmt.addBatch();
@@ -264,14 +283,22 @@ public abstract class JdbcAccountPatchReviewStore
@Override
public void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
- try (Connection con = ds.getConnection();
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Clear reviewed flag",
+ Metadata.builder()
+ .patchSetId(psId.get())
+ .accountId(accountId.get())
+ .filePath(path)
+ .build());
+ Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
"DELETE FROM account_patch_reviews "
+ "WHERE account_id = ? AND change_id = ? AND "
+ "patch_set_id = ? AND file_name = ?")) {
stmt.setInt(1, accountId.get());
- stmt.setInt(2, psId.getParentKey().get());
+ stmt.setInt(2, psId.changeId().get());
stmt.setInt(3, psId.get());
stmt.setString(4, path);
stmt.executeUpdate();
@@ -282,12 +309,16 @@ public abstract class JdbcAccountPatchReviewStore
@Override
public void clearReviewed(PatchSet.Id psId) {
- try (Connection con = ds.getConnection();
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Clear all reviewed flags of patch set",
+ Metadata.builder().patchSetId(psId.get()).build());
+ Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
"DELETE FROM account_patch_reviews "
+ "WHERE change_id = ? AND patch_set_id = ?")) {
- stmt.setInt(1, psId.getParentKey().get());
+ stmt.setInt(1, psId.changeId().get());
stmt.setInt(2, psId.get());
stmt.executeUpdate();
} catch (SQLException e) {
@@ -297,7 +328,11 @@ public abstract class JdbcAccountPatchReviewStore
@Override
public void clearReviewed(Change.Id changeId) {
- try (Connection con = ds.getConnection();
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Clear all reviewed flags of change",
+ Metadata.builder().changeId(changeId.get()).build());
+ Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement("DELETE FROM account_patch_reviews WHERE change_id = ?")) {
stmt.setInt(1, changeId.get());
@@ -309,7 +344,11 @@ public abstract class JdbcAccountPatchReviewStore
@Override
public Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId) {
- try (Connection con = ds.getConnection();
+ try (TraceTimer ignored =
+ TraceContext.newTimer(
+ "Find reviewed flags",
+ Metadata.builder().patchSetId(psId.get()).accountId(accountId.get()).build());
+ Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
"SELECT patch_set_id, file_name FROM account_patch_reviews APR1 "
@@ -319,11 +358,11 @@ public abstract class JdbcAccountPatchReviewStore
+ "AND APR1.change_id = APR2.change_id "
+ "AND patch_set_id <= ?)")) {
stmt.setInt(1, accountId.get());
- stmt.setInt(2, psId.getParentKey().get());
+ stmt.setInt(2, psId.changeId().get());
stmt.setInt(3, psId.get());
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
- PatchSet.Id id = new PatchSet.Id(psId.getParentKey(), rs.getInt("patch_set_id"));
+ PatchSet.Id id = PatchSet.id(psId.changeId(), rs.getInt("patch_set_id"));
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
do {
builder.add(rs.getString("file_name"));
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
index df95ff7c12..0e22af925f 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
@@ -20,8 +20,8 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java
index 33534fc0a6..0360ec0011 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java
@@ -14,15 +14,20 @@
package com.google.gerrit.server.schema;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.ProvisionException;
+import org.eclipse.jgit.lib.Config;
public class NoteDbSchemaVersionCheck implements LifecycleListener {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
public static Module module() {
return new LifecycleModule() {
@Override
@@ -34,11 +39,16 @@ public class NoteDbSchemaVersionCheck implements LifecycleListener {
private final NoteDbSchemaVersionManager versionManager;
private final SitePaths sitePaths;
+ private Config gerritConfig;
@Inject
- NoteDbSchemaVersionCheck(NoteDbSchemaVersionManager versionManager, SitePaths sitePaths) {
+ NoteDbSchemaVersionCheck(
+ NoteDbSchemaVersionManager versionManager,
+ SitePaths sitePaths,
+ @GerritServerConfig Config gerritConfig) {
this.versionManager = versionManager;
this.sitePaths = sitePaths;
+ this.gerritConfig = gerritConfig;
}
@Override
@@ -53,7 +63,18 @@ public class NoteDbSchemaVersionCheck implements LifecycleListener {
sitePaths.site_path.toAbsolutePath()));
}
int expected = NoteDbSchemaVersions.LATEST;
- if (current != expected) {
+
+ if (current > expected
+ && gerritConfig.getBoolean("gerrit", "experimentalRollingUpgrade", false)) {
+ logger.atWarning().log(
+ "Gerrit has detected refs/meta/version %d different than the expected %d."
+ + "Bear in mind that this is supported ONLY for rolling upgrades to immediate next "
+ + "Gerrit version (e.g. v3.1 to v3.2). If this is not expected, remove gerrit.experimentalRollingUpgrade "
+ + "from $GERRIT_SITE/etc/gerrit.config and restart Gerrit."
+ + "Please note that gerrit.experimentalRollingUpgrade is intended to be used "
+ + "for the rolling upgrade phase only and should be disabled afterwards.",
+ current, expected);
+ } else if (current != expected) {
String advice =
current > expected
? "Downgrade is not supported"
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
index 7ff0ea6c44..803872c441 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.schema;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_VERSION;
+import static com.google.gerrit.entities.RefNames.REFS_VERSION;
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.exceptions.StorageException;
diff --git a/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java b/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
index 9e12807cae..5e7dbf03ee 100644
--- a/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
+++ b/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
@@ -20,8 +20,8 @@ import static java.util.stream.Collectors.toList;
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
diff --git a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
index 0a5823a743..26f1990e89 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
@@ -17,10 +17,10 @@ package com.google.gerrit.server.schema;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.git.RefUpdateUtil;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.server.config.AllProjectsName;
@@ -218,8 +218,8 @@ public class SchemaCreatorImpl implements SchemaCreator {
private InternalGroupCreation getGroupCreation(Sequences seqs, GroupReference groupReference) {
int next = seqs.nextGroupId();
return InternalGroupCreation.builder()
- .setNameKey(new AccountGroup.NameKey(groupReference.getName()))
- .setId(new AccountGroup.Id(next))
+ .setNameKey(AccountGroup.nameKey(groupReference.getName()))
+ .setId(AccountGroup.id(next))
.setGroupUUID(groupReference.getUUID())
.build();
}
diff --git a/java/com/google/gerrit/server/schema/VersionedAccountPreferences.java b/java/com/google/gerrit/server/schema/VersionedAccountPreferences.java
index 297fdcddb7..468c26b162 100644
--- a/java/com/google/gerrit/server/schema/VersionedAccountPreferences.java
+++ b/java/com/google/gerrit/server/schema/VersionedAccountPreferences.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.schema;
import com.google.common.base.Strings;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
diff --git a/java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java b/java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java
index 63837b2627..a6424b9683 100644
--- a/java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java
+++ b/java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java
@@ -15,11 +15,12 @@
package com.google.gerrit.server.schema.testing;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
import java.io.IOException;
@@ -151,8 +152,8 @@ public class AllProjectsCreatorTestUtil {
Set<String> subsections1 = config1.getSubsections(section);
Set<String> subsections2 = config2.getSubsections(section);
- assertThat(subsections1)
- .named("section \"%s\"", section)
+ assertWithMessage("section \"%s\"", section)
+ .that(subsections1)
.containsExactlyElementsIn(subsections2);
subsections1.forEach(s -> assertSubsectionEquivalent(config1, config2, section, s));
@@ -163,12 +164,12 @@ public class AllProjectsCreatorTestUtil {
Set<String> subsectionNames1 = config1.getNames(section, subsection);
Set<String> subsectionNames2 = config2.getNames(section, subsection);
String name = String.format("subsection \"%s\" of section \"%s\"", subsection, section);
- assertThat(subsectionNames1).named(name).containsExactlyElementsIn(subsectionNames2);
+ assertWithMessage(name).that(subsectionNames1).containsExactlyElementsIn(subsectionNames2);
subsectionNames1.forEach(
n ->
- assertThat(config1.getStringList(section, subsection, n))
- .named(name)
+ assertWithMessage(name)
+ .that(config1.getStringList(section, subsection, n))
.asList()
.containsExactlyElementsIn(config2.getStringList(section, subsection, n)));
}
diff --git a/java/com/google/gerrit/server/schema/testing/BUILD b/java/com/google/gerrit/server/schema/testing/BUILD
index d641c47917..77bb777f0f 100644
--- a/java/com/google/gerrit/server/schema/testing/BUILD
+++ b/java/com/google/gerrit/server/schema/testing/BUILD
@@ -7,10 +7,10 @@ java_library(
testonly = True,
srcs = glob(["*.java"]),
deps = [
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
"//lib/truth",
],
)
diff --git a/java/com/google/gerrit/server/securestore/testing/BUILD b/java/com/google/gerrit/server/securestore/testing/BUILD
index c2582b9a26..9afc44acb2 100644
--- a/java/com/google/gerrit/server/securestore/testing/BUILD
+++ b/java/com/google/gerrit/server/securestore/testing/BUILD
@@ -8,6 +8,6 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/server",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
],
)
diff --git a/java/com/google/gerrit/server/ssh/NoSshKeyCache.java b/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
index 74bb50c912..3ba446c052 100644
--- a/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
+++ b/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.ssh;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.InvalidSshKeyException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountSshKey;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
diff --git a/java/com/google/gerrit/server/ssh/SshKeyCreator.java b/java/com/google/gerrit/server/ssh/SshKeyCreator.java
index beaf1bace7..2241d863dd 100644
--- a/java/com/google/gerrit/server/ssh/SshKeyCreator.java
+++ b/java/com/google/gerrit/server/ssh/SshKeyCreator.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.ssh;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.InvalidSshKeyException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountSshKey;
public interface SshKeyCreator {
diff --git a/java/com/google/gerrit/server/submit/ChangeSet.java b/java/com/google/gerrit/server/submit/ChangeSet.java
index e721be41ab..65e0b48227 100644
--- a/java/com/google/gerrit/server/submit/ChangeSet.java
+++ b/java/com/google/gerrit/server/submit/ChangeSet.java
@@ -20,9 +20,9 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.query.change.ChangeData;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -76,8 +76,8 @@ public class ChangeSet {
return changeData;
}
- public ListMultimap<Branch.NameKey, ChangeData> changesByBranch() {
- ListMultimap<Branch.NameKey, ChangeData> ret =
+ public ListMultimap<BranchNameKey, ChangeData> changesByBranch() {
+ ListMultimap<BranchNameKey, ChangeData> ret =
MultimapBuilder.hashKeys().arrayListValues().build();
for (ChangeData cd : changeData.values()) {
ret.put(cd.change().getDest(), cd);
diff --git a/java/com/google/gerrit/server/submit/CherryPick.java b/java/com/google/gerrit/server/submit/CherryPick.java
index 8ff3cc5dbc..8b7b2cd1fb 100644
--- a/java/com/google/gerrit/server/submit/CherryPick.java
+++ b/java/com/google/gerrit/server/submit/CherryPick.java
@@ -19,11 +19,11 @@ import static com.google.gerrit.server.submit.CommitMergeStatus.SKIPPED_IDENTICA
import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetInfo;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.MergeTip;
@@ -162,7 +162,7 @@ public class CherryPick extends SubmitStrategy {
ctx.getUpdate(psId),
psId,
newCommit,
- prevPs != null ? prevPs.getGroups() : ImmutableList.of(),
+ prevPs != null ? prevPs.groups() : ImmutableList.of(),
null,
null);
ctx.getChange().setCurrentPatchSet(patchSetInfo);
diff --git a/java/com/google/gerrit/server/submit/CommitMergeStatus.java b/java/com/google/gerrit/server/submit/CommitMergeStatus.java
index 12172dd7b6..bf8b8408d0 100644
--- a/java/com/google/gerrit/server/submit/CommitMergeStatus.java
+++ b/java/com/google/gerrit/server/submit/CommitMergeStatus.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.submit;
import static java.util.stream.Collectors.toSet;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -49,7 +49,8 @@ public enum CommitMergeStatus {
+ "Please rebase the change locally and upload the rebased commit for review."),
SKIPPED_IDENTICAL_TREE(
- "Marking change merged without cherry-picking to branch, as the resulting commit would be empty."),
+ "Marking change merged without cherry-picking to branch, as the resulting commit would be"
+ + " empty."),
MISSING_DEPENDENCY("Depends on change that was not submitted."),
@@ -102,24 +103,22 @@ public enum CommitMergeStatus {
commit, otherCommit, caller != null ? caller.getLoggableName() : "<user-not-available>");
} else if (changes.size() == 1) {
ChangeData cd = changes.get(0);
- if (cd.currentPatchSet().getRevision().get().equals(otherCommit)) {
+ if (cd.currentPatchSet().commitId().name().equals(otherCommit)) {
return String.format(
"Commit %s depends on commit %s of change %d which cannot be merged.",
commit, otherCommit, cd.getId().get());
}
Optional<PatchSet> patchSet =
- cd.patchSets().stream()
- .filter(ps -> ps.getRevision().get().equals(otherCommit))
- .findAny();
+ cd.patchSets().stream().filter(ps -> ps.commitId().name().equals(otherCommit)).findAny();
if (patchSet.isPresent()) {
return String.format(
"Commit %s depends on commit %s, which is outdated patch set %d of change %d."
+ " The latest patch set is %d.",
commit,
otherCommit,
- patchSet.get().getId().get(),
+ patchSet.get().id().get(),
cd.getId().get(),
- cd.currentPatchSet().getId().get());
+ cd.currentPatchSet().id().get());
}
// should not happen, fall-back to default message
return String.format(
diff --git a/java/com/google/gerrit/server/submit/EmailMerge.java b/java/com/google/gerrit/server/submit/EmailMerge.java
index a1f56eb1ac..c94d49e303 100644
--- a/java/com/google/gerrit/server/submit/EmailMerge.java
+++ b/java/com/google/gerrit/server/submit/EmailMerge.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.submit;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.NotifyResolver;
diff --git a/java/com/google/gerrit/server/submit/FastForwardOp.java b/java/com/google/gerrit/server/submit/FastForwardOp.java
index 08f5abb06d..c83e113365 100644
--- a/java/com/google/gerrit/server/submit/FastForwardOp.java
+++ b/java/com/google/gerrit/server/submit/FastForwardOp.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.submit;
import static com.google.gerrit.server.submit.CommitMergeStatus.EMPTY_COMMIT;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
+import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.update.RepoContext;
diff --git a/java/com/google/gerrit/server/submit/GitModules.java b/java/com/google/gerrit/server/submit/GitModules.java
index d49f53f2f9..f8f6bc4e9a 100644
--- a/java/com/google/gerrit/server/submit/GitModules.java
+++ b/java/com/google/gerrit/server/submit/GitModules.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.submit;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.SubmoduleSubscription;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
@@ -45,7 +45,7 @@ public class GitModules {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public interface Factory {
- GitModules create(Branch.NameKey project, MergeOpRepoManager m);
+ GitModules create(BranchNameKey project, MergeOpRepoManager m);
}
private static final String GIT_MODULES = ".gitmodules";
@@ -55,16 +55,16 @@ public class GitModules {
@Inject
GitModules(
@CanonicalWebUrl @Nullable String canonicalWebUrl,
- @Assisted Branch.NameKey branch,
+ @Assisted BranchNameKey branch,
@Assisted MergeOpRepoManager orm)
throws IOException {
- Project.NameKey project = branch.getParentKey();
+ Project.NameKey project = branch.project();
logger.atFine().log("Loading .gitmodules of %s for project %s", branch, project);
try {
OpenRepo or = orm.getRepo(project);
- ObjectId id = or.repo.resolve(branch.get());
+ ObjectId id = or.repo.resolve(branch.branch());
if (id == null) {
- throw new IOException("Cannot open branch " + branch.get());
+ throw new IOException("Cannot open branch " + branch.branch());
}
RevCommit commit = or.rw.parseCommit(id);
@@ -80,7 +80,7 @@ public class GitModules {
config = new BlobBasedConfig(null, or.repo, commit, GIT_MODULES);
} catch (ConfigInvalidException e) {
throw new IOException(
- "Could not read .gitmodules of super project: " + branch.getParentKey(), e);
+ "Could not read .gitmodules of super project: " + branch.project(), e);
}
subscriptions =
new SubmoduleSectionParser(config, canonicalWebUrl, branch).parseAllSections();
@@ -89,7 +89,7 @@ public class GitModules {
}
}
- Collection<SubmoduleSubscription> subscribedTo(Branch.NameKey src) {
+ Collection<SubmoduleSubscription> subscribedTo(BranchNameKey src) {
Collection<SubmoduleSubscription> ret = new ArrayList<>();
for (SubmoduleSubscription s : subscriptions) {
if (s.getSubmodule().equals(src)) {
diff --git a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
index 1219124fb3..ec6b35a243 100644
--- a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
+++ b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
@@ -23,12 +23,12 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -53,7 +53,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
@@ -75,12 +74,12 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
@AutoValue
abstract static class QueryKey {
- private static QueryKey create(Branch.NameKey branch, Iterable<String> hashes) {
+ private static QueryKey create(BranchNameKey branch, Iterable<String> hashes) {
return new AutoValue_LocalMergeSuperSetComputation_QueryKey(
branch, ImmutableSet.copyOf(hashes));
}
- abstract Branch.NameKey branch();
+ abstract BranchNameKey branch();
abstract ImmutableSet<String> hashes();
}
@@ -88,7 +87,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
private final PermissionBackend permissionBackend;
private final Provider<InternalChangeQuery> queryProvider;
private final Map<QueryKey, ImmutableList<ChangeData>> queryCache;
- private final Map<Branch.NameKey, Optional<RevCommit>> heads;
+ private final Map<BranchNameKey, Optional<RevCommit>> heads;
private final ProjectCache projectCache;
private final ChangeIsVisibleToPredicate.Factory changeIsVisibleToPredicateFactory;
@@ -115,10 +114,10 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
// For each target branch we run a separate rev walk to find open changes
// reachable from changes already in the merge super set.
- ImmutableListMultimap<Branch.NameKey, ChangeData> bc =
+ ImmutableListMultimap<BranchNameKey, ChangeData> bc =
byBranch(Iterables.concat(changeSet.changes(), changeSet.nonVisibleChanges()));
- for (Branch.NameKey b : bc.keySet()) {
- OpenRepo or = getRepo(orm, b.getParentKey());
+ for (BranchNameKey b : bc.keySet()) {
+ OpenRepo or = getRepo(orm, b.project());
List<RevCommit> visibleCommits = new ArrayList<>();
List<RevCommit> nonVisibleCommits = new ArrayList<>();
for (ChangeData cd : bc.get(b)) {
@@ -135,8 +134,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
}
// Get the underlying git commit object
- String objIdStr = cd.currentPatchSet().getRevision().get();
- RevCommit commit = or.rw.parseCommit(ObjectId.fromString(objIdStr));
+ RevCommit commit = or.rw.parseCommit(cd.currentPatchSet().commitId());
// Always include the input, even if merged. This allows
// SubmitStrategyOp to correct the situation later, assuming it gets
@@ -161,9 +159,9 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
return new ChangeSet(visibleChanges, nonVisibleChanges);
}
- private static ImmutableListMultimap<Branch.NameKey, ChangeData> byBranch(
+ private static ImmutableListMultimap<BranchNameKey, ChangeData> byBranch(
Iterable<ChangeData> changes) {
- ImmutableListMultimap.Builder<Branch.NameKey, ChangeData> builder =
+ ImmutableListMultimap.Builder<BranchNameKey, ChangeData> builder =
ImmutableListMultimap.builder();
for (ChangeData cd : changes) {
builder.put(cd.change().getDest(), cd);
@@ -213,7 +211,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
private ChangeSet byCommitsOnBranchNotMerged(
OpenRepo or,
- Branch.NameKey branch,
+ BranchNameKey branch,
Set<String> visibleHashes,
Set<String> nonVisibleHashes,
CurrentUser user)
@@ -236,7 +234,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
}
private ImmutableList<ChangeData> byCommitsOnBranchNotMerged(
- OpenRepo or, Branch.NameKey branch, Set<String> hashes) throws IOException {
+ OpenRepo or, BranchNameKey branch, Set<String> hashes) throws IOException {
if (hashes.isEmpty()) {
return ImmutableList.of();
}
@@ -252,7 +250,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
}
private Set<String> walkChangesByHashes(
- Collection<RevCommit> sourceCommits, Set<String> ignoreHashes, OpenRepo or, Branch.NameKey b)
+ Collection<RevCommit> sourceCommits, Set<String> ignoreHashes, OpenRepo or, BranchNameKey b)
throws IOException {
Set<String> destHashes = new HashSet<>();
or.rw.reset();
@@ -276,10 +274,10 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
return destHashes;
}
- private void markHeadUninteresting(OpenRepo or, Branch.NameKey b) throws IOException {
+ private void markHeadUninteresting(OpenRepo or, BranchNameKey b) throws IOException {
Optional<RevCommit> head = heads.get(b);
if (head == null) {
- Ref ref = or.repo.getRefDatabase().exactRef(b.get());
+ Ref ref = or.repo.getRefDatabase().exactRef(b.branch());
head = ref != null ? Optional.of(or.rw.parseCommit(ref.getObjectId())) : Optional.empty();
heads.put(b, head);
}
diff --git a/java/com/google/gerrit/server/submit/MergeOneOp.java b/java/com/google/gerrit/server/submit/MergeOneOp.java
index 9806bdfe4b..4555a327d2 100644
--- a/java/com/google/gerrit/server/submit/MergeOneOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOneOp.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.submit;
import static com.google.gerrit.server.submit.CommitMergeStatus.EMPTY_COMMIT;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
+import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.update.RepoContext;
import java.io.IOException;
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 63d60a68ef..adb75a402e 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -36,6 +36,11 @@ import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRequirement;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.SubmitInput;
@@ -48,11 +53,6 @@ import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.metrics.Counter0;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
@@ -91,6 +91,7 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -119,7 +120,7 @@ public class MergeOp implements AutoCloseable {
public static class CommitStatus {
private final ImmutableMap<Change.Id, ChangeData> changes;
- private final ImmutableSetMultimap<Branch.NameKey, Change.Id> byBranch;
+ private final ImmutableSetMultimap<BranchNameKey, Change.Id> byBranch;
private final Map<Change.Id, CodeReviewCommit> commits;
private final ListMultimap<Change.Id, String> problems;
private final boolean allowClosed;
@@ -128,7 +129,7 @@ public class MergeOp implements AutoCloseable {
checkArgument(
!cs.furtherHiddenChanges(), "CommitStatus must not be called with hidden changes");
changes = cs.changesById();
- ImmutableSetMultimap.Builder<Branch.NameKey, Change.Id> bb = ImmutableSetMultimap.builder();
+ ImmutableSetMultimap.Builder<BranchNameKey, Change.Id> bb = ImmutableSetMultimap.builder();
for (ChangeData cd : cs.changes()) {
bb.put(cd.change().getDest(), cd.getId());
}
@@ -142,7 +143,7 @@ public class MergeOp implements AutoCloseable {
return changes.keySet();
}
- public ImmutableSet<Change.Id> getChangeIds(Branch.NameKey branch) {
+ public ImmutableSet<Change.Id> getChangeIds(BranchNameKey branch) {
return byBranch.get(branch);
}
@@ -233,6 +234,9 @@ public class MergeOp implements AutoCloseable {
private final RetryHelper retryHelper;
private final ChangeData.Factory changeDataFactory;
+ // Changes that were updated by this MergeOp.
+ private final Map<Change.Id, Change> updatedChanges;
+
private Timestamp ts;
private RequestId submissionId;
private IdentifiedUser caller;
@@ -244,6 +248,7 @@ public class MergeOp implements AutoCloseable {
private Set<Project.NameKey> allProjects;
private boolean dryrun;
private TopicMetrics topicMetrics;
+ private String traceId;
@Inject
MergeOp(
@@ -273,6 +278,7 @@ public class MergeOp implements AutoCloseable {
this.retryHelper = retryHelper;
this.topicMetrics = topicMetrics;
this.changeDataFactory = changeDataFactory;
+ this.updatedChanges = new HashMap<>();
}
@Override
@@ -296,7 +302,7 @@ public class MergeOp implements AutoCloseable {
throw new IllegalStateException(
String.format(
"SubmitRuleEvaluator.evaluate for change %s returned empty list for %s in %s",
- cd.getId(), patchSet.getId(), cd.change().getProject().get()));
+ cd.getId(), patchSet.id(), cd.change().getProject().get()));
}
for (SubmitRecord record : results) {
@@ -318,7 +324,7 @@ public class MergeOp implements AutoCloseable {
throw new IllegalStateException(
String.format(
"Unexpected SubmitRecord status %s for %s in %s",
- record.status, patchSet.getId().getId(), cd.change().getProject().get()));
+ record.status, patchSet.id().getId(), cd.change().getProject().get()));
}
}
throw new IllegalStateException();
@@ -428,8 +434,9 @@ public class MergeOp implements AutoCloseable {
* @throws RestApiException if an error occurred.
* @throws PermissionBackendException if permissions can't be checked
* @throws IOException an error occurred reading from NoteDb.
+ * @return the merged change
*/
- public void merge(
+ public Change merge(
Change change,
IdentifiedUser caller,
boolean checkSubmitRules,
@@ -511,11 +518,22 @@ public class MergeOp implements AutoCloseable {
retryHelper
.getDefaultTimeout(ActionType.CHANGE_UPDATE)
.multipliedBy(cs.projects().size()))
+ .caller(getClass())
+ .retryWithTrace(t -> !(t instanceof RestApiException))
+ .onAutoTrace(traceId -> this.traceId = traceId)
.build());
if (projects > 1) {
topicMetrics.topicSubmissionsCompleted.increment();
}
+
+ // It's expected that callers invoke this method only for open changes and that the provided
+ // change either gets updated to merged or that this method fails with an exception. For
+ // safety, fall-back to return the provided change if there was no update for this change
+ // (e.g. caller provided a change that was already merged).
+ return updatedChanges.containsKey(change.getId())
+ ? updatedChanges.get(change.getId())
+ : change;
} catch (IOException e) {
// Anything before the merge attempt is an error
throw new StorageException(e);
@@ -523,6 +541,10 @@ public class MergeOp implements AutoCloseable {
}
}
+ public Optional<String> getTraceId() {
+ return Optional.ofNullable(traceId);
+ }
+
private void openRepoManager() {
if (orm != null) {
orm.close();
@@ -573,18 +595,18 @@ public class MergeOp implements AutoCloseable {
throws IntegrationException, RestApiException, UpdateException {
checkArgument(!cs.furtherHiddenChanges(), "cannot integrate hidden changes into history");
logger.atFine().log("Beginning merge attempt on %s", cs);
- Map<Branch.NameKey, BranchBatch> toSubmit = new HashMap<>();
+ Map<BranchNameKey, BranchBatch> toSubmit = new HashMap<>();
- ListMultimap<Branch.NameKey, ChangeData> cbb;
+ ListMultimap<BranchNameKey, ChangeData> cbb;
try {
cbb = cs.changesByBranch();
} catch (StorageException e) {
throw new IntegrationException("Error reading changes to submit", e);
}
- Set<Branch.NameKey> branches = cbb.keySet();
+ Set<BranchNameKey> branches = cbb.keySet();
- for (Branch.NameKey branch : branches) {
- OpenRepo or = openRepo(branch.getParentKey());
+ for (BranchNameKey branch : branches) {
+ OpenRepo or = openRepo(branch.project());
if (or != null) {
toSubmit.put(branch, validateChangeList(or, cbb.get(branch)));
}
@@ -597,10 +619,17 @@ public class MergeOp implements AutoCloseable {
SubmoduleOp submoduleOp = subOpFactory.create(branches, orm);
List<SubmitStrategy> strategies = getSubmitStrategies(toSubmit, submoduleOp, dryrun);
this.allProjects = submoduleOp.getProjectsInOrder();
- BatchUpdate.execute(
- orm.batchUpdates(allProjects),
- new SubmitStrategyListener(submitInput, strategies, commitStatus),
- dryrun);
+ try {
+ BatchUpdate.execute(
+ orm.batchUpdates(allProjects),
+ new SubmitStrategyListener(submitInput, strategies, commitStatus),
+ dryrun);
+ } finally {
+ // If the BatchUpdate fails it can be that merging some of the changes was actually
+ // successful. This is why we must to collect the updated changes also when an exception was
+ // thrown.
+ strategies.forEach(s -> updatedChanges.putAll(s.getUpdatedChanges()));
+ }
} catch (NoSuchProjectException e) {
throw new ResourceNotFoundException(e.getMessage());
} catch (IOException | SubmoduleException e) {
@@ -641,14 +670,14 @@ public class MergeOp implements AutoCloseable {
}
private List<SubmitStrategy> getSubmitStrategies(
- Map<Branch.NameKey, BranchBatch> toSubmit, SubmoduleOp submoduleOp, boolean dryrun)
+ Map<BranchNameKey, BranchBatch> toSubmit, SubmoduleOp submoduleOp, boolean dryrun)
throws IntegrationException, NoSuchProjectException, IOException {
List<SubmitStrategy> strategies = new ArrayList<>();
- Set<Branch.NameKey> allBranches = submoduleOp.getBranchesInOrder();
+ Set<BranchNameKey> allBranches = submoduleOp.getBranchesInOrder();
Set<CodeReviewCommit> allCommits =
toSubmit.values().stream().map(BranchBatch::commits).flatMap(Set::stream).collect(toSet());
- for (Branch.NameKey branch : allBranches) {
- OpenRepo or = orm.getRepo(branch.getParentKey());
+ for (BranchNameKey branch : allBranches) {
+ OpenRepo or = orm.getRepo(branch.project());
if (toSubmit.containsKey(branch)) {
BranchBatch submitting = toSubmit.get(branch);
logger.atFine().log("adding ops for branch batch %s", submitting);
@@ -769,55 +798,47 @@ public class MergeOp implements AutoCloseable {
}
PatchSet ps;
- Branch.NameKey destBranch = chg.getDest();
+ BranchNameKey destBranch = chg.getDest();
try {
ps = cd.currentPatchSet();
} catch (StorageException e) {
commitStatus.logProblem(changeId, e);
continue;
}
- if (ps == null || ps.getRevision() == null || ps.getRevision().get() == null) {
- commitStatus.logProblem(changeId, "Missing patch set or revision on change");
- continue;
- }
-
- String idstr = ps.getRevision().get();
- ObjectId id;
- try {
- id = ObjectId.fromString(idstr);
- } catch (IllegalArgumentException e) {
- commitStatus.logProblem(changeId, e);
+ if (ps == null) {
+ commitStatus.logProblem(changeId, "Missing patch set on change");
continue;
}
- if (!revisions.containsEntry(id, ps.getId())) {
- if (revisions.containsValue(ps.getId())) {
+ ObjectId id = ps.commitId();
+ if (!revisions.containsEntry(id, ps.id())) {
+ if (revisions.containsValue(ps.id())) {
// TODO This is actually an error, the patch set ref exists but points to a revision that
// is different from the revision that we have stored for the patch set in the change
// meta data.
commitStatus.logProblem(
changeId,
"Revision "
- + idstr
+ + id.name()
+ " of patch set "
- + ps.getPatchSetId()
+ + ps.number()
+ " does not match the revision of the patch set ref "
- + ps.getId().toRefName());
+ + ps.id().toRefName());
continue;
}
// The patch set ref is not found but we want to merge the change. We can't safely do that
- // if the patch set ref is missing. In a multi-master setup this can indicate a replication
- // lag (e.g. the change meta data was already replicated, but the replication of the patch
- // set ref is still pending).
+ // if the patch set ref is missing. In a cluster setups with multiple primary nodes this can
+ // indicate a replication lag (e.g. the change meta data was already replicated, but the
+ // replication of the patch set ref is still pending).
commitStatus.logProblem(
changeId,
"Patch set ref "
- + ps.getId().toRefName()
+ + ps.id().toRefName()
+ " not found. Expected patch set ref of "
- + ps.getPatchSetId()
+ + ps.number()
+ " to point to revision "
- + idstr);
+ + id.name());
continue;
}
@@ -830,13 +851,12 @@ public class MergeOp implements AutoCloseable {
}
commit.setNotes(notes);
- commit.setPatchsetId(ps.getId());
+ commit.setPatchsetId(ps.id());
commitStatus.put(commit);
MergeValidators mergeValidators = mergeValidatorsFactory.create();
try {
- mergeValidators.validatePreMerge(
- or.repo, commit, or.project, destBranch, ps.getId(), caller);
+ mergeValidators.validatePreMerge(or.repo, commit, or.project, destBranch, ps.id(), caller);
} catch (MergeValidationException mve) {
commitStatus.problem(changeId, mve.getMessage());
continue;
diff --git a/java/com/google/gerrit/server/submit/MergeOpRepoManager.java b/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
index 764aca8ce0..c2577e7f16 100644
--- a/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
+++ b/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
@@ -18,9 +18,9 @@ import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import com.google.common.collect.Maps;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.git.CodeReviewCommit;
@@ -67,7 +67,7 @@ public class MergeOpRepoManager implements AutoCloseable {
BatchUpdate update;
private final ObjectReader reader;
- private final Map<Branch.NameKey, OpenBranch> branches;
+ private final Map<BranchNameKey, OpenBranch> branches;
private OpenRepo(Repository repo, ProjectState project) {
this.repo = repo;
@@ -84,7 +84,7 @@ public class MergeOpRepoManager implements AutoCloseable {
branches = Maps.newHashMapWithExpectedSize(1);
}
- OpenBranch getBranch(Branch.NameKey branch) throws IntegrationException {
+ OpenBranch getBranch(BranchNameKey branch) throws IntegrationException {
OpenBranch ob = branches.get(branch);
if (ob == null) {
ob = new OpenBranch(this, branch);
@@ -134,13 +134,13 @@ public class MergeOpRepoManager implements AutoCloseable {
final CodeReviewCommit oldTip;
MergeTip mergeTip;
- OpenBranch(OpenRepo or, Branch.NameKey name) throws IntegrationException {
+ OpenBranch(OpenRepo or, BranchNameKey name) throws IntegrationException {
try {
- update = or.repo.updateRef(name.get());
+ update = or.repo.updateRef(name.branch());
if (update.getOldObjectId() != null) {
oldTip = or.rw.parseCommit(update.getOldObjectId());
- } else if (Objects.equals(or.repo.getFullBranch(), name.get())
- || Objects.equals(RefNames.REFS_CONFIG, name.get())) {
+ } else if (Objects.equals(or.repo.getFullBranch(), name.branch())
+ || Objects.equals(RefNames.REFS_CONFIG, name.branch())) {
oldTip = null;
update.setExpectedOldObjectId(ObjectId.zeroId());
} else {
diff --git a/java/com/google/gerrit/server/submit/MergeSuperSet.java b/java/com/google/gerrit/server/submit/MergeSuperSet.java
index d729833a96..bcebc7f27c 100644
--- a/java/com/google/gerrit/server/submit/MergeSuperSet.java
+++ b/java/com/google/gerrit/server/submit/MergeSuperSet.java
@@ -19,9 +19,9 @@ import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.logging.TraceContext;
diff --git a/java/com/google/gerrit/server/submit/RebaseSorter.java b/java/com/google/gerrit/server/submit/RebaseSorter.java
index 829ee9c575..47757681fa 100644
--- a/java/com/google/gerrit/server/submit/RebaseSorter.java
+++ b/java/com/google/gerrit/server/submit/RebaseSorter.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.submit;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
@@ -108,7 +108,7 @@ public class RebaseSorter {
return sorted;
}
- private boolean isAlreadyMerged(CodeReviewCommit commit, Branch.NameKey dest) throws IOException {
+ private boolean isAlreadyMerged(CodeReviewCommit commit, BranchNameKey dest) throws IOException {
try (CodeReviewRevWalk mirw = CodeReviewCommit.newRevWalk(rw.getObjectReader())) {
mirw.reset();
mirw.markStart(commit);
diff --git a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
index b2ad4e253a..65e18ad812 100644
--- a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
@@ -19,12 +19,12 @@ import static com.google.gerrit.server.submit.CommitMergeStatus.EMPTY_COMMIT;
import static com.google.gerrit.server.submit.CommitMergeStatus.SKIPPED_IDENTICAL_TREE;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.RebaseChangeOp;
import com.google.gerrit.server.git.CodeReviewCommit;
@@ -235,7 +235,7 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
ctx.getUpdate(newPatchSetId),
newPatchSetId,
newCommit,
- prevPs != null ? prevPs.getGroups() : ImmutableList.of(),
+ prevPs != null ? prevPs.groups() : ImmutableList.of(),
null,
null);
}
@@ -310,7 +310,6 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
throws IntegrationException {
// Test for merge instead of cherry pick to avoid false negatives
// on commit chains.
- return !args.mergeUtil.hasMissingDependencies(args.mergeSorter, toMerge)
- && args.mergeUtil.canMerge(args.mergeSorter, repo, mergeTip, toMerge);
+ return args.mergeUtil.canMerge(args.mergeSorter, repo, mergeTip, toMerge);
}
}
diff --git a/java/com/google/gerrit/server/submit/SubmitDryRun.java b/java/com/google/gerrit/server/submit/SubmitDryRun.java
index 391d9562bb..ff1a1f023c 100644
--- a/java/com/google/gerrit/server/submit/SubmitDryRun.java
+++ b/java/com/google/gerrit/server/submit/SubmitDryRun.java
@@ -20,8 +20,8 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
@@ -112,7 +112,7 @@ public class SubmitDryRun {
SubmitType submitType,
Repository repo,
CodeReviewRevWalk rw,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
ObjectId tip,
ObjectId toMerge,
Set<RevCommit> alreadyAccepted)
@@ -155,10 +155,10 @@ public class SubmitDryRun {
}
}
- private ProjectState getProject(Branch.NameKey branch) throws NoSuchProjectException {
- ProjectState p = projectCache.get(branch.getParentKey());
+ private ProjectState getProject(BranchNameKey branch) throws NoSuchProjectException {
+ ProjectState p = projectCache.get(branch.project());
if (p == null) {
- throw new NoSuchProjectException(branch.getParentKey());
+ throw new NoSuchProjectException(branch.project());
}
return p;
}
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategy.java b/java/com/google/gerrit/server/submit/SubmitStrategy.java
index dc221f87a2..4c68e1b521 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategy.java
@@ -14,14 +14,16 @@
package com.google.gerrit.server.submit;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.util.Objects.requireNonNull;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.GerritPersonIdent;
@@ -55,7 +57,9 @@ import com.google.inject.assistedinject.Assisted;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -82,7 +86,7 @@ public abstract class SubmitStrategy {
interface Factory {
Arguments create(
SubmitType submitType,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
CommitStatus commitStatus,
CodeReviewRevWalk rw,
IdentifiedUser caller,
@@ -114,7 +118,7 @@ public abstract class SubmitStrategy {
final ProjectConfig.Factory projectConfigFactory;
final SetPrivateOp.Factory setPrivateOpFactory;
- final Branch.NameKey destBranch;
+ final BranchNameKey destBranch;
final CodeReviewRevWalk rw;
final CommitStatus commitStatus;
final IdentifiedUser caller;
@@ -152,7 +156,7 @@ public abstract class SubmitStrategy {
Provider<InternalChangeQuery> queryProvider,
ProjectConfig.Factory projectConfigFactory,
SetPrivateOp.Factory setPrivateOpFactory,
- @Assisted Branch.NameKey destBranch,
+ @Assisted BranchNameKey destBranch,
@Assisted CommitStatus commitStatus,
@Assisted CodeReviewRevWalk rw,
@Assisted IdentifiedUser caller,
@@ -197,8 +201,8 @@ public abstract class SubmitStrategy {
this.project =
requireNonNull(
- projectCache.get(destBranch.getParentKey()),
- () -> String.format("project not found: %s", destBranch.getParentKey()));
+ projectCache.get(destBranch.project()),
+ () -> String.format("project not found: %s", destBranch.project()));
this.mergeSorter =
new MergeSorter(caller, rw, alreadyAccepted, canMergeFlag, queryProvider, incoming);
this.rebaseSorter =
@@ -217,8 +221,24 @@ public abstract class SubmitStrategy {
final Arguments args;
+ private final Set<SubmitStrategyOp> submitStrategyOps;
+
SubmitStrategy(Arguments args) {
this.args = requireNonNull(args);
+ this.submitStrategyOps = new HashSet<>();
+ }
+
+ /**
+ * Returns the updated changed after this submit strategy has been executed.
+ *
+ * @return the updated changes after this submit strategy has been executed
+ */
+ public ImmutableMap<Change.Id, Change> getUpdatedChanges() {
+ return submitStrategyOps.stream()
+ .map(SubmitStrategyOp::getUpdatedChange)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toImmutableMap(c -> c.getId(), c -> c));
}
/**
@@ -249,8 +269,10 @@ public abstract class SubmitStrategy {
for (CodeReviewCommit c : difference) {
Change.Id id = c.change().getId();
bu.addOp(id, args.setPrivateOpFactory.create(false, null));
- bu.addOp(id, new ImplicitIntegrateOp(args, c));
+ ImplicitIntegrateOp implicitIntegrateOp = new ImplicitIntegrateOp(args, c);
+ bu.addOp(id, implicitIntegrateOp);
maybeAddTestHelperOp(bu, id);
+ this.submitStrategyOps.add(implicitIntegrateOp);
}
// Then ops for explicitly merged changes
@@ -258,6 +280,7 @@ public abstract class SubmitStrategy {
bu.addOp(op.getId(), args.setPrivateOpFactory.create(false, null));
bu.addOp(op.getId(), op);
maybeAddTestHelperOp(bu, op.getId());
+ this.submitStrategyOps.add(op);
}
}
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java b/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
index 30326f773d..cba572bcb7 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
@@ -15,9 +15,9 @@
package com.google.gerrit.server.submit;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
@@ -48,7 +48,7 @@ public class SubmitStrategyFactory {
RevFlag canMergeFlag,
Set<RevCommit> alreadyAccepted,
Set<CodeReviewCommit> incoming,
- Branch.NameKey destBranch,
+ BranchNameKey destBranch,
IdentifiedUser caller,
MergeTip mergeTip,
CommitStatus commitStatus,
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyListener.java b/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
index 782cd7bde6..f8bcfc189b 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
@@ -17,9 +17,9 @@ package com.google.gerrit.server.submit;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.TestSubmitInput;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.submit.MergeOp.CommitStatus;
@@ -129,7 +129,7 @@ public class SubmitStrategyListener implements BatchUpdateListener {
case ALREADY_MERGED:
// Already an ancestor of tip.
- alreadyMerged.add(commit.getPatchsetId().getParentKey());
+ alreadyMerged.add(commit.getPatchsetId().changeId());
break;
case PATH_CONFLICT:
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index 14522a2a70..79f062d5f3 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -20,19 +20,18 @@ import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
-import com.google.common.base.Function;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
@@ -89,12 +88,12 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
return toMerge;
}
- protected final Branch.NameKey getDest() {
+ protected final BranchNameKey getDest() {
return toMerge.change().getDest();
}
protected final Project.NameKey getProject() {
- return getDest().getParentKey();
+ return getDest().project();
}
@Override
@@ -132,14 +131,15 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// Needed by postUpdate, at which point mergeTip will have advanced further,
// so it's easier to just snapshot the command.
command =
- new ReceiveCommand(firstNonNull(tipBefore, ObjectId.zeroId()), tipAfter, getDest().get());
+ new ReceiveCommand(
+ firstNonNull(tipBefore, ObjectId.zeroId()), tipAfter, getDest().branch());
ctx.addRefUpdate(command);
args.submoduleOp.addBranchTip(getDest(), tipAfter);
}
private void checkProjectConfig(RepoContext ctx, CodeReviewCommit commit)
throws IntegrationException {
- String refName = getDest().get();
+ String refName = getDest().branch();
if (RefNames.REFS_CONFIG.equals(refName)) {
logger.atFine().log("Loading new configuration from %s", RefNames.REFS_CONFIG);
try {
@@ -251,7 +251,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
args.psUtil.get(ctx.getNotes(), oldPsId),
() -> String.format("missing old patch set %s", oldPsId));
} else {
- PatchSet.Id n = newPatchSet.getId();
+ PatchSet.Id n = newPatchSet.id();
checkState(
!n.equals(oldPsId) && n.equals(newPsId),
"current patch was %s and is now %s, but updateChangeImpl returned"
@@ -294,6 +294,16 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
return true;
}
+ /**
+ * Returns the updated change after this op has been executed.
+ *
+ * @return the updated change after this op has been executed, {@link Optional#empty()} if the op
+ * was not executed yet, or if the execution has failed
+ */
+ public Optional<Change> getUpdatedChange() {
+ return Optional.ofNullable(updatedChange);
+ }
+
private PatchSet getOrCreateAlreadyMergedPatchSet(ChangeContext ctx) throws IOException {
PatchSet.Id psId = alreadyMergedCommit.getPatchsetId();
logger.atFine().log("Fixing up already-merged patch set %s", psId);
@@ -311,7 +321,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// a patch set ref. Fix up the database. Note that this uses the current
// user as the uploader, which is as good a guess as any.
List<String> groups =
- prevPs != null ? prevPs.getGroups() : GroupCollector.getDefaultGroups(alreadyMergedCommit);
+ prevPs != null ? prevPs.groups() : GroupCollector.getDefaultGroups(alreadyMergedCommit);
return args.psUtil.insert(
ctx.getRevWalk(), ctx.getUpdate(psId), psId, alreadyMergedCommit, groups, null, null);
}
@@ -333,7 +343,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// approvals as well.
if (!newPsId.equals(oldPsId)) {
saveApprovals(normalized, newPsUpdate, true);
- submitter = convertPatchSet(newPsId).apply(submitter);
+ submitter = submitter.copyWithPatchSet(newPsId);
}
}
@@ -344,12 +354,13 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
for (PatchSetApproval psa :
args.approvalsUtil.byPatchSet(
ctx.getNotes(), psId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
- byKey.put(psa.getKey(), psa);
+ byKey.put(psa.key(), psa);
}
submitter =
- ApprovalsUtil.newApproval(psId, ctx.getUser(), LabelId.legacySubmit(), 1, ctx.getWhen());
- byKey.put(submitter.getKey(), submitter);
+ ApprovalsUtil.newApproval(psId, ctx.getUser(), LabelId.legacySubmit(), 1, ctx.getWhen())
+ .build();
+ byKey.put(submitter.key(), submitter);
// Flatten out existing approvals for this patch set based upon the current
// permissions. Once the change is closed the approvals are not updated at
@@ -358,7 +369,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// permissions get modified in the future, historical records stay accurate.
LabelNormalizer.Result normalized =
args.labelNormalizer.normalize(ctx.getNotes(), byKey.values());
- update.putApproval(submitter.getLabel(), submitter.getValue());
+ update.putApproval(submitter.label(), submitter.value());
saveApprovals(normalized, update, false);
return normalized;
}
@@ -366,10 +377,10 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
private void saveApprovals(
LabelNormalizer.Result normalized, ChangeUpdate update, boolean includeUnchanged) {
for (PatchSetApproval psa : normalized.updated()) {
- update.putApprovalFor(psa.getAccountId(), psa.getLabel(), psa.getValue());
+ update.putApprovalFor(psa.accountId(), psa.label(), psa.value());
}
for (PatchSetApproval psa : normalized.deleted()) {
- update.removeApprovalFor(psa.getAccountId(), psa.getLabel());
+ update.removeApprovalFor(psa.accountId(), psa.label());
}
// TODO(dborowitz): Don't use a label in NoteDb; just check when status
@@ -377,27 +388,17 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
for (PatchSetApproval psa : normalized.unchanged()) {
if (includeUnchanged || psa.isLegacySubmit()) {
logger.atFine().log("Adding submit label %s", psa);
- update.putApprovalFor(psa.getAccountId(), psa.getLabel(), psa.getValue());
+ update.putApprovalFor(psa.accountId(), psa.label(), psa.value());
}
}
}
- private static Function<PatchSetApproval, PatchSetApproval> convertPatchSet(
- final PatchSet.Id psId) {
- return psa -> {
- if (psa.getPatchSetId().equals(psId)) {
- return psa;
- }
- return new PatchSetApproval(psId, psa);
- };
- }
-
private String getByAccountName() {
requireNonNull(submitter, "getByAccountName called before submitter populated");
Optional<Account> account =
- args.accountCache.get(submitter.getAccountId()).map(AccountState::getAccount);
- if (account.isPresent() && account.get().getFullName() != null) {
- return " by " + account.get().getFullName();
+ args.accountCache.get(submitter.accountId()).map(AccountState::account);
+ if (account.isPresent() && account.get().fullName() != null) {
+ return " by " + account.get().fullName();
}
return "";
}
@@ -483,7 +484,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
getProject(), command.getRefName(), command.getOldId(), command.getNewId());
// TODO(dborowitz): Move to BatchUpdate? Would also allow us to run once
// per project even if multiple changes to refs/meta/config are submitted.
- if (RefNames.REFS_CONFIG.equals(getDest().get())) {
+ if (RefNames.REFS_CONFIG.equals(getDest().branch())) {
args.projectCache.evict(getProject());
ProjectState p = args.projectCache.get(getProject());
try (Repository git = args.repoManager.openRepository(getProject())) {
@@ -498,7 +499,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// have failed fast in one of the other steps.
try {
args.mergedSenderFactory
- .create(ctx.getProject(), getId(), submitter.getAccountId(), ctx.getNotify(getId()))
+ .create(ctx.getProject(), getId(), submitter.accountId(), ctx.getNotify(getId()))
.sendAsync();
} catch (Exception e) {
logger.atSevere().withCause(e).log("Cannot email merged notification for %s", getId());
@@ -507,7 +508,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
args.changeMerged.fire(
updatedChange,
mergedPatchSet,
- args.accountCache.get(submitter.getAccountId()).orElse(null),
+ args.accountCache.get(submitter.accountId()).orElse(null),
args.mergeTip.getCurrentTip().name(),
ctx.getWhen());
}
diff --git a/java/com/google/gerrit/server/submit/SubmoduleOp.java b/java/com/google/gerrit/server/submit/SubmoduleOp.java
index afcf9c5d76..8ab99ddb01 100644
--- a/java/com/google/gerrit/server/submit/SubmoduleOp.java
+++ b/java/com/google/gerrit/server/submit/SubmoduleOp.java
@@ -23,11 +23,11 @@ import com.google.common.collect.SetMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.entities.SubmoduleSubscription;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.VerboseSuperprojectUpdate;
@@ -79,9 +79,9 @@ public class SubmoduleOp {
/** Only used for branches without code review changes */
public class GitlinkOp implements RepoOnlyOp {
- private final Branch.NameKey branch;
+ private final BranchNameKey branch;
- GitlinkOp(Branch.NameKey branch) {
+ GitlinkOp(BranchNameKey branch) {
this.branch = branch;
}
@@ -89,7 +89,7 @@ public class SubmoduleOp {
public void updateRepo(RepoContext ctx) throws Exception {
CodeReviewCommit c = composeGitlinksCommit(branch);
if (c != null) {
- ctx.addRefUpdate(c.getParent(0), c, branch.get());
+ ctx.addRefUpdate(c.getParent(0), c, branch.branch());
addBranchTip(branch, c);
}
}
@@ -114,7 +114,7 @@ public class SubmoduleOp {
this.projectCache = projectCache;
}
- public SubmoduleOp create(Set<Branch.NameKey> updatedBranches, MergeOpRepoManager orm)
+ public SubmoduleOp create(Set<BranchNameKey> updatedBranches, MergeOpRepoManager orm)
throws SubmoduleException {
return new SubmoduleOp(
gitmodulesFactory, serverIdent.get(), cfg, projectCache, updatedBranches, orm);
@@ -129,41 +129,41 @@ public class SubmoduleOp {
private final long maxCombinedCommitMessageSize;
private final long maxCommitMessages;
private final MergeOpRepoManager orm;
- private final Map<Branch.NameKey, GitModules> branchGitModules;
+ private final Map<BranchNameKey, GitModules> branchGitModules;
/** Branches updated as part of the enclosing submit or push batch. */
- private final ImmutableSet<Branch.NameKey> updatedBranches;
+ private final ImmutableSet<BranchNameKey> updatedBranches;
/**
* Current branch tips, taking into account commits created during the submit process as well as
* submodule updates produced by this class.
*/
- private final Map<Branch.NameKey, CodeReviewCommit> branchTips;
+ private final Map<BranchNameKey, CodeReviewCommit> branchTips;
/**
* Branches in a superproject that contain submodule subscriptions, plus branches in submodules
* which are subscribed to by some superproject.
*/
- private final Set<Branch.NameKey> affectedBranches;
+ private final Set<BranchNameKey> affectedBranches;
/** Copy of {@link #affectedBranches}, sorted by submodule traversal order. */
- private final ImmutableSet<Branch.NameKey> sortedBranches;
+ private final ImmutableSet<BranchNameKey> sortedBranches;
/** Multimap of superproject branch to submodule subscriptions contained in that branch. */
- private final SetMultimap<Branch.NameKey, SubmoduleSubscription> targets;
+ private final SetMultimap<BranchNameKey, SubmoduleSubscription> targets;
/**
* Multimap of superproject name to all branch names within that superproject which have submodule
* subscriptions.
*/
- private final SetMultimap<Project.NameKey, Branch.NameKey> branchesByProject;
+ private final SetMultimap<Project.NameKey, BranchNameKey> branchesByProject;
private SubmoduleOp(
GitModules.Factory gitmodulesFactory,
PersonIdent myIdent,
Config cfg,
ProjectCache projectCache,
- Set<Branch.NameKey> updatedBranches,
+ Set<BranchNameKey> updatedBranches,
MergeOpRepoManager orm)
throws SubmoduleException {
this.gitmodulesFactory = gitmodulesFactory;
@@ -214,15 +214,15 @@ public class SubmoduleOp {
//
// In addition to improving readability, this approach has the advantage of making (1) and (2)
// testable using small tests.
- private ImmutableSet<Branch.NameKey> calculateSubscriptionMaps() throws SubmoduleException {
+ private ImmutableSet<BranchNameKey> calculateSubscriptionMaps() throws SubmoduleException {
if (!enableSuperProjectSubscriptions) {
logger.atFine().log("Updating superprojects disabled");
return null;
}
logger.atFine().log("Calculating superprojects - submodules map");
- LinkedHashSet<Branch.NameKey> allVisited = new LinkedHashSet<>();
- for (Branch.NameKey updatedBranch : updatedBranches) {
+ LinkedHashSet<BranchNameKey> allVisited = new LinkedHashSet<>();
+ for (BranchNameKey updatedBranch : updatedBranches) {
if (allVisited.contains(updatedBranch)) {
continue;
}
@@ -240,9 +240,9 @@ public class SubmoduleOp {
}
private void searchForSuperprojects(
- Branch.NameKey current,
- LinkedHashSet<Branch.NameKey> currentVisited,
- LinkedHashSet<Branch.NameKey> allVisited)
+ BranchNameKey current,
+ LinkedHashSet<BranchNameKey> currentVisited,
+ LinkedHashSet<BranchNameKey> allVisited)
throws SubmoduleException {
logger.atFine().log("Now processing %s", current);
@@ -261,10 +261,10 @@ public class SubmoduleOp {
Collection<SubmoduleSubscription> subscriptions =
superProjectSubscriptionsForSubmoduleBranch(current);
for (SubmoduleSubscription sub : subscriptions) {
- Branch.NameKey superBranch = sub.getSuperProject();
+ BranchNameKey superBranch = sub.getSuperProject();
searchForSuperprojects(superBranch, currentVisited, allVisited);
targets.put(superBranch, sub);
- branchesByProject.put(superBranch.getParentKey(), superBranch);
+ branchesByProject.put(superBranch.project(), superBranch);
affectedBranches.add(superBranch);
affectedBranches.add(sub.getSubmodule());
}
@@ -303,31 +303,33 @@ public class SubmoduleOp {
return sb.toString();
}
- private Collection<Branch.NameKey> getDestinationBranches(Branch.NameKey src, SubscribeSection s)
+ private Collection<BranchNameKey> getDestinationBranches(BranchNameKey src, SubscribeSection s)
throws IOException {
- Collection<Branch.NameKey> ret = new HashSet<>();
+ Collection<BranchNameKey> ret = new HashSet<>();
logger.atFine().log("Inspecting SubscribeSection %s", s);
for (RefSpec r : s.getMatchingRefSpecs()) {
logger.atFine().log("Inspecting [matching] ref %s", r);
- if (!r.matchSource(src.get())) {
+ if (!r.matchSource(src.branch())) {
continue;
}
if (r.isWildcard()) {
// refs/heads/*[:refs/somewhere/*]
- ret.add(new Branch.NameKey(s.getProject(), r.expandFromSource(src.get()).getDestination()));
+ ret.add(
+ BranchNameKey.create(
+ s.getProject(), r.expandFromSource(src.branch()).getDestination()));
} else {
// e.g. refs/heads/master[:refs/heads/stable]
String dest = r.getDestination();
if (dest == null) {
dest = r.getSource();
}
- ret.add(new Branch.NameKey(s.getProject(), dest));
+ ret.add(BranchNameKey.create(s.getProject(), dest));
}
}
for (RefSpec r : s.getMultiMatchRefSpecs()) {
logger.atFine().log("Inspecting [all] ref %s", r);
- if (!r.matchSource(src.get())) {
+ if (!r.matchSource(src.branch())) {
continue;
}
OpenRepo or;
@@ -344,7 +346,7 @@ public class SubmoduleOp {
if (r.getDestination() != null && !r.matchDestination(ref.getName())) {
continue;
}
- Branch.NameKey b = new Branch.NameKey(s.getProject(), ref.getName());
+ BranchNameKey b = BranchNameKey.create(s.getProject(), ref.getName());
if (!ret.contains(b)) {
ret.add(b);
}
@@ -356,18 +358,18 @@ public class SubmoduleOp {
@UsedAt(UsedAt.Project.PLUGIN_DELETE_PROJECT)
public Collection<SubmoduleSubscription> superProjectSubscriptionsForSubmoduleBranch(
- Branch.NameKey srcBranch) throws IOException {
+ BranchNameKey srcBranch) throws IOException {
logger.atFine().log("Calculating possible superprojects for %s", srcBranch);
Collection<SubmoduleSubscription> ret = new ArrayList<>();
- Project.NameKey srcProject = srcBranch.getParentKey();
+ Project.NameKey srcProject = srcBranch.project();
for (SubscribeSection s : projectCache.get(srcProject).getSubscribeSections(srcBranch)) {
logger.atFine().log("Checking subscribe section %s", s);
- Collection<Branch.NameKey> branches = getDestinationBranches(srcBranch, s);
- for (Branch.NameKey targetBranch : branches) {
- Project.NameKey targetProject = targetBranch.getParentKey();
+ Collection<BranchNameKey> branches = getDestinationBranches(srcBranch, s);
+ for (BranchNameKey targetBranch : branches) {
+ Project.NameKey targetProject = targetBranch.project();
try {
OpenRepo or = orm.getRepo(targetProject);
- ObjectId id = or.repo.resolve(targetBranch.get());
+ ObjectId id = or.repo.resolve(targetBranch.branch());
if (id == null) {
logger.atFine().log("The branch %s doesn't exist.", targetBranch);
continue;
@@ -403,7 +405,7 @@ public class SubmoduleOp {
superProjects.add(project);
// get a new BatchUpdate for the super project
OpenRepo or = orm.getRepo(project);
- for (Branch.NameKey branch : branchesByProject.get(project)) {
+ for (BranchNameKey branch : branchesByProject.get(project)) {
addOp(or.getUpdate(), branch);
}
}
@@ -415,11 +417,11 @@ public class SubmoduleOp {
}
/** Create a separate gitlink commit */
- private CodeReviewCommit composeGitlinksCommit(Branch.NameKey subscriber)
+ private CodeReviewCommit composeGitlinksCommit(BranchNameKey subscriber)
throws IOException, SubmoduleException {
OpenRepo or;
try {
- or = orm.getRepo(subscriber.getParentKey());
+ or = orm.getRepo(subscriber.project());
} catch (NoSuchProjectException | IOException e) {
throw new SubmoduleException("Cannot access superproject", e);
}
@@ -428,7 +430,7 @@ public class SubmoduleOp {
if (branchTips.containsKey(subscriber)) {
currentCommit = branchTips.get(subscriber);
} else {
- Ref r = or.repo.exactRef(subscriber.get());
+ Ref r = or.repo.exactRef(subscriber.branch());
if (r == null) {
throw new SubmoduleException(
"The branch was probably deleted from the subscriber repository");
@@ -485,11 +487,11 @@ public class SubmoduleOp {
}
/** Amend an existing commit with gitlink updates */
- CodeReviewCommit composeGitlinksCommit(Branch.NameKey subscriber, CodeReviewCommit currentCommit)
+ CodeReviewCommit composeGitlinksCommit(BranchNameKey subscriber, CodeReviewCommit currentCommit)
throws IOException, SubmoduleException {
OpenRepo or;
try {
- or = orm.getRepo(subscriber.getParentKey());
+ or = orm.getRepo(subscriber.project());
} catch (NoSuchProjectException | IOException e) {
throw new SubmoduleException("Cannot access superproject", e);
}
@@ -531,7 +533,7 @@ public class SubmoduleOp {
logger.atFine().log("Updating gitlink for %s", s);
OpenRepo subOr;
try {
- subOr = orm.getRepo(s.getSubmodule().getParentKey());
+ subOr = orm.getRepo(s.getSubmodule().project());
} catch (NoSuchProjectException | IOException e) {
throw new SubmoduleException("Cannot access submodule", e);
}
@@ -544,7 +546,7 @@ public class SubmoduleOp {
"Requested to update gitlink "
+ s.getPath()
+ " in "
- + s.getSubmodule().getParentKey().get()
+ + s.getSubmodule().project().get()
+ " but entry "
+ "doesn't have gitlink file mode.";
throw new SubmoduleException(errMsg);
@@ -576,7 +578,7 @@ public class SubmoduleOp {
// superproject is still subscribed to this branch. Re-read the ref to see if anything has
// changed since the last time the gitlink was updated, and roll that update into the same
// commit as all other submodule updates.
- Ref ref = subOr.repo.getRefDatabase().exactRef(s.getSubmodule().get());
+ Ref ref = subOr.repo.getRefDatabase().exactRef(s.getSubmodule().branch());
if (ref == null) {
ed.add(new DeletePath(s.getPath()));
return null;
@@ -615,7 +617,7 @@ public class SubmoduleOp {
msgbuf.append("* Update ");
msgbuf.append(s.getPath());
msgbuf.append(" from branch '");
- msgbuf.append(s.getSubmodule().getShortName());
+ msgbuf.append(s.getSubmodule().shortName());
msgbuf.append("'");
msgbuf.append("\n to ");
msgbuf.append(newCommit.getName());
@@ -675,8 +677,8 @@ public class SubmoduleOp {
addAllSubmoduleProjects(project, new LinkedHashSet<>(), projects);
}
- for (Branch.NameKey branch : updatedBranches) {
- projects.add(branch.getParentKey());
+ for (BranchNameKey branch : updatedBranches) {
+ projects.add(branch.project());
}
return ImmutableSet.copyOf(projects);
}
@@ -697,10 +699,10 @@ public class SubmoduleOp {
current.add(project);
Set<Project.NameKey> subprojects = new HashSet<>();
- for (Branch.NameKey branch : branchesByProject.get(project)) {
+ for (BranchNameKey branch : branchesByProject.get(project)) {
Collection<SubmoduleSubscription> subscriptions = targets.get(branch);
for (SubmoduleSubscription s : subscriptions) {
- subprojects.add(s.getSubmodule().getParentKey());
+ subprojects.add(s.getSubmodule().project());
}
}
@@ -712,8 +714,8 @@ public class SubmoduleOp {
projects.add(project);
}
- ImmutableSet<Branch.NameKey> getBranchesInOrder() {
- LinkedHashSet<Branch.NameKey> branches = new LinkedHashSet<>();
+ ImmutableSet<BranchNameKey> getBranchesInOrder() {
+ LinkedHashSet<BranchNameKey> branches = new LinkedHashSet<>();
if (sortedBranches != null) {
branches.addAll(sortedBranches);
}
@@ -721,15 +723,15 @@ public class SubmoduleOp {
return ImmutableSet.copyOf(branches);
}
- boolean hasSubscription(Branch.NameKey branch) {
+ boolean hasSubscription(BranchNameKey branch) {
return targets.containsKey(branch);
}
- void addBranchTip(Branch.NameKey branch, CodeReviewCommit tip) {
+ void addBranchTip(BranchNameKey branch, CodeReviewCommit tip) {
branchTips.put(branch, tip);
}
- void addOp(BatchUpdate bu, Branch.NameKey branch) {
+ void addOp(BatchUpdate bu, BranchNameKey branch) {
bu.addRepoOnlyOp(new GitlinkOp(branch));
}
}
diff --git a/java/com/google/gerrit/server/submit/TestHelperOp.java b/java/com/google/gerrit/server/submit/TestHelperOp.java
index bbb198ad9a..7763e2f811 100644
--- a/java/com/google/gerrit/server/submit/TestHelperOp.java
+++ b/java/com/google/gerrit/server/submit/TestHelperOp.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.submit;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.change.TestSubmitInput;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.RepoContext;
diff --git a/java/com/google/gerrit/server/update/BatchUpdate.java b/java/com/google/gerrit/server/update/BatchUpdate.java
index 0ca9f5e60f..ce16706b45 100644
--- a/java/com/google/gerrit/server/update/BatchUpdate.java
+++ b/java/com/google/gerrit/server/update/BatchUpdate.java
@@ -32,14 +32,15 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountState;
@@ -52,6 +53,7 @@ import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
+import com.google.gerrit.server.notedb.TooManyUpdatesException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
@@ -110,7 +112,6 @@ public class BatchUpdate implements AutoCloseable {
};
}
- // TODO(dborowitz): Make this package-private to force all callers to use RetryHelper.
public interface Factory {
BatchUpdate create(Project.NameKey project, CurrentUser user, Timestamp when);
}
@@ -178,23 +179,27 @@ public class BatchUpdate implements AutoCloseable {
}
private static void wrapAndThrowException(Exception e) throws UpdateException, RestApiException {
- Throwables.throwIfUnchecked(e);
-
- // Propagate REST API exceptions thrown by operations; they commonly throw exceptions like
- // ResourceConflictException to indicate an atomic update failure.
- Throwables.throwIfInstanceOf(e, UpdateException.class);
- Throwables.throwIfInstanceOf(e, RestApiException.class);
-
- // Convert other common non-REST exception types with user-visible messages to corresponding
- // REST exception types
- if (e instanceof InvalidChangeOperationException) {
+ // Convert common non-REST exception types with user-visible messages to corresponding REST
+ // exception types.
+ if (e instanceof InvalidChangeOperationException || e instanceof TooManyUpdatesException) {
throw new ResourceConflictException(e.getMessage(), e);
} else if (e instanceof NoSuchChangeException
|| e instanceof NoSuchRefException
|| e instanceof NoSuchProjectException) {
throw new ResourceNotFoundException(e.getMessage(), e);
+ } else if (e instanceof CommentsRejectedException) {
+ // SC_BAD_REQUEST is not ideal because it's not a syntactic error, but there is no better
+ // status code and it's isolated in monitoring.
+ throw new BadRequestException(e.getMessage(), e);
}
+ Throwables.throwIfUnchecked(e);
+
+ // Propagate REST API exceptions thrown by operations; they commonly throw exceptions like
+ // ResourceConflictException to indicate an atomic update failure.
+ Throwables.throwIfInstanceOf(e, UpdateException.class);
+ Throwables.throwIfInstanceOf(e, RestApiException.class);
+
// Otherwise, wrap in a generic UpdateException, which does not include a user-visible message.
throw new UpdateException(e);
}
@@ -289,7 +294,7 @@ public class BatchUpdate implements AutoCloseable {
private enum ChangeResult {
SKIPPED,
UPSERTED,
- DELETED;
+ DELETED
}
private final GitRepositoryManager repoManager;
@@ -566,9 +571,7 @@ public class BatchUpdate implements AutoCloseable {
handle.setResult(id, ChangeResult.SKIPPED);
continue;
}
- for (ChangeUpdate u : ctx.updates.values()) {
- handle.manager.add(u);
- }
+ ctx.updates.values().forEach(handle.manager::add);
if (ctx.deleted) {
logDebug("Change %s was deleted", id);
handle.manager.deleteChange(id);
diff --git a/java/com/google/gerrit/server/update/BatchUpdateOp.java b/java/com/google/gerrit/server/update/BatchUpdateOp.java
index 87a43a354c..a2c2394498 100644
--- a/java/com/google/gerrit/server/update/BatchUpdateOp.java
+++ b/java/com/google/gerrit/server/update/BatchUpdateOp.java
@@ -19,7 +19,7 @@ package com.google.gerrit.server.update;
*
* <p>Each operation has {@link #updateChange(ChangeContext)} called once the change is read in a
* transaction. Ops are associated with updates via {@link
- * BatchUpdate#addOp(com.google.gerrit.reviewdb.client.Change.Id, BatchUpdateOp)}.
+ * BatchUpdate#addOp(com.google.gerrit.entities.Change.Id, BatchUpdateOp)}.
*
* <p>Usually, a single {@code BatchUpdateOp} instance is only associated with a single change, i.e.
* {@code addOp} is only called once with that instance. Additionally, each method in {@code
diff --git a/java/com/google/gerrit/server/update/ChangeContext.java b/java/com/google/gerrit/server/update/ChangeContext.java
index 28674fcbe6..bd6d90bdc3 100644
--- a/java/com/google/gerrit/server/update/ChangeContext.java
+++ b/java/com/google/gerrit/server/update/ChangeContext.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.update;
import static java.util.Objects.requireNonNull;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
diff --git a/java/com/google/gerrit/server/update/CommentsRejectedException.java b/java/com/google/gerrit/server/update/CommentsRejectedException.java
new file mode 100644
index 0000000000..6b0c04d77a
--- /dev/null
+++ b/java/com/google/gerrit/server/update/CommentsRejectedException.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.update;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.validators.CommentValidationFailure;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+/** Thrown when comment validation rejected a comment, preventing it from being published. */
+public class CommentsRejectedException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ private final ImmutableList<CommentValidationFailure> commentValidationFailures;
+
+ public CommentsRejectedException(Collection<CommentValidationFailure> commentValidationFailures) {
+ this.commentValidationFailures = ImmutableList.copyOf(commentValidationFailures);
+ }
+
+ @Override
+ public String getMessage() {
+ return "One or more comments were rejected in validation: "
+ + commentValidationFailures.stream()
+ .map(CommentValidationFailure::getMessage)
+ .collect(Collectors.joining("; "));
+ }
+
+ /**
+ * Returns the validation failures that caused this exception. By contract this list is never
+ * empty.
+ */
+ public ImmutableList<CommentValidationFailure> getCommentValidationFailures() {
+ return commentValidationFailures;
+ }
+}
diff --git a/java/com/google/gerrit/server/update/Context.java b/java/com/google/gerrit/server/update/Context.java
index 8704cf03fe..99471686ce 100644
--- a/java/com/google/gerrit/server/update/Context.java
+++ b/java/com/google/gerrit/server/update/Context.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.update;
import static java.util.Objects.requireNonNull;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountState;
@@ -114,7 +114,7 @@ public interface Context {
/**
* Get the account of the user performing the update.
*
- * <p>Convenience method for {@code getIdentifiedUser().getAccount()}.
+ * <p>Convenience method for {@code getIdentifiedUser().account()}.
*
* @see CurrentUser#asIdentifiedUser()
* @return account.
diff --git a/java/com/google/gerrit/server/update/InsertChangeOp.java b/java/com/google/gerrit/server/update/InsertChangeOp.java
index 70600590fb..2676494cf4 100644
--- a/java/com/google/gerrit/server/update/InsertChangeOp.java
+++ b/java/com/google/gerrit/server/update/InsertChangeOp.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.update;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import java.io.IOException;
/**
diff --git a/java/com/google/gerrit/server/update/RepoView.java b/java/com/google/gerrit/server/update/RepoView.java
index 73dd12f35e..52467a4437 100644
--- a/java/com/google/gerrit/server/update/RepoView.java
+++ b/java/com/google/gerrit/server/update/RepoView.java
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toMap;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import java.io.IOException;
import java.util.Map;
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index ae8ba53725..bea38677a8 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -28,7 +28,6 @@ import com.github.rholder.retry.WaitStrategies;
import com.github.rholder.retry.WaitStrategy;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
@@ -36,18 +35,25 @@ import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.metrics.Counter1;
+import com.google.gerrit.metrics.Counter2;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
-import com.google.gerrit.metrics.Histogram1;
import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.ExceptionHook;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.RequestId;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.time.Duration;
import java.util.Arrays;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
+import java.util.function.Predicate;
import org.eclipse.jgit.lib.Config;
@Singleton
@@ -94,12 +100,24 @@ public class RetryHelper {
@Nullable
abstract Duration timeout();
+ abstract Optional<Class<?>> caller();
+
+ abstract Optional<Predicate<Throwable>> retryWithTrace();
+
+ abstract Optional<Consumer<String>> onAutoTrace();
+
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder listener(RetryListener listener);
public abstract Builder timeout(Duration timeout);
+ public abstract Builder caller(Class<?> caller);
+
+ public abstract Builder retryWithTrace(Predicate<Throwable> exceptionPredicate);
+
+ public abstract Builder onAutoTrace(Consumer<String> traceIdConsumer);
+
public abstract Options build();
}
}
@@ -107,21 +125,24 @@ public class RetryHelper {
@VisibleForTesting
@Singleton
public static class Metrics {
- final Histogram1<ActionType> attemptCounts;
+ final Counter1<ActionType> attemptCounts;
final Counter1<ActionType> timeoutCount;
+ final Counter2<ActionType, String> autoRetryCount;
+ final Counter2<ActionType, String> failuresOnAutoRetryCount;
@Inject
Metrics(MetricMaker metricMaker) {
- Field<ActionType> view = Field.ofEnum(ActionType.class, "action_type");
+ Field<ActionType> actionTypeField =
+ Field.ofEnum(ActionType.class, "action_type", Metadata.Builder::actionType).build();
attemptCounts =
- metricMaker.newHistogram(
- "action/retry_attempt_counts",
+ metricMaker.newCounter(
+ "action/retry_attempt_count",
new Description(
- "Distribution of number of attempts made by RetryHelper to execute an action"
- + " (1 == single attempt, no retry)")
+ "Number of retry attempts made by RetryHelper to execute an action"
+ + " (0 == single attempt, no retry)")
.setCumulative()
.setUnit("attempts"),
- view);
+ actionTypeField);
timeoutCount =
metricMaker.newCounter(
"action/retry_timeout_count",
@@ -129,7 +150,27 @@ public class RetryHelper {
"Number of action executions of RetryHelper that ultimately timed out")
.setCumulative()
.setUnit("timeouts"),
- view);
+ actionTypeField);
+ autoRetryCount =
+ metricMaker.newCounter(
+ "action/auto_retry_count",
+ new Description("Number of automatic retries with tracing")
+ .setCumulative()
+ .setUnit("retries"),
+ actionTypeField,
+ Field.ofString("operation_name", Metadata.Builder::operationName)
+ .description("The name of the operation that was retried.")
+ .build());
+ failuresOnAutoRetryCount =
+ metricMaker.newCounter(
+ "action/failures_on_auto_retry_count",
+ new Description("Number of failures on auto retry")
+ .setCumulative()
+ .setUnit("failures"),
+ actionTypeField,
+ Field.ofString("operation_name", Metadata.Builder::operationName)
+ .description("The name of the operation that was retried.")
+ .build());
}
}
@@ -143,13 +184,19 @@ public class RetryHelper {
private final Metrics metrics;
private final BatchUpdate.Factory updateFactory;
+ private final PluginSetContext<ExceptionHook> exceptionHooks;
private final Map<ActionType, Duration> defaultTimeouts;
private final WaitStrategy waitStrategy;
@Nullable private final Consumer<RetryerBuilder<?>> overwriteDefaultRetryerStrategySetup;
+ private final boolean retryWithTraceOnFailure;
@Inject
- RetryHelper(@GerritServerConfig Config cfg, Metrics metrics, BatchUpdate.Factory updateFactory) {
- this(cfg, metrics, updateFactory, null);
+ RetryHelper(
+ @GerritServerConfig Config cfg,
+ Metrics metrics,
+ PluginSetContext<ExceptionHook> exceptionHooks,
+ BatchUpdate.Factory updateFactory) {
+ this(cfg, metrics, updateFactory, exceptionHooks, null);
}
@VisibleForTesting
@@ -157,9 +204,11 @@ public class RetryHelper {
@GerritServerConfig Config cfg,
Metrics metrics,
BatchUpdate.Factory updateFactory,
+ PluginSetContext<ExceptionHook> exceptionHooks,
@Nullable Consumer<RetryerBuilder<?>> overwriteDefaultRetryerStrategySetup) {
this.metrics = metrics;
this.updateFactory = updateFactory;
+ this.exceptionHooks = exceptionHooks;
Duration defaultTimeout =
Duration.ofMillis(
@@ -185,6 +234,7 @@ public class RetryHelper {
MILLISECONDS),
WaitStrategies.randomWait(50, MILLISECONDS));
this.overwriteDefaultRetryerStrategySetup = overwriteDefaultRetryerStrategySetup;
+ this.retryWithTraceOnFailure = cfg.getBoolean("retry", "retryWithTraceOnFailure", false);
}
public Duration getDefaultTimeout(ActionType actionType) {
@@ -255,15 +305,57 @@ public class RetryHelper {
Predicate<Throwable> exceptionPredicate)
throws Throwable {
MetricListener listener = new MetricListener();
- try {
- RetryerBuilder<T> retryerBuilder = createRetryerBuilder(actionType, opts, exceptionPredicate);
+ try (TraceContext traceContext = TraceContext.open()) {
+ RetryerBuilder<T> retryerBuilder =
+ createRetryerBuilder(
+ actionType,
+ opts,
+ t -> {
+ // exceptionPredicate checks for temporary errors for which the operation should be
+ // retried (e.g. LockFailure). The retry has good chances to succeed.
+ if (exceptionPredicate.test(t)) {
+ return true;
+ }
+
+ // Exception hooks may identify additional exceptions for retry.
+ if (exceptionHooks.stream().anyMatch(h -> h.shouldRetry(t))) {
+ return true;
+ }
+
+ // A non-recoverable failure occurred. Check if we should retry to capture a trace
+ // of the failure. If a trace was already done there is no need to retry.
+ if (retryWithTraceOnFailure
+ && opts.retryWithTrace().isPresent()
+ && opts.retryWithTrace().get().test(t)) {
+ String caller = opts.caller().map(Class::getSimpleName).orElse("N/A");
+ if (!traceContext.isTracing()) {
+ String traceId = "retry-on-failure-" + new RequestId();
+ traceContext.addTag(RequestId.Type.TRACE_ID, traceId).forceLogging();
+ opts.onAutoTrace().ifPresent(c -> c.accept(traceId));
+ logger.atFine().withCause(t).log(
+ "AutoRetry: %s failed, retry with tracing enabled", caller);
+ metrics.autoRetryCount.increment(actionType, caller);
+ return true;
+ }
+
+ // A non-recoverable failure occurred. We retried the operation with tracing
+ // enabled and it failed again. Log the failure so that admin can see if it
+ // differs from the failure that triggered the retry.
+ logger.atFine().withCause(t).log(
+ "AutoRetry: auto-retry of %s has failed", caller);
+ metrics.failuresOnAutoRetryCount.increment(actionType, caller);
+ return false;
+ }
+
+ return false;
+ });
retryerBuilder.withRetryListener(listener);
return executeWithTimeoutCount(actionType, action, retryerBuilder.build());
} finally {
if (listener.getAttemptCount() > 1) {
logger.atFine().log("%s was attempted %d times", actionType, listener.getAttemptCount());
+ metrics.attemptCounts.incrementBy(actionType, listener.getAttemptCount() - 1);
}
- metrics.attemptCounts.record(actionType, listener.getAttemptCount());
}
}
@@ -295,7 +387,7 @@ public class RetryHelper {
private <O> RetryerBuilder<O> createRetryerBuilder(
ActionType actionType, Options opts, Predicate<Throwable> exceptionPredicate) {
RetryerBuilder<O> retryerBuilder =
- RetryerBuilder.<O>newBuilder().retryIfException(exceptionPredicate);
+ RetryerBuilder.<O>newBuilder().retryIfException(exceptionPredicate::test);
if (opts.listener() != null) {
retryerBuilder.withRetryListener(opts.listener());
}
diff --git a/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java b/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
index cd4df4593c..bce1209bb9 100644
--- a/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
+++ b/java/com/google/gerrit/server/update/RetryingRestCollectionModifyView.java
@@ -14,11 +14,15 @@
package com.google.gerrit.server.update;
+import com.google.common.base.Throwables;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
import com.google.gerrit.extensions.restapi.RestResource;
+import java.util.concurrent.atomic.AtomicReference;
public abstract class RetryingRestCollectionModifyView<
P extends RestResource, C extends RestResource, I, O>
@@ -30,11 +34,25 @@ public abstract class RetryingRestCollectionModifyView<
}
@Override
- public final O apply(P parentResource, I input)
+ public final Response<O> apply(P parentResource, I input)
throws AuthException, BadRequestException, ResourceConflictException, Exception {
- return retryHelper.execute(updateFactory -> applyImpl(updateFactory, parentResource, input));
+ AtomicReference<String> traceId = new AtomicReference<>(null);
+ try {
+ RetryHelper.Options retryOptions =
+ RetryHelper.options()
+ .caller(getClass())
+ .retryWithTrace(t -> !(t instanceof RestApiException))
+ .onAutoTrace(traceId::set)
+ .build();
+ return retryHelper
+ .execute(updateFactory -> applyImpl(updateFactory, parentResource, input), retryOptions)
+ .traceId(traceId.get());
+ } catch (Exception e) {
+ Throwables.throwIfInstanceOf(e, RestApiException.class);
+ return Response.<O>internalServerError(e).traceId(traceId.get());
+ }
}
- protected abstract O applyImpl(BatchUpdate.Factory updateFactory, P parentResource, I input)
- throws Exception;
+ protected abstract Response<O> applyImpl(
+ BatchUpdate.Factory updateFactory, P parentResource, I input) throws Exception;
}
diff --git a/java/com/google/gerrit/server/update/RetryingRestModifyView.java b/java/com/google/gerrit/server/update/RetryingRestModifyView.java
index 1b0b1f4d0a..56c3eec095 100644
--- a/java/com/google/gerrit/server/update/RetryingRestModifyView.java
+++ b/java/com/google/gerrit/server/update/RetryingRestModifyView.java
@@ -14,8 +14,12 @@
package com.google.gerrit.server.update;
+import com.google.common.base.Throwables;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestResource;
+import java.util.concurrent.atomic.AtomicReference;
public abstract class RetryingRestModifyView<R extends RestResource, I, O>
implements RestModifyView<R, I> {
@@ -26,10 +30,24 @@ public abstract class RetryingRestModifyView<R extends RestResource, I, O>
}
@Override
- public final O apply(R resource, I input) throws Exception {
- return retryHelper.execute(updateFactory -> applyImpl(updateFactory, resource, input));
+ public final Response<O> apply(R resource, I input) throws RestApiException {
+ AtomicReference<String> traceId = new AtomicReference<>(null);
+ try {
+ RetryHelper.Options retryOptions =
+ RetryHelper.options()
+ .caller(getClass())
+ .retryWithTrace(t -> !(t instanceof RestApiException))
+ .onAutoTrace(traceId::set)
+ .build();
+ return retryHelper
+ .execute(updateFactory -> applyImpl(updateFactory, resource, input), retryOptions)
+ .traceId(traceId.get());
+ } catch (Exception e) {
+ Throwables.throwIfInstanceOf(e, RestApiException.class);
+ return Response.<O>internalServerError(e).traceId(traceId.get());
+ }
}
- protected abstract O applyImpl(BatchUpdate.Factory updateFactory, R resource, I input)
+ protected abstract Response<O> applyImpl(BatchUpdate.Factory updateFactory, R resource, I input)
throws Exception;
}
diff --git a/java/com/google/gerrit/server/util/CommitMessageUtil.java b/java/com/google/gerrit/server/util/CommitMessageUtil.java
index 4ad226b7fd..1c8ce0c8ad 100644
--- a/java/com/google/gerrit/server/util/CommitMessageUtil.java
+++ b/java/com/google/gerrit/server/util/CommitMessageUtil.java
@@ -18,8 +18,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.reviewdb.client.Change;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.eclipse.jgit.lib.Constants;
@@ -34,7 +34,7 @@ public class CommitMessageUtil {
try {
rng = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("Cannot create RNG for Change-Id generator", e);
+ throw new IllegalStateException("Cannot create RNG for Change-Id generator", e);
}
}
@@ -71,6 +71,6 @@ public class CommitMessageUtil {
}
public static Change.Key generateKey() {
- return new Change.Key("I" + generateChangeId().name());
+ return Change.key("I" + generateChangeId().name());
}
}
diff --git a/java/com/google/gerrit/server/util/IdGenerator.java b/java/com/google/gerrit/server/util/IdGenerator.java
index 276df06d66..d4c2dc4855 100644
--- a/java/com/google/gerrit/server/util/IdGenerator.java
+++ b/java/com/google/gerrit/server/util/IdGenerator.java
@@ -45,8 +45,8 @@ public class IdGenerator {
public static int mix(int salt, int in) {
short v0 = hi16(in);
short v1 = lo16(in);
- v0 += ((v1 << 2) + 0 ^ v1) + (salt ^ (v1 >>> 3)) + 1;
- v1 += ((v0 << 2) + 2 ^ v0) + (salt ^ (v0 >>> 3)) + 3;
+ v0 += (short) (((v1 << 2) + 0 ^ v1) + (salt ^ (v1 >>> 3)) + 1);
+ v1 += (short) (((v0 << 2) + 2 ^ v0) + (salt ^ (v0 >>> 3)) + 3);
return result(v0, v1);
}
@@ -54,8 +54,8 @@ public class IdGenerator {
static int unmix(int in) {
short v0 = hi16(in);
short v1 = lo16(in);
- v1 -= ((v0 << 2) + 2 ^ v0) + (salt ^ (v0 >>> 3)) + 3;
- v0 -= ((v1 << 2) + 0 ^ v1) + (salt ^ (v1 >>> 3)) + 1;
+ v1 -= (short) (((v0 << 2) + 2 ^ v0) + (salt ^ (v0 >>> 3)) + 3);
+ v0 -= (short) (((v1 << 2) + 0 ^ v1) + (salt ^ (v1 >>> 3)) + 1);
return result(v0, v1);
}
diff --git a/java/com/google/gerrit/server/util/MagicBranch.java b/java/com/google/gerrit/server/util/MagicBranch.java
index 4e41be0e01..924c2887c6 100644
--- a/java/com/google/gerrit/server/util/MagicBranch.java
+++ b/java/com/google/gerrit/server/util/MagicBranch.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.util;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.Capable;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import java.io.IOException;
import java.util.List;
import org.eclipse.jgit.lib.Ref;
@@ -26,28 +26,19 @@ public final class MagicBranch {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static final String NEW_CHANGE = "refs/for/";
- // TODO(xchangcheng): remove after 'repo' supports private/wip changes.
- public static final String NEW_DRAFT_CHANGE = "refs/drafts/";
/** Extracts the destination from a ref name */
public static String getDestBranchName(String refName) {
- String magicBranch = NEW_CHANGE;
- if (refName.startsWith(NEW_DRAFT_CHANGE)) {
- magicBranch = NEW_DRAFT_CHANGE;
- }
- return refName.substring(magicBranch.length());
+ return refName.substring(NEW_CHANGE.length());
}
/** Checks if the supplied ref name is a magic branch */
public static boolean isMagicBranch(String refName) {
- return refName.startsWith(NEW_DRAFT_CHANGE) || refName.startsWith(NEW_CHANGE);
+ return refName.startsWith(NEW_CHANGE);
}
/** Returns the ref name prefix for a magic branch, {@code null} if the branch is not magic */
public static String getMagicRefNamePrefix(String refName) {
- if (refName.startsWith(NEW_DRAFT_CHANGE)) {
- return NEW_DRAFT_CHANGE;
- }
if (refName.startsWith(NEW_CHANGE)) {
return NEW_CHANGE;
}
@@ -63,15 +54,7 @@ public final class MagicBranch {
* branch.
*/
public static Capable checkMagicBranchRefs(Repository repo, Project project) {
- Capable result = checkMagicBranchRef(NEW_CHANGE, repo, project);
- if (result != Capable.OK) {
- return result;
- }
- result = checkMagicBranchRef(NEW_DRAFT_CHANGE, repo, project);
- if (result != Capable.OK) {
- return result;
- }
- return Capable.OK;
+ return checkMagicBranchRef(NEW_CHANGE, repo, project);
}
private static Capable checkMagicBranchRef(String branchName, Repository repo, Project project) {
diff --git a/java/com/google/gerrit/server/util/OneOffRequestContext.java b/java/com/google/gerrit/server/util/OneOffRequestContext.java
index 1788343bae..62683f0490 100644
--- a/java/com/google/gerrit/server/util/OneOffRequestContext.java
+++ b/java/com/google/gerrit/server/util/OneOffRequestContext.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.util;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
import com.google.inject.Inject;
diff --git a/java/com/google/gerrit/server/util/ReplicaUtil.java b/java/com/google/gerrit/server/util/ReplicaUtil.java
new file mode 100644
index 0000000000..bf6111a7e3
--- /dev/null
+++ b/java/com/google/gerrit/server/util/ReplicaUtil.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.util;
+
+import org.eclipse.jgit.lib.Config;
+
+public class ReplicaUtil {
+ /** Provides backward compatibility for container.slave property. */
+ public static boolean isReplica(Config cfg) {
+ return cfg.getBoolean("container", "slave", false)
+ || cfg.getBoolean("container", "replica", false);
+ }
+}
diff --git a/java/com/google/gerrit/server/util/RequestScopePropagator.java b/java/com/google/gerrit/server/util/RequestScopePropagator.java
index 789b9b16a7..dc8a13681d 100644
--- a/java/com/google/gerrit/server/util/RequestScopePropagator.java
+++ b/java/com/google/gerrit/server/util/RequestScopePropagator.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.util;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Throwables;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.git.ProjectRunnable;
import com.google.inject.Key;
diff --git a/java/com/google/gerrit/server/util/git/BUILD b/java/com/google/gerrit/server/util/git/BUILD
index a8ae918829..4f4ba83d7a 100644
--- a/java/com/google/gerrit/server/util/git/BUILD
+++ b/java/com/google/gerrit/server/util/git/BUILD
@@ -5,7 +5,7 @@ java_library(
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/reviewdb:server",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//java/com/google/gerrit/entities",
+ "//lib:jgit",
],
)
diff --git a/java/com/google/gerrit/server/util/git/DelegateSystemReader.java b/java/com/google/gerrit/server/util/git/DelegateSystemReader.java
new file mode 100644
index 0000000000..279bb95f04
--- /dev/null
+++ b/java/com/google/gerrit/server/util/git/DelegateSystemReader.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.util.git;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
+
+public class DelegateSystemReader extends SystemReader {
+ private final SystemReader delegate;
+
+ public DelegateSystemReader(SystemReader delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public String getHostname() {
+ return delegate.getHostname();
+ }
+
+ @Override
+ public String getenv(String variable) {
+ return delegate.getenv(variable);
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return delegate.getProperty(key);
+ }
+
+ @Override
+ public FileBasedConfig openUserConfig(Config parent, FS fs) {
+ return delegate.openUserConfig(parent, fs);
+ }
+
+ @Override
+ public FileBasedConfig openSystemConfig(Config parent, FS fs) {
+ return delegate.openSystemConfig(parent, fs);
+ }
+
+ @Override
+ public FileBasedConfig openJGitConfig(Config parent, FS fs) {
+ return delegate.openJGitConfig(parent, fs);
+ }
+
+ @Override
+ public long getCurrentTime() {
+ return delegate.getCurrentTime();
+ }
+
+ @Override
+ public int getTimezone(long when) {
+ return delegate.getTimezone(when);
+ }
+}
diff --git a/java/com/google/gerrit/server/util/git/SubmoduleSectionParser.java b/java/com/google/gerrit/server/util/git/SubmoduleSectionParser.java
index f05d1d774b..97132a32ae 100644
--- a/java/com/google/gerrit/server/util/git/SubmoduleSectionParser.java
+++ b/java/com/google/gerrit/server/util/git/SubmoduleSectionParser.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.util.git;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.SubmoduleSubscription;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
@@ -45,10 +45,10 @@ public class SubmoduleSectionParser {
private final Config config;
private final String canonicalWebUrl;
- private final Branch.NameKey superProjectBranch;
+ private final BranchNameKey superProjectBranch;
public SubmoduleSectionParser(
- Config config, String canonicalWebUrl, Branch.NameKey superProjectBranch) {
+ Config config, String canonicalWebUrl, BranchNameKey superProjectBranch) {
this.config = config;
this.canonicalWebUrl = canonicalWebUrl;
this.superProjectBranch = superProjectBranch;
@@ -81,13 +81,13 @@ public class SubmoduleSectionParser {
String project;
if (branch.equals(".")) {
- branch = superProjectBranch.get();
+ branch = superProjectBranch.branch();
}
// relative URL
if (url.startsWith("../")) {
// prefix with a slash for easier relative path walks
- project = '/' + superProjectBranch.getParentKey().get();
+ project = '/' + superProjectBranch.project().get();
String hostPart = url;
while (hostPart.startsWith("../")) {
int lastSlash = project.lastIndexOf('/');
@@ -133,9 +133,9 @@ public class SubmoduleSectionParser {
0, //
project.length() - Constants.DOT_GIT_EXT.length());
}
- Project.NameKey projectKey = new Project.NameKey(project);
+ Project.NameKey projectKey = Project.nameKey(project);
return new SubmoduleSubscription(
- superProjectBranch, new Branch.NameKey(projectKey, branch), path);
+ superProjectBranch, BranchNameKey.create(projectKey, branch), path);
}
} catch (URISyntaxException e) {
// Error in url syntax (in fact it is uri syntax)
diff --git a/java/com/google/gerrit/server/util/time/BUILD b/java/com/google/gerrit/server/util/time/BUILD
index ea39efe455..b1126f07a2 100644
--- a/java/com/google/gerrit/server/util/time/BUILD
+++ b/java/com/google/gerrit/server/util/time/BUILD
@@ -5,7 +5,9 @@ java_library(
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
deps = [
+ "//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/server/util/git",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
],
)
diff --git a/java/com/google/gerrit/server/util/time/TimeUtil.java b/java/com/google/gerrit/server/util/time/TimeUtil.java
index 645dbb92f8..639d0a6589 100644
--- a/java/com/google/gerrit/server/util/time/TimeUtil.java
+++ b/java/com/google/gerrit/server/util/time/TimeUtil.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.util.time;
import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.common.UsedAt.Project;
+import com.google.gerrit.server.util.git.DelegateSystemReader;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.function.LongSupplier;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
/** Static utility methods for dealing with dates and times. */
@@ -43,6 +43,17 @@ public class TimeUtil {
return new Timestamp(nowMs());
}
+ /**
+ * Returns the magic timestamp representing no specific time.
+ *
+ * <p>This "null object" is helpful in contexts where using {@code null} directly is not possible.
+ */
+ @UsedAt(Project.PLUGIN_CHECKS)
+ public static Timestamp never() {
+ // Always create a new object as timestamps are mutable.
+ return new Timestamp(0);
+ }
+
public static Timestamp truncateToSecond(Timestamp t) {
return new Timestamp((t.getTime() / 1000) * 1000);
}
@@ -63,47 +74,15 @@ public class TimeUtil {
SystemReader.setInstance(null);
}
- private static class GerritSystemReader extends SystemReader {
- SystemReader delegate;
-
- GerritSystemReader(SystemReader delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public String getHostname() {
- return delegate.getHostname();
- }
-
- @Override
- public String getenv(String variable) {
- return delegate.getenv(variable);
- }
-
- @Override
- public String getProperty(String key) {
- return delegate.getProperty(key);
- }
-
- @Override
- public FileBasedConfig openUserConfig(Config parent, FS fs) {
- return delegate.openUserConfig(parent, fs);
- }
-
- @Override
- public FileBasedConfig openSystemConfig(Config parent, FS fs) {
- return delegate.openSystemConfig(parent, fs);
+ static class GerritSystemReader extends DelegateSystemReader {
+ GerritSystemReader(SystemReader reader) {
+ super(reader);
}
@Override
public long getCurrentTime() {
return currentMillisSupplier.getAsLong();
}
-
- @Override
- public int getTimezone(long when) {
- return delegate.getTimezone(when);
- }
}
private TimeUtil() {}
diff --git a/java/com/google/gerrit/server/validators/AccountActivationValidationListener.java b/java/com/google/gerrit/server/validators/AccountActivationValidationListener.java
index bc52308609..9fdc9e6d71 100644
--- a/java/com/google/gerrit/server/validators/AccountActivationValidationListener.java
+++ b/java/com/google/gerrit/server/validators/AccountActivationValidationListener.java
@@ -26,6 +26,9 @@ public interface AccountActivationValidationListener {
/**
* Called when an account should be activated to allow validation of the account activation.
*
+ * <p>See {@link com.google.gerrit.extensions.events.AccountActivationListener} for a listener
+ * that's run after the account got activated.
+ *
* @param account the account that should be activated
* @throws ValidationException if validation fails
*/
@@ -34,6 +37,9 @@ public interface AccountActivationValidationListener {
/**
* Called when an account should be deactivated to allow validation of the account deactivation.
*
+ * <p>See {@link com.google.gerrit.extensions.events.AccountActivationListener} for a listener
+ * that's run after the account got deactivated.
+ *
* @param account the account that should be deactivated
* @throws ValidationException if validation fails
*/
diff --git a/java/com/google/gerrit/server/validators/AssigneeValidationListener.java b/java/com/google/gerrit/server/validators/AssigneeValidationListener.java
index a97ce0b88f..514125f0cd 100644
--- a/java/com/google/gerrit/server/validators/AssigneeValidationListener.java
+++ b/java/com/google/gerrit/server/validators/AssigneeValidationListener.java
@@ -14,9 +14,9 @@
package com.google.gerrit.server.validators;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
/** Listener to provide validation of assignees. */
@ExtensionPoint
diff --git a/java/com/google/gerrit/server/validators/HashtagValidationListener.java b/java/com/google/gerrit/server/validators/HashtagValidationListener.java
index fbf8e76db0..5ea6bfa53d 100644
--- a/java/com/google/gerrit/server/validators/HashtagValidationListener.java
+++ b/java/com/google/gerrit/server/validators/HashtagValidationListener.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.validators;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.client.Change;
import java.util.Set;
/** Listener to provide validation of hashtag changes. */
diff --git a/java/com/google/gerrit/sshd/AbstractGitCommand.java b/java/com/google/gerrit/sshd/AbstractGitCommand.java
index 90f0c4365d..9efcff2d4c 100644
--- a/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -14,7 +14,7 @@
package com.google.gerrit.sshd;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -24,11 +24,14 @@ import com.google.gerrit.sshd.SshScope.Context;
import com.google.inject.Inject;
import java.io.IOException;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository;
import org.kohsuke.args4j.Argument;
public abstract class AbstractGitCommand extends BaseCommand {
+ private static final String GIT_PROTOCOL = "GIT_PROTOCOL";
+
@Argument(index = 0, metaVar = "PROJECT.git", required = true, usage = "project name")
protected ProjectState projectState;
@@ -45,10 +48,16 @@ public abstract class AbstractGitCommand extends BaseCommand {
protected Repository repo;
protected Project.NameKey projectName;
protected Project project;
+ protected String[] extraParameters;
@Override
- public void start(Environment env) {
+ public void start(ChannelSession channel, Environment env) {
enableGracefulStop();
+ String gitProtocol = env.getEnv().get(GIT_PROTOCOL);
+ if (gitProtocol != null) {
+ extraParameters = gitProtocol.split(":");
+ }
+
Context ctx = context.subContext(newSession(), context.getCommandLine());
final Context old = sshScope.set(ctx);
try {
diff --git a/java/com/google/gerrit/sshd/AliasCommand.java b/java/com/google/gerrit/sshd/AliasCommand.java
index 567cf00735..bf0dd91097 100644
--- a/java/com/google/gerrit/sshd/AliasCommand.java
+++ b/java/com/google/gerrit/sshd/AliasCommand.java
@@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.command.Command;
/** Command that executes some other command. */
@@ -47,9 +48,9 @@ public class AliasCommand extends BaseCommand {
}
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
try {
- begin(env);
+ begin(channel, env);
} catch (Failure e) {
String msg = e.getMessage();
if (!msg.endsWith("\n")) {
@@ -61,7 +62,7 @@ public class AliasCommand extends BaseCommand {
}
}
- private void begin(Environment env) throws IOException, Failure {
+ private void begin(ChannelSession channel, Environment env) throws IOException, Failure {
Map<String, CommandProvider> map = root.getMap();
for (String name : chain(command)) {
CommandProvider p = map.get(name);
@@ -90,15 +91,15 @@ public class AliasCommand extends BaseCommand {
}
provideStateTo(cmd);
atomicCmd.set(cmd);
- cmd.start(env);
+ cmd.start(channel, env);
}
@Override
- public void destroy() {
+ public void destroy(ChannelSession channel) {
Command cmd = atomicCmd.getAndSet(null);
if (cmd != null) {
try {
- cmd.destroy();
+ cmd.destroy(channel);
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index f6f9b52935..689c567dd8 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -7,25 +7,27 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
+ "//java/com/google/gerrit/git",
"//java/com/google/gerrit/json",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/audit",
"//java/com/google/gerrit/server/git/receive",
"//java/com/google/gerrit/server/ioutil",
"//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/restapi",
- "//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/logging",
"//lib:args4j",
"//lib:gson",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-archive",
"//lib:jsch",
"//lib:servlet-api",
"//lib/auto:auto-value",
@@ -37,8 +39,6 @@ java_library(
"//lib/guice",
"//lib/guice:guice-assistedinject",
"//lib/guice:guice-servlet", # SSH should not depend on servlet
- "//lib/jgit/org.eclipse.jgit.archive:jgit-archive",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/log:log4j",
"//lib/mina:core",
"//lib/mina:sshd",
diff --git a/java/com/google/gerrit/sshd/BaseCommand.java b/java/com/google/gerrit/sshd/BaseCommand.java
index 1d9635f108..a027dd197d 100644
--- a/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/java/com/google/gerrit/sshd/BaseCommand.java
@@ -20,10 +20,10 @@ import com.google.common.base.Joiner;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.Atomics;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.DynamicOptions;
@@ -57,6 +57,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.common.SshException;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
+import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.command.Command;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
@@ -182,7 +183,7 @@ public abstract class BaseCommand implements Command {
}
@Override
- public void destroy() {
+ public void destroy(ChannelSession channel) {
Future<?> future = task.getAndSet(null);
if (future != null && !future.isDone()) {
future.cancel(true);
@@ -264,7 +265,8 @@ public abstract class BaseCommand implements Command {
/**
* Spawn a function into its own thread.
*
- * <p>Typically this should be invoked within {@link Command#start(Environment)}, such as:
+ * <p>Typically this should be invoked within {@link Command#start(ChannelSession, Environment)},
+ * such as:
*
* <pre>
* startThread(new CommandRunnable() {
diff --git a/java/com/google/gerrit/sshd/ChangeArgumentParser.java b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
index ed6f87feab..491bcb84de 100644
--- a/java/com/google/gerrit/sshd/ChangeArgumentParser.java
+++ b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
@@ -15,10 +15,10 @@
package com.google.gerrit.sshd;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -122,7 +122,7 @@ public class ChangeArgumentParser {
private List<Change.Id> parseId(String id) throws UnloggedFailure {
try {
- return Arrays.asList(new Change.Id(Integer.parseInt(id)));
+ return Arrays.asList(Change.id(Integer.parseInt(id)));
} catch (NumberFormatException e) {
throw new UnloggedFailure(2, "Invalid change ID " + id, e);
}
diff --git a/java/com/google/gerrit/sshd/ChannelIdTrackingUnknownChannelReferenceHandler.java b/java/com/google/gerrit/sshd/ChannelIdTrackingUnknownChannelReferenceHandler.java
new file mode 100644
index 0000000000..f8ab90e56e
--- /dev/null
+++ b/java/com/google/gerrit/sshd/ChannelIdTrackingUnknownChannelReferenceHandler.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * This file is based on sshd-contrib Apache SSHD Mina project. Original commit:
+ * https://github.com/apache/mina-sshd/commit/11b33dee37b5b9c71a40a8a98a42007e3687131e
+ */
+package com.google.gerrit.sshd;
+
+import com.google.common.flogger.FluentLogger;
+import java.io.IOException;
+import org.apache.sshd.common.AttributeRepository.AttributeKey;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.channel.ChannelListener;
+import org.apache.sshd.common.channel.exception.SshChannelNotFoundException;
+import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.helpers.DefaultUnknownChannelReferenceHandler;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Makes sure that the referenced &quot;unknown&quot; channel identifier is one that was assigned in
+ * the past. <B>Note:</B> it relies on the fact that the default {@code ConnectionService}
+ * implementation assigns channels identifiers in ascending order.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ChannelIdTrackingUnknownChannelReferenceHandler
+ extends DefaultUnknownChannelReferenceHandler implements ChannelListener {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ public static final AttributeKey<Integer> LAST_CHANNEL_ID_KEY = new AttributeKey<>();
+
+ public static final ChannelIdTrackingUnknownChannelReferenceHandler TRACKER =
+ new ChannelIdTrackingUnknownChannelReferenceHandler();
+
+ public ChannelIdTrackingUnknownChannelReferenceHandler() {
+ super();
+ }
+
+ @Override
+ public void channelInitialized(Channel channel) {
+ int channelId = channel.getId();
+ Session session = channel.getSession();
+ Integer lastTracked = session.setAttribute(LAST_CHANNEL_ID_KEY, channelId);
+ logger.atFine().log(
+ "channelInitialized(%s) updated last tracked channel ID %s => %s",
+ channel, lastTracked, channelId);
+ }
+
+ @Override
+ public Channel handleUnknownChannelCommand(
+ ConnectionService service, byte cmd, int channelId, Buffer buffer) throws IOException {
+ Session session = service.getSession();
+ Integer lastTracked = session.getAttribute(LAST_CHANNEL_ID_KEY);
+ if ((lastTracked != null) && (channelId <= lastTracked.intValue())) {
+ // Use TRACE level in order to avoid messages flooding
+ logger.atFinest().log(
+ "handleUnknownChannelCommand(%s) apply default handling for %s on channel=%s (lastTracked=%s)",
+ session, SshConstants.getCommandMessageName(cmd), channelId, lastTracked);
+ return super.handleUnknownChannelCommand(service, cmd, channelId, buffer);
+ }
+
+ throw new SshChannelNotFoundException(
+ channelId,
+ "Received "
+ + SshConstants.getCommandMessageName(cmd)
+ + " on unassigned channel "
+ + channelId
+ + " (last assigned="
+ + lastTracked
+ + ")");
+ }
+}
diff --git a/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
index c003b467ae..38ac26daf8 100644
--- a/java/com/google/gerrit/sshd/CommandFactoryProvider.java
+++ b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -40,6 +40,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.SessionAware;
+import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.command.CommandFactory;
import org.apache.sshd.server.session.ServerSession;
@@ -91,13 +92,13 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
@Override
public CommandFactory get() {
- return requestCommand -> {
- String c = requestCommand;
+ return (channelSession, requestCommand) -> {
+ String command = requestCommand;
SshCreateCommandInterceptor interceptor = createCommandInterceptor.get();
if (interceptor != null) {
- c = interceptor.intercept(c);
+ command = interceptor.intercept(command);
}
- return new Trampoline(c);
+ return new Trampoline(command);
};
}
@@ -148,7 +149,7 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
}
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
this.env = env;
final Context ctx = this.ctx;
task.set(
@@ -157,7 +158,7 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
@Override
public void run() {
try {
- onStart();
+ onStart(channel);
} catch (Exception e) {
logger.atWarning().withCause(e).log(
"Cannot start command \"%s\" for user %s",
@@ -172,7 +173,7 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
}));
}
- private void onStart() throws IOException {
+ private void onStart(ChannelSession channel) throws IOException {
synchronized (this) {
final Context old = sshScope.set(ctx);
try {
@@ -195,7 +196,7 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
log(rc);
}
});
- cmd.start(env);
+ cmd.start(channel, env);
} finally {
sshScope.set(old);
}
@@ -231,20 +232,20 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
}
@Override
- public void destroy() {
+ public void destroy(ChannelSession channel) {
Future<?> future = task.getAndSet(null);
if (future != null) {
future.cancel(true);
- destroyExecutor.execute(this::onDestroy);
+ destroyExecutor.execute(() -> onDestroy(channel));
}
}
- private void onDestroy() {
+ private void onDestroy(ChannelSession channel) {
synchronized (this) {
if (cmd != null) {
final Context old = sshScope.set(ctx);
try {
- cmd.destroy();
+ cmd.destroy(channel);
log(BaseCommand.STATUS_CANCEL);
} finally {
ctx = null;
diff --git a/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java b/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
index 1e32e1b246..6c0f3af040 100644
--- a/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
+++ b/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
@@ -31,6 +31,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
+import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.Collection;
@@ -80,19 +81,24 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
}
private static Set<PublicKey> myHostKeys(KeyPairProvider p) {
- final Set<PublicKey> keys = new HashSet<>(6);
- addPublicKey(keys, p, KeyPairProvider.SSH_ED25519);
- addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP256);
- addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP384);
- addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP521);
- addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
- addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+ Set<PublicKey> keys = new HashSet<>(6);
+ try {
+ addPublicKey(keys, p, KeyPairProvider.SSH_ED25519);
+ addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP256);
+ addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP384);
+ addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP521);
+ addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
+ addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+ } catch (IOException | GeneralSecurityException e) {
+ throw new IllegalStateException("Cannot load SSHD host key", e);
+ }
+
return keys;
}
- private static void addPublicKey(
- final Collection<PublicKey> out, KeyPairProvider p, String type) {
- final KeyPair pair = p.loadKey(type);
+ private static void addPublicKey(Collection<PublicKey> out, KeyPairProvider p, String type)
+ throws IOException, GeneralSecurityException {
+ KeyPair pair = p.loadKey(null, type);
if (pair != null && pair.getPublic() != null) {
out.add(pair.getPublic());
}
diff --git a/java/com/google/gerrit/sshd/DispatchCommand.java b/java/com/google/gerrit/sshd/DispatchCommand.java
index 68962db18b..7db65bd0b2 100644
--- a/java/com/google/gerrit/sshd/DispatchCommand.java
+++ b/java/com/google/gerrit/sshd/DispatchCommand.java
@@ -33,6 +33,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.command.Command;
import org.kohsuke.args4j.Argument;
@@ -69,7 +70,7 @@ final class DispatchCommand extends BaseCommand {
}
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
try {
parseCommandLine();
if (Strings.isNullOrEmpty(commandName)) {
@@ -115,7 +116,7 @@ final class DispatchCommand extends BaseCommand {
provideStateTo(cmd);
atomicCmd.set(cmd);
- cmd.start(env);
+ cmd.start(channel, env);
} catch (UnloggedFailure e) {
String msg = e.getMessage();
@@ -145,11 +146,11 @@ final class DispatchCommand extends BaseCommand {
}
@Override
- public void destroy() {
+ public void destroy(ChannelSession channel) {
Command cmd = atomicCmd.getAndSet(null);
if (cmd != null) {
try {
- cmd.destroy();
+ cmd.destroy(channel);
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
diff --git a/java/com/google/gerrit/sshd/GerritGSSAuthenticator.java b/java/com/google/gerrit/sshd/GerritGSSAuthenticator.java
index adb50852f6..67592751c1 100644
--- a/java/com/google/gerrit/sshd/GerritGSSAuthenticator.java
+++ b/java/com/google/gerrit/sshd/GerritGSSAuthenticator.java
@@ -14,7 +14,7 @@
package com.google.gerrit.sshd;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.IdentifiedUser.GenericFactory;
import com.google.gerrit.server.account.AccountCache;
@@ -66,7 +66,7 @@ class GerritGSSAuthenticator extends GSSAuthenticator {
}
Optional<Account> account =
- accounts.getByUsername(username).map(AccountState::getAccount).filter(Account::isActive);
+ accounts.getByUsername(username).map(AccountState::account).filter(Account::isActive);
if (!account.isPresent()) {
return false;
}
@@ -77,6 +77,6 @@ class GerritGSSAuthenticator extends GSSAuthenticator {
sshScope,
sshLog,
sd,
- SshUtil.createUser(sd, userFactory, account.get().getId()));
+ SshUtil.createUser(sd, userFactory, account.get().id()));
}
}
diff --git a/java/com/google/gerrit/sshd/HostKeyProvider.java b/java/com/google/gerrit/sshd/HostKeyProvider.java
index bffcfcd7b9..3578fb9107 100644
--- a/java/com/google/gerrit/sshd/HostKeyProvider.java
+++ b/java/com/google/gerrit/sshd/HostKeyProvider.java
@@ -18,7 +18,6 @@ import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
-import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -44,21 +43,21 @@ class HostKeyProvider implements Provider<KeyPairProvider> {
Path ecdsaKey_521 = site.ssh_ecdsa_521;
Path ed25519Key = site.ssh_ed25519;
- final List<File> stdKeys = new ArrayList<>(6);
+ final List<Path> stdKeys = new ArrayList<>(6);
if (Files.exists(rsaKey)) {
- stdKeys.add(rsaKey.toAbsolutePath().toFile());
+ stdKeys.add(rsaKey);
}
if (Files.exists(ecdsaKey_256)) {
- stdKeys.add(ecdsaKey_256.toAbsolutePath().toFile());
+ stdKeys.add(ecdsaKey_256);
}
if (Files.exists(ecdsaKey_384)) {
- stdKeys.add(ecdsaKey_384.toAbsolutePath().toFile());
+ stdKeys.add(ecdsaKey_384);
}
if (Files.exists(ecdsaKey_521)) {
- stdKeys.add(ecdsaKey_521.toAbsolutePath().toFile());
+ stdKeys.add(ecdsaKey_521);
}
if (Files.exists(ed25519Key)) {
- stdKeys.add(ed25519Key.toAbsolutePath().toFile());
+ stdKeys.add(ed25519Key);
}
if (Files.exists(objKey)) {
@@ -70,14 +69,14 @@ class HostKeyProvider implements Provider<KeyPairProvider> {
// Both formats of host key exist, we don't know which format
// should be authoritative. Complain and abort.
//
- stdKeys.add(objKey.toAbsolutePath().toFile());
+ stdKeys.add(objKey);
throw new ProvisionException("Multiple host keys exist: " + stdKeys);
}
if (stdKeys.isEmpty()) {
throw new ProvisionException("No SSH keys under " + site.etc_dir);
}
FileKeyPairProvider kp = new FileKeyPairProvider();
- kp.setFiles(stdKeys);
+ kp.setPaths(stdKeys);
return kp;
}
}
diff --git a/java/com/google/gerrit/sshd/InactiveAccountDisconnector.java b/java/com/google/gerrit/sshd/InactiveAccountDisconnector.java
new file mode 100644
index 0000000000..1086626ce6
--- /dev/null
+++ b/java/com/google/gerrit/sshd/InactiveAccountDisconnector.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.sshd;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.events.AccountActivationListener;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.sshd.BaseCommand.Failure;
+import com.google.inject.Inject;
+import java.io.IOException;
+
+/** Closes open SSH connections upon account deactivation. */
+public class InactiveAccountDisconnector implements AccountActivationListener {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final SshDaemon sshDaemon;
+
+ @Inject
+ InactiveAccountDisconnector(SshDaemon sshDaemon) {
+ this.sshDaemon = sshDaemon;
+ }
+
+ @Override
+ public void onAccountDeactivated(int id) {
+ try {
+ SshUtil.forEachSshSession(
+ sshDaemon,
+ (sshId, sshSession, abstractSession, ioSession) -> {
+ CurrentUser sessionUser = sshSession.getUser();
+ if (sessionUser.isIdentifiedUser() && sessionUser.getAccountId().get() == id) {
+ logger.atInfo().log(
+ "Disconnecting SSH session %s because user %s(%d) got deactivated",
+ abstractSession, sessionUser.getLoggableName(), id);
+ try {
+ abstractSession.disconnect(-1, "user deactivated");
+ } catch (IOException e) {
+ logger.atWarning().withCause(e).log(
+ "Failure while deactivating session %s", abstractSession);
+ }
+ }
+ });
+ } catch (Failure e) {
+ // Ssh Daemon no longer running. Since we're only disconnecting connections anyways, this is
+ // most likely ok, so we log only at info level.
+ logger.atInfo().withCause(e).log("Failure while disconnecting deactivated account %d", id);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/sshd/NoShell.java b/java/com/google/gerrit/sshd/NoShell.java
index 5aaa647912..dd31e4c081 100644
--- a/java/com/google/gerrit/sshd/NoShell.java
+++ b/java/com/google/gerrit/sshd/NoShell.java
@@ -14,7 +14,7 @@
package com.google.gerrit.sshd;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.CanonicalWebUrl;
@@ -27,12 +27,13 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
-import org.apache.sshd.common.Factory;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.SessionAware;
+import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.shell.ShellFactory;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.util.SystemReader;
@@ -42,7 +43,7 @@ import org.eclipse.jgit.util.SystemReader;
* <p>This implementation is used to ensure clients who try to SSH directly to this server without
* supplying a command will get a reasonable error message, but cannot continue further.
*/
-class NoShell implements Factory<Command> {
+class NoShell implements ShellFactory {
private final Provider<SendMessage> shell;
@Inject
@@ -51,7 +52,7 @@ class NoShell implements Factory<Command> {
}
@Override
- public Command create() {
+ public Command createShell(ChannelSession channel) {
return shell.get();
}
@@ -98,7 +99,7 @@ class NoShell implements Factory<Command> {
}
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
Context old = sshScope.set(context);
String message;
try {
@@ -116,7 +117,7 @@ class NoShell implements Factory<Command> {
}
@Override
- public void destroy() {}
+ public void destroy(ChannelSession channel) {}
}
static class MessageFactory {
@@ -145,7 +146,7 @@ class NoShell implements Factory<Command> {
msg.append("\r\n");
Account account = user.getAccount();
- String name = account.getFullName();
+ String name = account.fullName();
if (name == null || name.isEmpty()) {
name = user.getUserName().orElse(anonymousCowardName);
}
diff --git a/java/com/google/gerrit/sshd/SshCommand.java b/java/com/google/gerrit/sshd/SshCommand.java
index 98289570b4..e60ba6d274 100644
--- a/java/com/google/gerrit/sshd/SshCommand.java
+++ b/java/com/google/gerrit/sshd/SshCommand.java
@@ -14,14 +14,28 @@
package com.google.gerrit.sshd;
+import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.AccessPath;
+import com.google.gerrit.server.RequestInfo;
+import com.google.gerrit.server.RequestListener;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.logging.PerformanceLogContext;
+import com.google.gerrit.server.logging.PerformanceLogger;
import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.inject.Inject;
import java.io.IOException;
import java.io.PrintWriter;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
+import org.eclipse.jgit.lib.Config;
import org.kohsuke.args4j.Option;
public abstract class SshCommand extends BaseCommand {
+ @Inject private DynamicSet<PerformanceLogger> performanceLoggers;
+ @Inject private PluginSetContext<RequestListener> requestListeners;
+ @Inject @GerritServerConfig private Config config;
+
@Option(name = "--trace", usage = "enable request tracing")
private boolean trace;
@@ -32,13 +46,18 @@ public abstract class SshCommand extends BaseCommand {
protected PrintWriter stderr;
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
startThread(
() -> {
parseCommandLine();
stdout = toPrintWriter(out);
stderr = toPrintWriter(err);
- try (TraceContext traceContext = enableTracing()) {
+ try (TraceContext traceContext = enableTracing();
+ PerformanceLogContext performanceLogContext =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ RequestInfo requestInfo =
+ RequestInfo.builder(RequestInfo.RequestType.SSH, user, traceContext).build();
+ requestListeners.runEach(l -> l.onRequest(requestInfo));
SshCommand.this.run();
} finally {
stdout.flush();
diff --git a/java/com/google/gerrit/sshd/SshDaemon.java b/java/com/google/gerrit/sshd/SshDaemon.java
index 8265413d7b..a1c057ffc0 100644
--- a/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/java/com/google/gerrit/sshd/SshDaemon.java
@@ -51,6 +51,7 @@ import java.nio.file.PathMatcher;
import java.nio.file.WatchService;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
+import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.PublicKey;
@@ -214,6 +215,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
final boolean enableCompression = cfg.getBoolean("sshd", "enableCompression", false);
SshSessionBackend backend = cfg.getEnum("sshd", null, "backend", SshSessionBackend.NIO2);
+ boolean channelIdTracking = cfg.getBoolean("sshd", "enableChannelIdTracking", true);
gracefulStopTimeout = cfg.getTimeUnit("sshd", null, "gracefulStopTimeout", 0, TimeUnit.SECONDS);
@@ -229,7 +231,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
initMacs(cfg);
initSignatures();
initChannels();
- initUnknownChannelReferenceHandler();
+ initUnknownChannelReferenceHandler(channelIdTracking);
initForwarding();
initFileSystemFactory();
initSubsystems();
@@ -429,12 +431,12 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
return Collections.emptyList();
}
- final List<PublicKey> keys = myHostKeys();
- final List<HostKey> r = new ArrayList<>();
+ List<HostKey> r = new ArrayList<>();
+ List<PublicKey> keys = myHostKeys();
for (PublicKey pub : keys) {
- final Buffer buf = new ByteArrayBuffer();
+ Buffer buf = new ByteArrayBuffer();
buf.putRawPublicKey(pub);
- final byte[] keyBin = buf.getCompactData();
+ byte[] keyBin = buf.getCompactData();
for (String addr : advertised) {
try {
@@ -445,24 +447,29 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
}
}
}
+
return Collections.unmodifiableList(r);
}
private List<PublicKey> myHostKeys() {
- final KeyPairProvider p = getKeyPairProvider();
- final List<PublicKey> keys = new ArrayList<>(6);
- addPublicKey(keys, p, KeyPairProvider.SSH_ED25519);
- addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP256);
- addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP384);
- addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP521);
- addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
- addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+ KeyPairProvider p = getKeyPairProvider();
+ List<PublicKey> keys = new ArrayList<>(6);
+ try {
+ addPublicKey(keys, p, KeyPairProvider.SSH_ED25519);
+ addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP256);
+ addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP384);
+ addPublicKey(keys, p, KeyPairProvider.ECDSA_SHA2_NISTP521);
+ addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
+ addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+ } catch (IOException | GeneralSecurityException e) {
+ throw new IllegalStateException("Cannot load SSHD host key", e);
+ }
return keys;
}
- private static void addPublicKey(
- final Collection<PublicKey> out, KeyPairProvider p, String type) {
- final KeyPair pair = p.loadKey(type);
+ private static void addPublicKey(final Collection<PublicKey> out, KeyPairProvider p, String type)
+ throws IOException, GeneralSecurityException {
+ final KeyPair pair = p.loadKey(null, type);
if (pair != null && pair.getPublic() != null) {
out.add(pair.getPublic());
}
@@ -562,14 +569,14 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
@SuppressWarnings("unchecked")
private void initCiphers(Config cfg) {
- final List<NamedFactory<Cipher>> a = BaseBuilder.setUpDefaultCiphers(true);
+ List<NamedFactory<Cipher>> a = BaseBuilder.setUpDefaultCiphers(true);
for (Iterator<NamedFactory<Cipher>> i = a.iterator(); i.hasNext(); ) {
- final NamedFactory<Cipher> f = i.next();
+ NamedFactory<Cipher> f = i.next();
try {
- final Cipher c = f.create();
- final byte[] key = new byte[c.getBlockSize()];
- final byte[] iv = new byte[c.getIVSize()];
+ Cipher c = f.create();
+ byte[] key = new byte[c.getKdfSize()];
+ byte[] iv = new byte[c.getIVSize()];
c.init(Cipher.Mode.Encrypt, key, iv);
} catch (InvalidKeyException e) {
logger.atWarning().log(
@@ -662,7 +669,8 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
}
private void initSignatures() {
- setSignatureFactories(BaseBuilder.setUpDefaultSignatures(true));
+ setSignatureFactories(
+ NamedFactory.setUpBuiltinFactories(false, ServerBuilder.DEFAULT_SIGNATURE_PREFERENCE));
}
private void initCompression(boolean enableCompression) {
@@ -678,10 +686,9 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
// However, if there are CPU in abundance and the server is reachable through
// slow networks, gits with huge amount of refs can benefit from SSH-compression
// since git does not compress the ref announcement during the handshake.
- //
- // Compression can be especially useful when Gerrit slaves are being used
- // for the larger clones and fetches and the master server mostly takes small
- // receive-packs.
+ // Compression can be especially useful when Gerrit replica are being used
+ // for the larger clones and fetches and the primary server handling write
+ // operations mostly takes small receive-packs.
if (enableCompression) {
compressionFactories.add(BuiltinCompressions.zlib);
@@ -694,8 +701,11 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
setChannelFactories(ServerBuilder.DEFAULT_CHANNEL_FACTORIES);
}
- private void initUnknownChannelReferenceHandler() {
- setUnknownChannelReferenceHandler(DefaultUnknownChannelReferenceHandler.INSTANCE);
+ private void initUnknownChannelReferenceHandler(boolean enableChannelIdTracking) {
+ setUnknownChannelReferenceHandler(
+ enableChannelIdTracking
+ ? ChannelIdTrackingUnknownChannelReferenceHandler.TRACKER
+ : DefaultUnknownChannelReferenceHandler.INSTANCE);
}
private void initSubsystems() {
diff --git a/java/com/google/gerrit/sshd/SshKeyCacheEntry.java b/java/com/google/gerrit/sshd/SshKeyCacheEntry.java
index 8e962e3a63..78fd7da330 100644
--- a/java/com/google/gerrit/sshd/SshKeyCacheEntry.java
+++ b/java/com/google/gerrit/sshd/SshKeyCacheEntry.java
@@ -14,7 +14,7 @@
package com.google.gerrit.sshd;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import java.security.PublicKey;
class SshKeyCacheEntry {
diff --git a/java/com/google/gerrit/sshd/SshKeyCacheImpl.java b/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
index fb0b8f6f15..773c25bce6 100644
--- a/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
+++ b/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
@@ -24,6 +24,7 @@ import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.ssh.SshKeyCache;
@@ -106,7 +107,9 @@ public class SshKeyCacheImpl implements SshKeyCache {
@Override
public Iterable<SshKeyCacheEntry> load(String username) throws Exception {
try (TraceTimer timer =
- TraceContext.newTimer("Loading SSH keys for account with username %s", username)) {
+ TraceContext.newTimer(
+ "Loading SSH keys for account with username",
+ Metadata.builder().username(username).build())) {
Optional<ExternalId> user =
externalIds.get(ExternalId.Key.create(SCHEME_USERNAME, username));
if (!user.isPresent()) {
diff --git a/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java b/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
index b2853b9dfd..02a9e48bae 100644
--- a/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
+++ b/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
@@ -15,8 +15,8 @@
package com.google.gerrit.sshd;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.InvalidSshKeyException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountSshKey;
import com.google.gerrit.server.ssh.SshKeyCreator;
import java.security.NoSuchAlgorithmException;
diff --git a/java/com/google/gerrit/sshd/SshModule.java b/java/com/google/gerrit/sshd/SshModule.java
index e4aa14cf30..9301f8a39a 100644
--- a/java/com/google/gerrit/sshd/SshModule.java
+++ b/java/com/google/gerrit/sshd/SshModule.java
@@ -19,6 +19,7 @@ import static com.google.inject.Scopes.SINGLETON;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
+import com.google.gerrit.extensions.events.AccountActivationListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.lifecycle.LifecycleModule;
@@ -103,6 +104,9 @@ public class SshModule extends LifecycleModule {
DynamicItem.itemOf(binder(), SshCreateCommandInterceptor.class);
DynamicSet.setOf(binder(), SshExecuteCommandInterceptor.class);
+ DynamicSet.bind(binder(), AccountActivationListener.class)
+ .to(InactiveAccountDisconnector.class);
+
listener().toInstance(registerInParentInjectors());
listener().to(SshLog.class);
listener().to(SshDaemon.class);
diff --git a/java/com/google/gerrit/sshd/SshSession.java b/java/com/google/gerrit/sshd/SshSession.java
index a2c3d93648..b39eaede24 100644
--- a/java/com/google/gerrit/sshd/SshSession.java
+++ b/java/com/google/gerrit/sshd/SshSession.java
@@ -19,7 +19,7 @@ import com.google.gerrit.server.CurrentUser;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import org.apache.sshd.common.AttributeStore.AttributeKey;
+import org.apache.sshd.common.AttributeRepository.AttributeKey;
/** Global data related to an active SSH connection. */
public class SshSession {
diff --git a/java/com/google/gerrit/sshd/SshUtil.java b/java/com/google/gerrit/sshd/SshUtil.java
index 670d64ccc4..6176628c4d 100644
--- a/java/com/google/gerrit/sshd/SshUtil.java
+++ b/java/com/google/gerrit/sshd/SshUtil.java
@@ -14,10 +14,11 @@
package com.google.gerrit.sshd;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountSshKey;
+import com.google.gerrit.sshd.BaseCommand.Failure;
import com.google.gerrit.sshd.SshScope.Context;
import java.io.BufferedReader;
import java.io.IOException;
@@ -30,7 +31,10 @@ import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import org.apache.commons.codec.binary.Base64;
import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.helpers.AbstractSession;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.server.session.ServerSession;
import org.eclipse.jgit.lib.Constants;
@@ -154,4 +158,30 @@ public class SshUtil {
final Account.Id account) {
return userFactory.create(sd.getRemoteAddress(), account);
}
+
+ public static void forEachSshSession(SshDaemon sshDaemon, SessionConsumer consumer)
+ throws Failure {
+ IoAcceptor ioAcceptor = sshDaemon.getIoAcceptor();
+ if (ioAcceptor == null) {
+ throw new Failure(1, "fatal: sshd no longer running");
+ }
+ ioAcceptor
+ .getManagedSessions()
+ .forEach(
+ (id, ioSession) -> {
+ AbstractSession abstractSession = AbstractSession.getSession(ioSession, true);
+ if (abstractSession != null) {
+ SshSession sshSession = abstractSession.getAttribute(SshSession.KEY);
+ if (sshSession != null) {
+ consumer.accept(id, sshSession, abstractSession, ioSession);
+ }
+ }
+ });
+ }
+
+ @FunctionalInterface
+ public interface SessionConsumer {
+ public void accept(
+ Long id, SshSession sshSession, AbstractSession abstractSession, IoSession ioSession);
+ }
}
diff --git a/java/com/google/gerrit/sshd/SuExec.java b/java/com/google/gerrit/sshd/SuExec.java
index 6d0bf2bc3f..ea163d52ab 100644
--- a/java/com/google/gerrit/sshd/SuExec.java
+++ b/java/com/google/gerrit/sshd/SuExec.java
@@ -18,8 +18,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.Atomics;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PeerDaemonUser;
@@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.command.Command;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
@@ -90,7 +91,7 @@ public final class SuExec extends BaseCommand {
}
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
try {
checkCanRunAs();
parseCommandLine();
@@ -102,7 +103,7 @@ public final class SuExec extends BaseCommand {
cmd.setArguments(args.toArray(new String[args.size()]));
provideStateTo(cmd);
atomicCmd.set(cmd);
- cmd.start(env);
+ cmd.start(channel, env);
} finally {
sshScope.set(old);
}
@@ -158,11 +159,11 @@ public final class SuExec extends BaseCommand {
}
@Override
- public void destroy() {
+ public void destroy(ChannelSession channel) {
Command cmd = atomicCmd.getAndSet(null);
if (cmd != null) {
try {
- cmd.destroy();
+ cmd.destroy(channel);
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
diff --git a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
index 42e5624718..134fb03828 100644
--- a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -69,7 +69,7 @@ public class BanCommitCommand extends SshCommand {
BanCommitInput.fromCommits(Lists.transform(commitsToBan, ObjectId::getName));
input.reason = reason;
- BanResultInfo r = banCommit.apply(new ProjectResource(projectState, user), input);
+ BanResultInfo r = banCommit.apply(new ProjectResource(projectState, user), input).value();
printCommits(r.newlyBanned, "The following commits were banned");
printCommits(r.alreadyBanned, "The following commits were already banned");
printCommits(r.ignored, "The following ids do not represent commits and were ignored");
diff --git a/java/com/google/gerrit/sshd/commands/CloseConnection.java b/java/com/google/gerrit/sshd/commands/CloseConnection.java
index 6cd009b54b..e0b87f88cd 100644
--- a/java/com/google/gerrit/sshd/commands/CloseConnection.java
+++ b/java/com/google/gerrit/sshd/commands/CloseConnection.java
@@ -23,15 +23,12 @@ import com.google.gerrit.sshd.AdminHighPriorityCommand;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.gerrit.sshd.SshDaemon;
-import com.google.gerrit.sshd.SshSession;
+import com.google.gerrit.sshd.SshUtil;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.io.IoAcceptor;
-import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.session.helpers.AbstractSession;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
@@ -61,36 +58,26 @@ final class CloseConnection extends SshCommand {
@Override
protected void run() throws Failure {
enableGracefulStop();
- IoAcceptor acceptor = sshDaemon.getIoAcceptor();
- if (acceptor == null) {
- throw new Failure(1, "fatal: sshd no longer running");
- }
- for (String sessionId : sessionIds) {
- boolean connectionFound = false;
- int id = (int) Long.parseLong(sessionId, 16);
- for (IoSession io : acceptor.getManagedSessions().values()) {
- AbstractSession serverSession = AbstractSession.getSession(io, true);
- SshSession sshSession =
- serverSession != null ? serverSession.getAttribute(SshSession.KEY) : null;
- if (sshSession != null && sshSession.getSessionId() == id) {
- connectionFound = true;
- stdout.println("closing connection " + sessionId + "...");
- CloseFuture future = io.close(true);
- if (wait) {
- try {
- future.await();
- stdout.println("closed connection " + sessionId);
- } catch (IOException e) {
- logger.atWarning().log(
- "Wait for connection to close interrupted: %s", e.getMessage());
+ SshUtil.forEachSshSession(
+ sshDaemon,
+ (k, sshSession, abstractSession, ioSession) -> {
+ String sessionId = String.format("%08x", sshSession.getSessionId());
+ if (sessionIds.remove(sessionId)) {
+ stdout.println("closing connection " + sessionId + "...");
+ CloseFuture future = ioSession.close(true);
+ if (wait) {
+ try {
+ future.await();
+ stdout.println("closed connection " + sessionId);
+ } catch (IOException e) {
+ logger.atWarning().log(
+ "Wait for connection to close interrupted: %s", e.getMessage());
+ }
}
}
- break;
- }
- }
- if (!connectionFound) {
- stderr.print("close connection " + sessionId + ": no such connection\n");
- }
+ });
+ for (String sessionId : sessionIds) {
+ stderr.print("close connection " + sessionId + ": no such connection\n");
}
}
}
diff --git a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
index 00fc20fc02..4da55e22f5 100644
--- a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
@@ -18,12 +18,12 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.accounts.AccountInput;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.account.CreateAccount;
import com.google.gerrit.sshd.CommandMetaData;
diff --git a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index 8169901677..b243748adc 100644
--- a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -17,14 +17,14 @@ package com.google.gerrit.sshd.commands;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.group.AddMembers;
@@ -115,11 +115,12 @@ final class CreateGroupCommand extends SshCommand {
}
} catch (RestApiException e) {
throw die(e);
+ } catch (Exception e) {
+ throw new Failure(1, "unavailable", e);
}
}
- private GroupResource createGroup()
- throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
+ private GroupResource createGroup() throws Exception {
GroupInput input = new GroupInput();
input.description = groupDescription;
input.visibleToAll = visibleToAll;
@@ -129,7 +130,9 @@ final class CreateGroupCommand extends SshCommand {
}
GroupInfo group =
- createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(groupName), input);
+ createGroup
+ .apply(TopLevelResource.INSTANCE, IdString.fromDecoded(groupName), input)
+ .value();
return groups.parse(TopLevelResource.INSTANCE, IdString.fromUrl(group.id));
}
diff --git a/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
index ad7021b12c..f2ab4e8520 100644
--- a/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -18,6 +18,8 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.projects.ConfigValue;
@@ -25,8 +27,6 @@ import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SuggestParentCandidates;
diff --git a/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
index 87b6f02144..905ba9c17e 100644
--- a/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
+++ b/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -14,7 +14,7 @@
package com.google.gerrit.sshd.commands;
-import com.google.gerrit.reviewdb.client.CoreDownloadSchemes;
+import com.google.gerrit.entities.CoreDownloadSchemes;
import com.google.gerrit.server.config.DownloadConfig;
import com.google.gerrit.sshd.CommandModule;
import com.google.gerrit.sshd.CommandName;
diff --git a/java/com/google/gerrit/sshd/commands/FlushCaches.java b/java/com/google/gerrit/sshd/commands/FlushCaches.java
index e048eae557..fe2a8972f8 100644
--- a/java/com/google/gerrit/sshd/commands/FlushCaches.java
+++ b/java/com/google/gerrit/sshd/commands/FlushCaches.java
@@ -23,7 +23,6 @@ import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.config.ConfigResource;
-import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.config.ListCaches;
import com.google.gerrit.server.restapi.config.ListCaches.OutputFormat;
import com.google.gerrit.server.restapi.config.PostCaches;
@@ -82,15 +81,16 @@ final class FlushCaches extends SshCommand {
}
} catch (RestApiException e) {
throw die(e.getMessage());
- } catch (PermissionBackendException e) {
+ } catch (Exception e) {
throw new Failure(1, "unavailable", e);
}
}
@SuppressWarnings("unchecked")
- private void doList() {
+ private void doList() throws Exception {
for (String name :
- (List<String>) listCaches.setFormat(OutputFormat.LIST).apply(new ConfigResource())) {
+ (List<String>)
+ listCaches.setFormat(OutputFormat.LIST).apply(new ConfigResource()).value()) {
stderr.print(name);
stderr.print('\n');
}
diff --git a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
index 97737b0e01..28a7804498 100644
--- a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
@@ -21,8 +21,8 @@ import static java.util.stream.Collectors.toList;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GarbageCollectionResult;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GarbageCollection;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
diff --git a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
index 376b8370fa..041c85c856 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
@@ -14,9 +14,9 @@
package com.google.gerrit.sshd.commands;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.Input;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.change.Index;
diff --git a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
index a6fe833479..7bf42eb06a 100644
--- a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -18,9 +18,9 @@ import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.ioutil.ColumnFormatter;
@@ -63,7 +63,7 @@ public class ListGroupsCommand extends SshCommand {
if (verboseOutput) {
Optional<InternalGroup> group =
info.ownerId != null
- ? groupCache.get(new AccountGroup.UUID(Url.decode(info.ownerId)))
+ ? groupCache.get(AccountGroup.uuid(Url.decode(info.ownerId)))
: Optional.empty();
formatter.addColumn(Url.decode(info.id));
diff --git a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
index 53d4ac6407..3269c2b0c4 100644
--- a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
@@ -18,8 +18,8 @@ import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl;
@@ -70,7 +70,7 @@ public class ListMembersCommand extends SshCommand {
}
void display(PrintWriter writer) throws PermissionBackendException {
- Optional<InternalGroup> group = groupCache.get(new AccountGroup.NameKey(name));
+ Optional<InternalGroup> group = groupCache.get(AccountGroup.nameKey(name));
String errorText = "Group not found or not visible\n";
if (!group.isPresent()) {
diff --git a/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
index a1af9a084b..b8fc55a09d 100644
--- a/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -17,12 +17,12 @@ package com.google.gerrit.sshd.commands;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -77,7 +77,7 @@ public class LsUserRefs extends SshCommand {
enableGracefulStop();
Account.Id userAccountId;
try {
- userAccountId = accountResolver.resolve(userName).asUnique().getAccount().getId();
+ userAccountId = accountResolver.resolve(userName).asUnique().account().id();
} catch (UnprocessableEntityException e) {
stdout.println(e.getMessage());
stdout.flush();
diff --git a/java/com/google/gerrit/sshd/commands/PatchSetParser.java b/java/com/google/gerrit/sshd/commands/PatchSetParser.java
index 45210f6c1b..f804c2dae2 100644
--- a/java/com/google/gerrit/sshd/commands/PatchSetParser.java
+++ b/java/com/google/gerrit/sshd/commands/PatchSetParser.java
@@ -15,10 +15,10 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -56,7 +56,7 @@ public class PatchSetParser {
throws UnloggedFailure {
// By commit?
//
- if (token.matches("^([0-9a-fA-F]{4," + RevId.LEN + "})$")) {
+ if (token.matches("^([0-9a-fA-F]{4," + ObjectIds.STR_LEN + "})$")) {
InternalChangeQuery query = queryProvider.get();
List<ChangeData> cds;
if (projectState != null) {
@@ -76,7 +76,7 @@ public class PatchSetParser {
continue;
}
for (PatchSet ps : cd.patchSets()) {
- if (ps.getRevision().matches(token)) {
+ if (ObjectIds.matchesAbbreviation(ps.commitId(), token)) {
matches.add(ps);
}
}
@@ -101,7 +101,7 @@ public class PatchSetParser {
} catch (IllegalArgumentException e) {
throw error("\"" + token + "\" is not a valid patch set", e);
}
- ChangeNotes notes = getNotes(projectState, patchSetId.getParentKey());
+ ChangeNotes notes = getNotes(projectState, patchSetId.changeId());
PatchSet patchSet = psUtil.get(notes, patchSetId);
if (patchSet == null) {
throw error("\"" + token + "\" no such patch set");
@@ -147,7 +147,7 @@ public class PatchSetParser {
// No --branch option, so they want every branch.
return true;
}
- return change.getDest().get().equals(branch);
+ return change.getDest().branch().equals(branch);
}
public static UnloggedFailure error(String msg) {
diff --git a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
index 7216ed9bbd..504b239d68 100644
--- a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
@@ -42,7 +42,7 @@ public class PluginLsCommand extends SshCommand {
@Override
public void run() throws Exception {
enableGracefulStop();
- Map<String, PluginInfo> output = list.apply(TopLevelResource.INSTANCE);
+ Map<String, PluginInfo> output = list.apply(TopLevelResource.INSTANCE).value();
if (format.isJson()) {
format
diff --git a/java/com/google/gerrit/sshd/commands/Receive.java b/java/com/google/gerrit/sshd/commands/Receive.java
index 0b089b68ce..92666f3b13 100644
--- a/java/com/google/gerrit/sshd/commands/Receive.java
+++ b/java/com/google/gerrit/sshd/commands/Receive.java
@@ -18,10 +18,9 @@ import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.Capable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -31,12 +30,8 @@ import com.google.gerrit.sshd.AbstractGitCommand;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.inject.Inject;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.errors.TooLargeObjectInPackException;
import org.eclipse.jgit.errors.UnpackException;
-import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.AdvertiseRefsHook;
import org.eclipse.jgit.transport.ReceivePack;
import org.kohsuke.args4j.Option;
@@ -118,52 +113,17 @@ final class Receive extends AbstractGitCommand {
logger.atInfo().log(msg.toString());
throw new UnloggedFailure(128, "error: " + badStream.getCause().getMessage());
}
-
- // This may have been triggered by branch level access controls.
- // Log what the heck is going on, as detailed as we can.
- //
StringBuilder msg = new StringBuilder();
msg.append("Unpack error on project \"").append(projectState.getName()).append("\":\n");
msg.append(" AdvertiseRefsHook: ").append(rp.getAdvertiseRefsHook());
if (rp.getAdvertiseRefsHook() == AdvertiseRefsHook.DEFAULT) {
msg.append("DEFAULT");
- } else if (rp.getAdvertiseRefsHook() instanceof DefaultAdvertiseRefsHook) {
- msg.append("DefaultAdvertiseRefsHook");
} else {
msg.append(rp.getAdvertiseRefsHook().getClass());
}
msg.append("\n");
- if (rp.getAdvertiseRefsHook() instanceof DefaultAdvertiseRefsHook) {
- Map<String, Ref> adv = rp.getAdvertisedRefs();
- msg.append(" Visible references (").append(adv.size()).append("):\n");
- for (Ref ref : adv.values()) {
- msg.append(" - ")
- .append(ref.getObjectId().abbreviate(8).name())
- .append(" ")
- .append(ref.getName())
- .append("\n");
- }
-
- List<Ref> allRefs = rp.getRepository().getRefDatabase().getRefs();
- List<Ref> hidden = new ArrayList<>();
- for (Ref ref : allRefs) {
- if (!adv.containsKey(ref.getName())) {
- hidden.add(ref);
- }
- }
-
- msg.append(" Hidden references (").append(hidden.size()).append("):\n");
- for (Ref ref : hidden) {
- msg.append(" - ")
- .append(ref.getObjectId().abbreviate(8).name())
- .append(" ")
- .append(ref.getName())
- .append("\n");
- }
- }
-
IOException detail = new IOException(msg.toString(), badStream);
throw new Failure(128, "fatal: Unpack error, check server log", detail);
}
diff --git a/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 4202aac9d4..61e529f121 100644
--- a/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -19,10 +19,12 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.CharStreams;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.AbandonInput;
@@ -34,7 +36,6 @@ import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.json.OutputFormat;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
@@ -228,11 +229,11 @@ public class ReviewCommand extends SshCommand {
writeError("error", e.getMessage() + "\n");
} catch (NoSuchChangeException e) {
ok = false;
- writeError("error", "no such change " + patchSet.getId().getParentKey().get());
+ writeError("error", "no such change " + patchSet.id().changeId().get());
} catch (Exception e) {
ok = false;
- writeError("fatal", "internal server error while reviewing " + patchSet.getId() + "\n");
- logger.atSevere().withCause(e).log("internal error while reviewing %s", patchSet.getId());
+ writeError("fatal", "internal server error while reviewing " + patchSet.id() + "\n");
+ logger.atSevere().withCause(e).log("internal error while reviewing %s", patchSet.id());
}
}
@@ -243,8 +244,8 @@ public class ReviewCommand extends SshCommand {
private void applyReview(PatchSet patchSet, ReviewInput review) throws RestApiException {
gApi.changes()
- .id(patchSet.getId().getParentKey().get())
- .revision(patchSet.getRevision().get())
+ .id(patchSet.id().changeId().get())
+ .revision(patchSet.commitId().name())
.review(review);
}
@@ -311,11 +312,11 @@ public class ReviewCommand extends SshCommand {
}
private ChangeApi changeApi(PatchSet patchSet) throws RestApiException {
- return gApi.changes().id(patchSet.getId().getParentKey().get());
+ return gApi.changes().id(patchSet.id().changeId().get());
}
private RevisionApi revisionApi(PatchSet patchSet) throws RestApiException {
- return changeApi(patchSet).revision(patchSet.getRevision().get());
+ return changeApi(patchSet).revision(patchSet.commitId().name());
}
@Override
@@ -350,15 +351,15 @@ public class ReviewCommand extends SshCommand {
private static Option newApproveOption(LabelType type, String usage) {
return OptionUtil.newOption(
asOptionName(type),
- new String[0],
+ ImmutableList.of(),
usage,
"N",
false,
false,
false,
LabelHandler.class,
- new String[0],
- new String[0]);
+ ImmutableList.of(),
+ ImmutableList.of());
}
private static class LabelSetter implements Setter<Short> {
diff --git a/java/com/google/gerrit/sshd/commands/ScpCommand.java b/java/com/google/gerrit/sshd/commands/ScpCommand.java
index 5122b3503b..6912795c1d 100644
--- a/java/com/google/gerrit/sshd/commands/ScpCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ScpCommand.java
@@ -34,6 +34,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
final class ScpCommand extends BaseCommand {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -81,7 +82,7 @@ final class ScpCommand extends BaseCommand {
}
@Override
- public void start(Environment env) {
+ public void start(ChannelSession channel, Environment env) {
startThread(this::runImp, AccessPath.SSH_COMMAND);
}
diff --git a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 1ecb591d8d..43a1670891 100644
--- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -18,6 +18,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
import com.google.gerrit.common.RawInputUtil;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.api.accounts.SshKeyInput;
@@ -31,7 +32,6 @@ import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
@@ -212,8 +212,7 @@ final class SetAccountCommand extends SshCommand {
}
}
- private void setAccount()
- throws IOException, UnloggedFailure, ConfigInvalidException, PermissionBackendException {
+ private void setAccount() throws Failure {
user = genericUserFactory.create(id);
rsrc = new AccountResource(user.asIdentifiedUser());
try {
@@ -268,6 +267,8 @@ final class SetAccountCommand extends SshCommand {
}
} catch (RestApiException e) {
throw die(e.getMessage());
+ } catch (Exception e) {
+ throw new Failure(1, "unavailable", e);
}
}
@@ -280,10 +281,8 @@ final class SetAccountCommand extends SshCommand {
}
}
- private void deleteSshKeys(List<String> sshKeys)
- throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
- PermissionBackendException {
- List<SshKeyInfo> infos = getSshKeys.apply(rsrc);
+ private void deleteSshKeys(List<String> sshKeys) throws Exception {
+ List<SshKeyInfo> infos = getSshKeys.apply(rsrc).value();
if (sshKeys.contains("ALL")) {
for (SshKeyInfo i : infos) {
deleteSshKey(i);
@@ -319,10 +318,9 @@ final class SetAccountCommand extends SshCommand {
}
}
- private void deleteEmail(String email)
- throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
+ private void deleteEmail(String email) throws Exception {
if (email.equals("ALL")) {
- List<EmailInfo> emails = getEmails.apply(rsrc);
+ List<EmailInfo> emails = getEmails.apply(rsrc).value();
for (EmailInfo e : emails) {
deleteEmail.apply(new AccountResource.Email(user.asIdentifiedUser(), e.email), new Input());
}
@@ -331,9 +329,8 @@ final class SetAccountCommand extends SshCommand {
}
}
- private void putPreferred(String email)
- throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
- for (EmailInfo e : getEmails.apply(rsrc)) {
+ private void putPreferred(String email) throws Exception {
+ for (EmailInfo e : getEmails.apply(rsrc).value()) {
if (e.email.equals(email)) {
putPreferred.apply(new AccountResource.Email(user.asIdentifiedUser(), email), null);
return;
diff --git a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
index 5557b16ace..db8e42a3c1 100644
--- a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
@@ -19,11 +19,11 @@ import static java.util.stream.Collectors.toList;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Streams;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.GroupCache;
@@ -141,7 +141,7 @@ public class SetMembersCommand extends SshCommand {
return "n/a";
}
return MoreObjects.firstNonNull(
- accountState.get().getAccount().getPreferredEmail(), "n/a");
+ accountState.get().account().preferredEmail(), "n/a");
})
.collect(joining(", "));
out.write(
diff --git a/java/com/google/gerrit/sshd/commands/SetParentCommand.java b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
index 87722d926b..bfdbff9032 100644
--- a/java/com/google/gerrit/sshd/commands/SetParentCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
@@ -16,16 +16,14 @@ package com.google.gerrit.sshd.commands;
import static java.util.stream.Collectors.toList;
-import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.ParentInput;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectResource;
@@ -113,7 +111,7 @@ final class SetParentCommand extends SshCommand {
childProjects.addAll(getChildrenForReparenting(oldParent));
} catch (PermissionBackendException e) {
throw new Failure(1, "permissions unavailable", e);
- } catch (StorageException | RestApiException e) {
+ } catch (Exception e) {
throw new Failure(1, "failure in request", e);
}
}
@@ -149,8 +147,7 @@ final class SetParentCommand extends SshCommand {
* list of child projects does not contain projects that were specified to be excluded from
* reparenting.
*/
- private List<Project.NameKey> getChildrenForReparenting(ProjectState parent)
- throws PermissionBackendException, RestApiException {
+ private List<Project.NameKey> getChildrenForReparenting(ProjectState parent) throws Exception {
final List<Project.NameKey> childProjects = new ArrayList<>();
final List<Project.NameKey> excluded = new ArrayList<>(excludedChildren.size());
for (ProjectState excludedChild : excludedChildren) {
@@ -160,8 +157,8 @@ final class SetParentCommand extends SshCommand {
if (newParentKey != null) {
automaticallyExcluded.addAll(getAllParents(newParentKey));
}
- for (ProjectInfo child : listChildProjects.apply(new ProjectResource(parent, user))) {
- final Project.NameKey childName = new Project.NameKey(child.name);
+ for (ProjectInfo child : listChildProjects.apply(new ProjectResource(parent, user)).value()) {
+ final Project.NameKey childName = Project.nameKey(child.name);
if (!excluded.contains(childName)) {
if (!automaticallyExcluded.contains(childName)) {
childProjects.add(childName);
diff --git a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index 5a04026638..0e6fa65d27 100644
--- a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -15,12 +15,12 @@
package com.google.gerrit.sshd.commands;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -142,7 +142,7 @@ public class SetReviewersCommand extends SshCommand {
input.confirmed = true;
String error;
try {
- error = postReviewers.apply(changeRsrc, input).error;
+ error = postReviewers.apply(changeRsrc, input).value().error;
} catch (Exception e) {
error = String.format("could not add %s: %s", reviewer, e.getMessage());
}
diff --git a/java/com/google/gerrit/sshd/commands/ShowCaches.java b/java/com/google/gerrit/sshd/commands/ShowCaches.java
index 039b74e3d6..63a0dda771 100644
--- a/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -51,6 +51,7 @@ import org.apache.sshd.common.io.IoAcceptor;
import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.io.mina.MinaSession;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
import org.kohsuke.args4j.Option;
/** Show the current cache states. */
@@ -97,7 +98,7 @@ final class ShowCaches extends SshCommand {
private int nw;
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
String s = env.getEnv().get(Environment.ENV_COLUMNS);
if (s != null && !s.isEmpty()) {
try {
@@ -106,11 +107,11 @@ final class ShowCaches extends SshCommand {
columns = 80;
}
}
- super.start(env);
+ super.start(channel, env);
}
@Override
- protected void run() throws UnloggedFailure {
+ protected void run() throws Failure {
enableGracefulStop();
nw = columns - 50;
Date now = new Date();
@@ -162,39 +163,45 @@ final class ShowCaches extends SshCommand {
}
stdout.print("+---------------------+---------+---------+\n");
- Collection<CacheInfo> caches = getCaches();
- printMemoryCoreCaches(caches);
- printMemoryPluginCaches(caches);
- printDiskCaches(caches);
- stdout.print('\n');
-
- boolean showJvm;
try {
- permissionBackend.user(self).check(GlobalPermission.MAINTAIN_SERVER);
- showJvm = true;
- } catch (AuthException | PermissionBackendException e) {
- // Silently ignore and do not display detailed JVM information.
- showJvm = false;
- }
- if (showJvm) {
- sshSummary();
+ Collection<CacheInfo> caches = getCaches();
+ printMemoryCoreCaches(caches);
+ printMemoryPluginCaches(caches);
+ printDiskCaches(caches);
+ stdout.print('\n');
- SummaryInfo summary = getSummary.setGc(gc).setJvm(showJVM).apply(new ConfigResource());
- taskSummary(summary.taskSummary);
- memSummary(summary.memSummary);
- threadSummary(summary.threadSummary);
+ boolean showJvm;
+ try {
+ permissionBackend.user(self).check(GlobalPermission.MAINTAIN_SERVER);
+ showJvm = true;
+ } catch (AuthException | PermissionBackendException e) {
+ // Silently ignore and do not display detailed JVM information.
+ showJvm = false;
+ }
+ if (showJvm) {
+ sshSummary();
- if (showJVM && summary.jvmSummary != null) {
- jvmSummary(summary.jvmSummary);
+ SummaryInfo summary =
+ getSummary.setGc(gc).setJvm(showJVM).apply(new ConfigResource()).value();
+ taskSummary(summary.taskSummary);
+ memSummary(summary.memSummary);
+ threadSummary(summary.threadSummary);
+
+ if (showJVM && summary.jvmSummary != null) {
+ jvmSummary(summary.jvmSummary);
+ }
}
+ } catch (Exception e) {
+ throw new Failure(1, "unavailable", e);
}
stdout.flush();
}
- private Collection<CacheInfo> getCaches() {
+ private Collection<CacheInfo> getCaches() throws Exception {
@SuppressWarnings("unchecked")
- Map<String, CacheInfo> caches = (Map<String, CacheInfo>) listCaches.apply(new ConfigResource());
+ Map<String, CacheInfo> caches =
+ (Map<String, CacheInfo>) listCaches.apply(new ConfigResource()).value();
for (Map.Entry<String, CacheInfo> entry : caches.entrySet()) {
CacheInfo cache = entry.getValue();
cache.name = entry.getKey();
diff --git a/java/com/google/gerrit/sshd/commands/ShowConnections.java b/java/com/google/gerrit/sshd/commands/ShowConnections.java
index 52e824b483..d27136469b 100644
--- a/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -44,6 +44,7 @@ import org.apache.sshd.common.io.mina.MinaSession;
import org.apache.sshd.common.io.nio2.Nio2Acceptor;
import org.apache.sshd.common.session.helpers.AbstractSession;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
import org.kohsuke.args4j.Option;
/** Show the current SSH connections. */
@@ -71,7 +72,7 @@ final class ShowConnections extends SshCommand {
private int columns = 80;
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
String s = env.getEnv().get(Environment.ENV_COLUMNS);
if (s != null && !s.isEmpty()) {
try {
@@ -80,7 +81,7 @@ final class ShowConnections extends SshCommand {
columns = 80;
}
}
- super.start(env);
+ super.start(channel, env);
}
@Override
@@ -149,7 +150,7 @@ final class ShowConnections extends SshCommand {
}
stdout.print("--\n");
- stdout.print("SSHD Backend: " + getBackend() + "\n");
+ stdout.print(String.format(" %d connections; SSHD Backend: %s\n", list.size(), getBackend()));
}
private String getBackend() {
diff --git a/java/com/google/gerrit/sshd/commands/ShowQueue.java b/java/com/google/gerrit/sshd/commands/ShowQueue.java
index 04d06eebb9..779f2df591 100644
--- a/java/com/google/gerrit/sshd/commands/ShowQueue.java
+++ b/java/com/google/gerrit/sshd/commands/ShowQueue.java
@@ -40,6 +40,7 @@ import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
import org.kohsuke.args4j.Option;
/** Display the current work queue. */
@@ -70,7 +71,7 @@ final class ShowQueue extends SshCommand {
private int maxCommandWidth;
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
String s = env.getEnv().get(Environment.ENV_COLUMNS);
if (s != null && !s.isEmpty()) {
try {
@@ -79,7 +80,7 @@ final class ShowQueue extends SshCommand {
columns = 80;
}
}
- super.start(env);
+ super.start(channel, env);
}
@Override
@@ -95,11 +96,13 @@ final class ShowQueue extends SshCommand {
List<TaskInfo> tasks;
try {
- tasks = listTasks.apply(new ConfigResource());
+ tasks = listTasks.apply(new ConfigResource()).value();
} catch (AuthException e) {
throw die(e);
} catch (PermissionBackendException e) {
throw new Failure(1, "permission backend unavailable", e);
+ } catch (Exception e) {
+ throw new Failure(1, "unavailable", e);
}
boolean viewAll = permissionBackend.user(currentUser).testOrFalse(GlobalPermission.VIEW_QUEUE);
diff --git a/java/com/google/gerrit/sshd/commands/StreamEvents.java b/java/com/google/gerrit/sshd/commands/StreamEvents.java
index ffd98d575c..45540a0a76 100644
--- a/java/com/google/gerrit/sshd/commands/StreamEvents.java
+++ b/java/com/google/gerrit/sshd/commands/StreamEvents.java
@@ -16,26 +16,22 @@ package com.google.gerrit.sshd.commands;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.common.base.Supplier;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.RegistrationHandle;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.events.Event;
+import com.google.gerrit.server.events.EventGson;
import com.google.gerrit.server.events.EventTypes;
-import com.google.gerrit.server.events.ProjectNameKeySerializer;
-import com.google.gerrit.server.events.SupplierSerializer;
import com.google.gerrit.server.events.UserScopedEventListener;
import com.google.gerrit.server.git.WorkQueue.CancelableRunnable;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.StreamCommandExecutor;
import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
import com.google.inject.Inject;
import java.io.IOException;
import java.io.PrintWriter;
@@ -45,11 +41,12 @@ import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.channel.ChannelSession;
import org.kohsuke.args4j.Option;
@RequiresCapability(GlobalCapability.STREAM_EVENTS)
@CommandMetaData(name = "stream-events", description = "Monitor events occurring in real time")
-final class StreamEvents extends BaseCommand {
+public final class StreamEvents extends BaseCommand {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/** Maximum number of events that may be queued up for each connection. */
@@ -71,11 +68,11 @@ final class StreamEvents extends BaseCommand {
@Inject @StreamCommandExecutor private ScheduledThreadPoolExecutor pool;
+ @Inject @EventGson private Gson gson;
+
/** Queue of events to stream to the connected user. */
private final LinkedBlockingQueue<Event> queue = new LinkedBlockingQueue<>(MAX_EVENTS);
- private Gson gson;
-
private RegistrationHandle eventListenerRegistration;
/** Special event to notify clients they missed other events. */
@@ -91,29 +88,6 @@ final class StreamEvents extends BaseCommand {
EventTypes.register(DroppedOutputEvent.TYPE, DroppedOutputEvent.class);
}
- private final CancelableRunnable writer =
- new CancelableRunnable() {
- @Override
- public void run() {
- writeEvents();
- }
-
- @Override
- public void cancel() {
- onExit(0);
- }
-
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder();
- b.append("Stream Events");
- if (currentUser.getUserName().isPresent()) {
- b.append(" (").append(currentUser.getUserName().get()).append(")");
- }
- return b.toString();
- }
- };
-
/** True if {@link DroppedOutputEvent} needs to be sent. */
private volatile boolean dropped;
@@ -131,10 +105,8 @@ final class StreamEvents extends BaseCommand {
*/
private Future<?> task;
- private PrintWriter stdout;
-
@Override
- public void start(Environment env) throws IOException {
+ public void start(ChannelSession channel, Environment env) throws IOException {
try {
parseCommandLine();
} catch (UnloggedFailure e) {
@@ -148,7 +120,30 @@ final class StreamEvents extends BaseCommand {
return;
}
- stdout = toPrintWriter(out);
+ PrintWriter stdout = toPrintWriter(out);
+ CancelableRunnable writer =
+ new CancelableRunnable() {
+ @Override
+ public void run() {
+ writeEvents(this, stdout);
+ }
+
+ @Override
+ public void cancel() {
+ onExit(0);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("Stream Events");
+ if (currentUser.getUserName().isPresent()) {
+ b.append(" (").append(currentUser.getUserName().get()).append(")");
+ }
+ return b.toString();
+ }
+ };
+
eventListenerRegistration =
eventListeners.add(
"gerrit",
@@ -156,7 +151,7 @@ final class StreamEvents extends BaseCommand {
@Override
public void onEvent(Event event) {
if (subscribedToEvents.isEmpty() || subscribedToEvents.contains(event.getType())) {
- offer(event);
+ offer(writer, event);
}
}
@@ -165,12 +160,6 @@ final class StreamEvents extends BaseCommand {
return currentUser;
}
});
-
- gson =
- new GsonBuilder()
- .registerTypeAdapter(Supplier.class, new SupplierSerializer())
- .registerTypeAdapter(Project.NameKey.class, new ProjectNameKeySerializer())
- .create();
}
private void removeEventListenerRegistration() {
@@ -191,7 +180,7 @@ final class StreamEvents extends BaseCommand {
}
@Override
- public void destroy() {
+ public void destroy(ChannelSession channel) {
removeEventListenerRegistration();
final boolean exit;
@@ -209,7 +198,7 @@ final class StreamEvents extends BaseCommand {
}
}
- private void offer(Event event) {
+ private void offer(CancelableRunnable writer, Event event) {
synchronized (taskLock) {
if (!queue.offer(event)) {
dropped = true;
@@ -231,7 +220,7 @@ final class StreamEvents extends BaseCommand {
}
}
- private void writeEvents() {
+ private void writeEvents(CancelableRunnable writer, PrintWriter stdout) {
int processed = 0;
while (processed < BATCH_SIZE) {
@@ -241,13 +230,13 @@ final class StreamEvents extends BaseCommand {
// accepting output. Either way terminate this instance.
//
removeEventListenerRegistration();
- flush();
+ flush(stdout);
onExit(0);
return;
}
if (dropped) {
- write(new DroppedOutputEvent());
+ write(stdout, new DroppedOutputEvent());
dropped = false;
}
@@ -256,11 +245,11 @@ final class StreamEvents extends BaseCommand {
break;
}
- write(event);
+ write(stdout, event);
processed++;
}
- flush();
+ flush(stdout);
if (BATCH_SIZE <= processed) {
// We processed the limit, but more might remain in the queue.
@@ -273,7 +262,7 @@ final class StreamEvents extends BaseCommand {
}
}
- private void write(Object message) {
+ private void write(PrintWriter stdout, Object message) {
String msg = null;
try {
msg = gson.toJson(message) + "\n";
@@ -287,7 +276,7 @@ final class StreamEvents extends BaseCommand {
}
}
- private void flush() {
+ private void flush(PrintWriter stdout) {
synchronized (stdout) {
stdout.flush();
}
diff --git a/java/com/google/gerrit/sshd/commands/Upload.java b/java/com/google/gerrit/sshd/commands/Upload.java
index e195098a64..b80b879f58 100644
--- a/java/com/google/gerrit/sshd/commands/Upload.java
+++ b/java/com/google/gerrit/sshd/commands/Upload.java
@@ -14,22 +14,29 @@
package com.google.gerrit.sshd.commands;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
+import com.google.gerrit.server.RequestInfo;
+import com.google.gerrit.server.RequestListener;
+import com.google.gerrit.server.git.PermissionAwareRepositoryManager;
+import com.google.gerrit.server.git.TracingHook;
import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.UploadPackInitializer;
+import com.google.gerrit.server.git.UsersSelfAdvertiseRefsHook;
import com.google.gerrit.server.git.validators.UploadValidationException;
import com.google.gerrit.server.git.validators.UploadValidators;
+import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.sshd.AbstractGitCommand;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.List;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.transport.PostUploadHook;
import org.eclipse.jgit.transport.PostUploadHookChain;
@@ -43,8 +50,10 @@ final class Upload extends AbstractGitCommand {
@Inject private DynamicSet<PreUploadHook> preUploadHooks;
@Inject private DynamicSet<PostUploadHook> postUploadHooks;
@Inject private DynamicSet<UploadPackInitializer> uploadPackInitializers;
+ @Inject private PluginSetContext<RequestListener> requestListeners;
@Inject private UploadValidators.Factory uploadValidatorsFactory;
@Inject private PermissionBackend permissionBackend;
+ @Inject private UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook;
private PackStatistics stats;
@@ -53,7 +62,6 @@ final class Upload extends AbstractGitCommand {
PermissionBackend.ForProject perm =
permissionBackend.user(user).project(projectState.getNameKey());
try {
-
perm.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
throw new Failure(1, "fatal: upload-pack not permitted on this server", e);
@@ -61,20 +69,35 @@ final class Upload extends AbstractGitCommand {
throw new Failure(1, "fatal: unable to check permissions ", e);
}
- final UploadPack up = new UploadPack(repo);
- up.setAdvertiseRefsHook(new DefaultAdvertiseRefsHook(perm, RefFilterOptions.defaults()));
+ Repository permissionAwareRepo = PermissionAwareRepositoryManager.wrap(repo, perm);
+ UploadPack up = new UploadPack(permissionAwareRepo);
+
up.setPackConfig(config.getPackConfig());
up.setTimeout(config.getTimeout());
up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
+ if (projectState.isAllUsers()) {
+ up.setAdvertiseRefsHook(usersSelfAdvertiseRefsHook);
+ }
+ if (extraParameters != null) {
+ up.setExtraParameters(ImmutableList.copyOf(extraParameters));
+ }
List<PreUploadHook> allPreUploadHooks = Lists.newArrayList(preUploadHooks);
allPreUploadHooks.add(
- uploadValidatorsFactory.create(project, repo, session.getRemoteAddressAsString()));
+ uploadValidatorsFactory.create(
+ project, permissionAwareRepo, session.getRemoteAddressAsString()));
up.setPreUploadHook(PreUploadHookChain.newChain(allPreUploadHooks));
for (UploadPackInitializer initializer : uploadPackInitializers) {
initializer.init(projectState.getNameKey(), up);
}
- try {
+ try (TraceContext traceContext = TraceContext.open();
+ TracingHook tracingHook = new TracingHook()) {
+ RequestInfo requestInfo =
+ RequestInfo.builder(RequestInfo.RequestType.GIT_UPLOAD, user, traceContext)
+ .project(projectState.getNameKey())
+ .build();
+ requestListeners.runEach(l -> l.onRequest(requestInfo));
+ up.setProtocolV2Hook(tracingHook);
up.upload(in, out, err);
session.setPeerAgent(up.getPeerUserAgent());
stats = up.getStatistics();
diff --git a/java/com/google/gerrit/sshd/commands/UploadArchive.java b/java/com/google/gerrit/sshd/commands/UploadArchive.java
index 24f82a78ab..c25a1a8f66 100644
--- a/java/com/google/gerrit/sshd/commands/UploadArchive.java
+++ b/java/com/google/gerrit/sshd/commands/UploadArchive.java
@@ -139,7 +139,7 @@ public class UploadArchive extends AbstractGitCommand {
PacketLineIn packetIn = new PacketLineIn(in);
for (; ; ) {
String s = packetIn.readString();
- if (s == PacketLineIn.END) {
+ if (PacketLineIn.isEnd(s)) {
break;
}
if (!s.startsWith(argCmd)) {
diff --git a/java/com/google/gerrit/testing/AssertableExecutorService.java b/java/com/google/gerrit/testing/AssertableExecutorService.java
new file mode 100644
index 0000000000..18ac2e9773
--- /dev/null
+++ b/java/com/google/gerrit/testing/AssertableExecutorService.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.testing;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Forwards all calls to a direct executor making it so that the submitted {@link Runnable}s run
+ * synchronously. Holds a count of the number of tasks that were executed.
+ */
+public class AssertableExecutorService extends ForwardingExecutorService {
+
+ private final ExecutorService delegate = MoreExecutors.newDirectExecutorService();
+ private final AtomicInteger numInteractions = new AtomicInteger();
+
+ @Override
+ protected ExecutorService delegate() {
+ return delegate;
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ numInteractions.incrementAndGet();
+ return super.submit(task);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ numInteractions.incrementAndGet();
+ return super.submit(task);
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ numInteractions.incrementAndGet();
+ return super.submit(task, result);
+ }
+
+ /** Asserts and resets the number of executions this executor observed. */
+ public void assertInteractions(int expectedNumInteractions) {
+ assertWithMessage("expectedRunnablesSubmittedOnExecutor")
+ .that(numInteractions.get())
+ .isEqualTo(expectedNumInteractions);
+ numInteractions.set(0);
+ }
+}
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index dc1c150853..c610d073e2 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -3,19 +3,19 @@ load("@rules_java//java:defs.bzl", "java_library")
java_library(
name = "gerrit-test-util",
testonly = True,
- srcs = glob(["**/*.java"]),
+ srcs = glob(
+ ["**/*.java"],
+ exclude = ["AssertableExecutorService.java"],
+ ),
visibility = ["//visibility:public"],
exports = [
- "//lib/easymock",
- "//lib/powermock:powermock-api-easymock",
- "//lib/powermock:powermock-api-support",
- "//lib/powermock:powermock-core",
- "//lib/powermock:powermock-module-junit4",
- "//lib/powermock:powermock-module-junit4-common",
+ "//lib:junit",
+ "//lib/mockito",
],
deps = [
+ "//java/com/google/gerrit/acceptance/testsuite/project",
"//java/com/google/gerrit/common:annotations",
- "//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/gpg",
@@ -24,8 +24,6 @@ java_library(
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/mail",
"//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/pgm/init",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server:module",
"//java/com/google/gerrit/server/api",
@@ -38,16 +36,28 @@ java_library(
"//java/com/google/gerrit/server/util/time",
"//lib:guava",
"//lib:h2",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib:junit",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/log:impl-log4j",
"//lib/log:log4j",
"//lib/truth",
],
)
+
+java_library(
+ # This can't be part of gerrit-test-util because of https://github.com/google/guava/issues/2837
+ name = "assertable-executor",
+ testonly = True,
+ srcs = ["AssertableExecutorService.java"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//lib:guava",
+ "//lib/truth",
+ ],
+)
diff --git a/java/com/google/gerrit/testing/ConfigSuite.java b/java/com/google/gerrit/testing/ConfigSuite.java
index 0fcca240a9..d7ae397a21 100644
--- a/java/com/google/gerrit/testing/ConfigSuite.java
+++ b/java/com/google/gerrit/testing/ConfigSuite.java
@@ -57,6 +57,7 @@ import org.junit.runners.model.InitializationError;
* public static Config firstConfig() {
* Config cfg = new Config();
* cfg.setString("gerrit", null, "testValue", "a");
+ * return cfg;
* }
* }
*
@@ -65,6 +66,7 @@ import org.junit.runners.model.InitializationError;
* public static Config secondConfig() {
* Config cfg = new Config();
* cfg.setString("gerrit", null, "testValue", "b");
+ * return cfg;
* }
*
* {@literal @}Test
diff --git a/java/com/google/gerrit/testing/FakeAccountCache.java b/java/com/google/gerrit/testing/FakeAccountCache.java
index b99a32d7df..0178c7211b 100644
--- a/java/com/google/gerrit/testing/FakeAccountCache.java
+++ b/java/com/google/gerrit/testing/FakeAccountCache.java
@@ -17,11 +17,9 @@ package com.google.gerrit.testing;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.util.time.TimeUtil;
import java.util.HashMap;
import java.util.Map;
@@ -42,7 +40,10 @@ public class FakeAccountCache implements AccountCache {
if (state != null) {
return state;
}
- return newState(new Account(accountId, TimeUtil.nowTs()));
+ return newState(
+ Account.builder(accountId, TimeUtil.nowTs())
+ .setMetaId("1234567812345678123456781234567812345678")
+ .build());
}
@Override
@@ -74,10 +75,10 @@ public class FakeAccountCache implements AccountCache {
public synchronized void put(Account account) {
AccountState state = newState(account);
- byId.put(account.getId(), state);
+ byId.put(account.id(), state);
}
private static AccountState newState(Account account) {
- return AccountState.forAccount(new AllUsersName(AllUsersNameProvider.DEFAULT), account);
+ return AccountState.forAccount(account);
}
}
diff --git a/java/com/google/gerrit/testing/GerritServerTests.java b/java/com/google/gerrit/testing/GerritServerTests.java
index 9a922d6449..ad985b6976 100644
--- a/java/com/google/gerrit/testing/GerritServerTests.java
+++ b/java/com/google/gerrit/testing/GerritServerTests.java
@@ -21,7 +21,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
@RunWith(ConfigSuite.class)
-public class GerritServerTests extends GerritBaseTests {
+public class GerritServerTests {
@ConfigSuite.Parameter public Config config;
@ConfigSuite.Name private String configName;
diff --git a/java/com/google/gerrit/testing/GerritBaseTests.java b/java/com/google/gerrit/testing/GerritTestName.java
index 500d791685..d287837430 100644
--- a/java/com/google/gerrit/testing/GerritBaseTests.java
+++ b/java/com/google/gerrit/testing/GerritTestName.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2015 The Android Open Source Project
+// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -16,23 +16,21 @@ package com.google.gerrit.testing;
import com.google.common.base.CharMatcher;
import org.junit.BeforeClass;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.rules.ExpectedException;
import org.junit.rules.TestName;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
-@Ignore
-public abstract class GerritBaseTests {
- @Rule public ExpectedException exception = ExpectedException.none();
- @Rule public final TestName testName = new TestName();
+public class GerritTestName implements TestRule {
+ private final TestName delegate = new TestName();
@BeforeClass
public static void beforeClassTest() {
TestLoggingActivator.configureLogging();
}
- protected String getSanitizedMethodName() {
- String name = testName.getMethodName().toLowerCase();
+ public String getSanitizedMethodName() {
+ String name = delegate.getMethodName().toLowerCase();
name =
CharMatcher.inRange('a', 'z')
.or(CharMatcher.inRange('A', 'Z'))
@@ -42,4 +40,9 @@ public abstract class GerritBaseTests {
name = CharMatcher.is('_').trimTrailingFrom(name);
return name;
}
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return delegate.apply(base, description);
+ }
}
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index 83b9e2b6e0..abb5955ecc 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -19,10 +19,13 @@ import static com.google.inject.Scopes.SINGLETON;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.MoreExecutors;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperationsImpl;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.gerrit.gpg.GpgModule;
+import com.google.gerrit.index.IndexType;
import com.google.gerrit.index.SchemaDefinitions;
import com.google.gerrit.index.project.ProjectSchemaDefinitions;
import com.google.gerrit.metrics.DisabledMetricMaker;
@@ -61,7 +64,6 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.PerThreadRequestScope;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
import com.google.gerrit.server.index.account.AllAccountsIndexer;
import com.google.gerrit.server.index.change.AllChangesIndexer;
@@ -82,6 +84,7 @@ import com.google.gerrit.server.securestore.DefaultSecureStore;
import com.google.gerrit.server.securestore.SecureStore;
import com.google.gerrit.server.ssh.NoSshKeyCache;
import com.google.gerrit.server.submit.LocalMergeSuperSetComputation;
+import com.google.gerrit.server.util.ReplicaUtil;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@@ -172,8 +175,8 @@ public class InMemoryModule extends FactoryModule {
bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
- // TODO(dborowitz): Use Jimfs. The biggest blocker is that JGit does not support Path-based
- // Configs, only FileBasedConfig.
+ // It would be nice to use Jimfs for the SitePath, but the biggest blocker is that JGit does not
+ // support Path-based Configs, only FileBasedConfig.
bind(Path.class).annotatedWith(SitePath.class).toInstance(Paths.get("."));
bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(cfg);
bind(GerritOptions.class).toInstance(new GerritOptions(false, false, false));
@@ -218,28 +221,19 @@ public class InMemoryModule extends FactoryModule {
bind(AllChangesIndexer.class).toProvider(Providers.of(null));
bind(AllGroupsIndexer.class).toProvider(Providers.of(null));
- IndexType indexType = null;
- try {
- indexType = cfg.getEnum("index", null, "type", IndexType.LUCENE);
- } catch (IllegalArgumentException e) {
- // Custom index type, caller must provide their own module.
- }
- if (indexType != null) {
- switch (indexType) {
- case LUCENE:
- install(luceneIndexModule());
- break;
- case ELASTICSEARCH:
- install(elasticIndexModule());
- break;
- default:
- throw new ProvisionException("index type unsupported in tests: " + indexType);
- }
+ IndexType indexType = new IndexType(cfg.getString("index", null, "type"));
+ // For custom index types, callers must provide their own module.
+ if (indexType.isLucene()) {
+ install(luceneIndexModule());
+ } else if (indexType.isElasticsearch()) {
+ install(elasticIndexModule());
}
bind(ServerInformationImpl.class);
bind(ServerInformation.class).to(ServerInformationImpl.class);
install(new RestApiModule());
install(new DefaultProjectNameLockManager.Module());
+
+ bind(ProjectOperations.class).to(ProjectOperationsImpl.class);
}
/** Copy of SchemaModule with a slightly different server ID provider. */
@@ -301,11 +295,10 @@ public class InMemoryModule extends FactoryModule {
private Module indexModule(String moduleClassName) {
try {
- boolean slave = cfg.getBoolean("container", "slave", false);
Class<?> clazz = Class.forName(moduleClassName);
Method m =
clazz.getMethod("singleVersionWithExplicitVersions", Map.class, int.class, boolean.class);
- return (Module) m.invoke(null, getSingleSchemaVersions(), 0, slave);
+ return (Module) m.invoke(null, getSingleSchemaVersions(), 0, ReplicaUtil.isReplica(cfg));
} catch (ClassNotFoundException
| SecurityException
| NoSuchMethodException
diff --git a/java/com/google/gerrit/testing/InMemoryRepositoryManager.java b/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
index e44d8d381e..09ae115dae 100644
--- a/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
+++ b/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
@@ -16,7 +16,7 @@ package com.google.gerrit.testing;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.RepositoryCaseMismatchException;
import com.google.inject.Inject;
@@ -103,7 +103,7 @@ public class InMemoryRepositoryManager implements GitRepositoryManager {
public synchronized SortedSet<Project.NameKey> list() {
SortedSet<Project.NameKey> names = Sets.newTreeSet();
for (DfsRepository repo : repos.values()) {
- names.add(new Project.NameKey(repo.getDescription().getRepositoryName()));
+ names.add(Project.nameKey(repo.getDescription().getRepositoryName()));
}
return ImmutableSortedSet.copyOf(names);
}
diff --git a/java/com/google/gerrit/testing/InMemoryTestEnvironment.java b/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
index 05672aa044..44d5cead97 100644
--- a/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
+++ b/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
@@ -14,8 +14,8 @@
package com.google.gerrit.testing;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
diff --git a/java/com/google/gerrit/testing/IndexConfig.java b/java/com/google/gerrit/testing/IndexConfig.java
index 9cace8879b..21c49dda06 100644
--- a/java/com/google/gerrit/testing/IndexConfig.java
+++ b/java/com/google/gerrit/testing/IndexConfig.java
@@ -23,6 +23,7 @@ public class IndexConfig {
public static Config createFromExistingConfig(Config cfg) {
cfg.setInt("index", null, "maxPages", 10);
+ cfg.setBoolean("index", null, "reindexAfterRefUpdate", false);
cfg.setString("trackingid", "query-bug", "footer", "Bug:");
cfg.setString("trackingid", "query-bug", "match", "QUERY\\d{2,8}");
cfg.setString("trackingid", "query-bug", "system", "querytests");
diff --git a/java/com/google/gerrit/testing/TestChanges.java b/java/com/google/gerrit/testing/TestChanges.java
index d0a939da61..b795c5bb41 100644
--- a/java/com/google/gerrit/testing/TestChanges.java
+++ b/java/com/google/gerrit/testing/TestChanges.java
@@ -17,14 +17,13 @@ package com.google.gerrit.testing;
import static com.google.common.base.MoreObjects.firstNonNull;
import com.google.common.collect.Ordering;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetInfo;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.AbstractChangeNotes;
@@ -52,13 +51,13 @@ public class TestChanges {
}
public static Change newChange(Project.NameKey project, Account.Id userId, int id) {
- Change.Id changeId = new Change.Id(id);
+ Change.Id changeId = Change.id(id);
Change c =
new Change(
- new Change.Key("Iabcd1234abcd1234abcd1234abcd1234abcd1234"),
+ Change.key("Iabcd1234abcd1234abcd1234abcd1234abcd1234"),
changeId,
userId,
- new Branch.NameKey(project, "master"),
+ BranchNameKey.create(project, "master"),
TimeUtil.nowTs());
incrementPatchSet(c);
return c;
@@ -69,11 +68,12 @@ public class TestChanges {
}
public static PatchSet newPatchSet(PatchSet.Id id, String revision, Account.Id userId) {
- PatchSet ps = new PatchSet(id);
- ps.setRevision(new RevId(revision));
- ps.setUploader(userId);
- ps.setCreatedOn(TimeUtil.nowTs());
- return ps;
+ return PatchSet.builder()
+ .id(id)
+ .commitId(ObjectId.fromString(revision))
+ .uploader(userId)
+ .createdOn(TimeUtil.nowTs())
+ .build();
}
public static ChangeUpdate newUpdate(
@@ -115,11 +115,11 @@ public class TestChanges {
.author(ident)
.committer(ident)
.message(firstNonNull(c.getSubject(), "Test change"));
- Ref parent = repo.exactRef(c.getDest().get());
+ Ref parent = repo.exactRef(c.getDest().branch());
if (parent != null) {
cb.parent(tr.getRevWalk().parseCommit(parent.getObjectId()));
}
- update.setBranch(c.getDest().get());
+ update.setBranch(c.getDest().branch());
update.setChangeId(c.getKey().get());
update.setCommit(tr.getRevWalk(), cb.create());
return update;
@@ -129,7 +129,7 @@ public class TestChanges {
public static void incrementPatchSet(Change change) {
PatchSet.Id curr = change.currentPatchSetId();
PatchSetInfo ps =
- new PatchSetInfo(new PatchSet.Id(change.getId(), curr != null ? curr.get() + 1 : 1));
+ new PatchSetInfo(PatchSet.id(change.getId(), curr != null ? curr.get() + 1 : 1));
ps.setSubject("Change subject");
change.setCurrentPatchSet(ps);
}
diff --git a/java/com/google/gerrit/testing/TestCommentHelper.java b/java/com/google/gerrit/testing/TestCommentHelper.java
new file mode 100644
index 0000000000..b72cca7f01
--- /dev/null
+++ b/java/com/google/gerrit/testing/TestCommentHelper.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.testing;
+
+import static java.util.stream.Collectors.toList;
+
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.changes.DraftInput;
+import com.google.gerrit.extensions.client.Comment;
+import com.google.gerrit.extensions.client.Comment.Range;
+import com.google.gerrit.extensions.client.Side;
+import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.inject.Inject;
+import java.util.Collection;
+
+/** Test helper for dealing with comments/drafts. */
+public class TestCommentHelper {
+ private final GerritApi gApi;
+
+ @Inject
+ public TestCommentHelper(GerritApi gerritApi) {
+ gApi = gerritApi;
+ }
+
+ public DraftInput newDraft(String message) {
+ return populate(new DraftInput(), "file", message);
+ }
+
+ public DraftInput newDraft(String path, Side side, int line, String message) {
+ DraftInput d = new DraftInput();
+ return populate(d, path, side, line, message);
+ }
+
+ public void addDraft(String changeId, String revId, DraftInput in) throws Exception {
+ gApi.changes().id(changeId).revision(revId).createDraft(in).get();
+ }
+
+ public Collection<CommentInfo> getPublishedComments(String changeId) throws Exception {
+ return gApi.changes().id(changeId).comments().values().stream()
+ .flatMap(Collection::stream)
+ .collect(toList());
+ }
+
+ public static <C extends Comment> C populate(C c, String path, String message) {
+ return populate(c, path, createLineRange(), message);
+ }
+
+ private static <C extends Comment> C populate(C c, String path, Range range, String message) {
+ int line = range.startLine;
+ c.path = path;
+ c.side = Side.REVISION;
+ c.parent = null;
+ c.line = line != 0 ? line : null;
+ c.message = message;
+ c.unresolved = false;
+ if (line != 0) c.range = range;
+ return c;
+ }
+
+ private static <C extends Comment> C populate(
+ C c, String path, Side side, Range range, String message) {
+ int line = range.startLine;
+ c.path = path;
+ c.side = side;
+ c.parent = null;
+ c.line = line != 0 ? line : null;
+ c.message = message;
+ c.unresolved = false;
+ if (line != 0) c.range = range;
+ return c;
+ }
+
+ private static <C extends Comment> C populate(
+ C c, String path, Side side, int line, String message) {
+ return populate(c, path, side, createLineRange(line), message);
+ }
+
+ private static Range createLineRange() {
+ Range range = new Range();
+ range.startLine = 0;
+ range.startCharacter = 1;
+ range.endLine = 0;
+ range.endCharacter = 5;
+ return range;
+ }
+
+ private static Range createLineRange(int line) {
+ Range range = new Range();
+ range.startLine = line;
+ range.startCharacter = 1;
+ range.endLine = line;
+ range.endCharacter = 5;
+ return range;
+ }
+}
diff --git a/java/com/google/gerrit/truth/BUILD b/java/com/google/gerrit/truth/BUILD
index f21e3c9548..b0dfef0cb1 100644
--- a/java/com/google/gerrit/truth/BUILD
+++ b/java/com/google/gerrit/truth/BUILD
@@ -8,6 +8,7 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//lib:guava",
+ "//lib:jgit",
"//lib/truth",
],
)
diff --git a/java/com/google/gerrit/truth/CacheStatsSubject.java b/java/com/google/gerrit/truth/CacheStatsSubject.java
index f1a9393a50..ff943341cf 100644
--- a/java/com/google/gerrit/truth/CacheStatsSubject.java
+++ b/java/com/google/gerrit/truth/CacheStatsSubject.java
@@ -24,7 +24,7 @@ import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.UsedAt.Project;
@UsedAt(Project.PLUGINS_ALL)
-public class CacheStatsSubject extends Subject<CacheStatsSubject, CacheStats> {
+public class CacheStatsSubject extends Subject {
public static CacheStatsSubject assertThat(CacheStats stats) {
return assertAbout(CacheStatsSubject::new).that(stats);
}
@@ -39,10 +39,12 @@ public class CacheStatsSubject extends Subject<CacheStatsSubject, CacheStats> {
other.evictionCount());
}
+ private final CacheStats stats;
private CacheStats start = new CacheStats(0, 0, 0, 0, 0, 0);
private CacheStatsSubject(FailureMetadata failureMetadata, CacheStats stats) {
super(failureMetadata, stats);
+ this.stats = stats;
}
public CacheStatsSubject since(CacheStats start) {
@@ -52,11 +54,11 @@ public class CacheStatsSubject extends Subject<CacheStatsSubject, CacheStats> {
public void hasHitCount(int expectedHitCount) {
isNotNull();
- check("hitCount()").that(actual().minus(start).hitCount()).isEqualTo(expectedHitCount);
+ check("hitCount()").that(stats.minus(start).hitCount()).isEqualTo(expectedHitCount);
}
public void hasMissCount(int expectedMissCount) {
isNotNull();
- check("missCount()").that(actual().minus(start).missCount()).isEqualTo(expectedMissCount);
+ check("missCount()").that(stats.minus(start).missCount()).isEqualTo(expectedMissCount);
}
}
diff --git a/java/com/google/gerrit/truth/ConfigSubject.java b/java/com/google/gerrit/truth/ConfigSubject.java
new file mode 100644
index 0000000000..dd55b7115e
--- /dev/null
+++ b/java/com/google/gerrit/truth/ConfigSubject.java
@@ -0,0 +1,129 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.truth;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.truth.BooleanSubject;
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.IntegerSubject;
+import com.google.common.truth.IterableSubject;
+import com.google.common.truth.LongSubject;
+import com.google.common.truth.MultimapSubject;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.Subject;
+import com.google.gerrit.common.Nullable;
+import java.util.Arrays;
+import org.eclipse.jgit.lib.Config;
+
+public class ConfigSubject extends Subject {
+ public static ConfigSubject assertThat(Config config) {
+ return assertAbout(ConfigSubject::new).that(config);
+ }
+
+ private final Config config;
+
+ private ConfigSubject(FailureMetadata metadata, Config actual) {
+ super(metadata, actual);
+ this.config = actual;
+ }
+
+ public IterableSubject sections() {
+ isNotNull();
+ return check("getSections()").that(config.getSections());
+ }
+
+ public IterableSubject subsections(String section) {
+ requireNonNull(section);
+ isNotNull();
+ return check("getSubsections(%s)", section).that(config.getSubsections(section));
+ }
+
+ public MultimapSubject sectionValues(String section) {
+ requireNonNull(section);
+ return sectionValuesImpl(section, null);
+ }
+
+ public MultimapSubject subsectionValues(String section, String subsection) {
+ requireNonNull(section);
+ requireNonNull(subsection);
+ return sectionValuesImpl(section, subsection);
+ }
+
+ private MultimapSubject sectionValuesImpl(String section, @Nullable String subsection) {
+ isNotNull();
+ ImmutableListMultimap.Builder<String, String> b = ImmutableListMultimap.builder();
+ config
+ .getNames(section, subsection, true)
+ .forEach(
+ n ->
+ Arrays.stream(config.getStringList(section, subsection, n))
+ .forEach(v -> b.put(n, v)));
+ return check("getSection(%s, %s)", section, subsection).that(b.build());
+ }
+
+ public void isEmpty() {
+ sections().isEmpty();
+ }
+
+ public StringSubject text() {
+ isNotNull();
+ return check("toText()").that(config.toText());
+ }
+
+ public IterableSubject stringValues(String section, @Nullable String subsection, String name) {
+ requireNonNull(section);
+ requireNonNull(name);
+ isNotNull();
+ return check("getStringList(%s, %s, %s)", section, subsection, name)
+ .that(Arrays.asList(config.getStringList(section, subsection, name)));
+ }
+
+ public StringSubject stringValue(String section, @Nullable String subsection, String name) {
+ requireNonNull(section);
+ requireNonNull(name);
+ isNotNull();
+ return check("getString(%s, %s, %s)", section, subsection, name)
+ .that(config.getString(section, subsection, name));
+ }
+
+ public IntegerSubject intValue(
+ String section, @Nullable String subsection, String name, int defaultValue) {
+ requireNonNull(section);
+ requireNonNull(name);
+ isNotNull();
+ return check("getInt(%s, %s, %s, %s)", section, subsection, name, defaultValue)
+ .that(config.getInt(section, subsection, name, defaultValue));
+ }
+
+ public LongSubject longValue(String section, String subsection, String name, long defaultValue) {
+ requireNonNull(section);
+ requireNonNull(name);
+ isNotNull();
+ return check("getLong(%s, %s, %s, %s)", section, subsection, name, defaultValue)
+ .that(config.getLong(section, subsection, name, defaultValue));
+ }
+
+ public BooleanSubject booleanValue(
+ String section, String subsection, String name, boolean defaultValue) {
+ requireNonNull(section);
+ requireNonNull(name);
+ isNotNull();
+ return check("getBoolean(%s, %s, %s, %s)", section, subsection, name, defaultValue)
+ .that(config.getBoolean(section, subsection, name, defaultValue));
+ }
+}
diff --git a/java/com/google/gerrit/truth/ListSubject.java b/java/com/google/gerrit/truth/ListSubject.java
index 9a839dda89..9f939640bb 100644
--- a/java/com/google/gerrit/truth/ListSubject.java
+++ b/java/com/google/gerrit/truth/ListSubject.java
@@ -27,12 +27,13 @@ import com.google.common.truth.Subject;
import java.util.List;
import java.util.function.BiFunction;
-public class ListSubject<S extends Subject<S, E>, E> extends IterableSubject {
+public class ListSubject<S extends Subject, E> extends IterableSubject {
- private final BiFunction<StandardSubjectBuilder, E, S> elementSubjectCreator;
+ private final List<E> list;
+ private final BiFunction<StandardSubjectBuilder, ? super E, ? extends S> elementSubjectCreator;
- public static <S extends Subject<S, E>, E> ListSubject<S, E> assertThat(
- List<E> list, Subject.Factory<S, E> subjectFactory) {
+ public static <S extends Subject, E> ListSubject<S, E> assertThat(
+ List<E> list, Subject.Factory<? extends S, ? super E> subjectFactory) {
return assertAbout(elements()).thatCustom(list, subjectFactory);
}
@@ -43,15 +44,15 @@ public class ListSubject<S extends Subject<S, E>, E> extends IterableSubject {
private ListSubject(
FailureMetadata failureMetadata,
List<E> list,
- BiFunction<StandardSubjectBuilder, E, S> elementSubjectCreator) {
+ BiFunction<StandardSubjectBuilder, ? super E, ? extends S> elementSubjectCreator) {
super(failureMetadata, list);
+ this.list = list;
this.elementSubjectCreator = elementSubjectCreator;
}
public S element(int index) {
checkArgument(index >= 0, "index(%s) must be >= 0", index);
isNotNull();
- List<E> list = getActualList();
if (index >= list.size()) {
failWithoutActual(fact("expected to have element at index", index));
}
@@ -61,43 +62,29 @@ public class ListSubject<S extends Subject<S, E>, E> extends IterableSubject {
public S onlyElement() {
isNotNull();
hasSize(1);
- List<E> list = getActualList();
return elementSubjectCreator.apply(check("onlyElement()"), Iterables.getOnlyElement(list));
}
public S lastElement() {
isNotNull();
isNotEmpty();
- List<E> list = getActualList();
return elementSubjectCreator.apply(check("lastElement()"), Iterables.getLast(list));
}
- @SuppressWarnings("unchecked")
- private List<E> getActualList() {
- // The constructor only accepts lists. -> Casting is appropriate.
- return (List<E>) actual();
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public ListSubject<S, E> named(String s, Object... objects) {
- // This object is returned which is of type ListSubject. -> Casting is appropriate.
- return (ListSubject<S, E>) super.named(s, objects);
- }
-
public static class ListSubjectBuilder extends CustomSubjectBuilder {
ListSubjectBuilder(FailureMetadata failureMetadata) {
super(failureMetadata);
}
- public <S extends Subject<S, E>, E> ListSubject<S, E> thatCustom(
- List<E> list, Subject.Factory<S, E> subjectFactory) {
+ public <S extends Subject, E> ListSubject<S, E> thatCustom(
+ List<E> list, Subject.Factory<? extends S, ? super E> subjectFactory) {
return that(list, (builder, element) -> builder.about(subjectFactory).that(element));
}
- public <S extends Subject<S, E>, E> ListSubject<S, E> that(
- List<E> list, BiFunction<StandardSubjectBuilder, E, S> elementSubjectCreator) {
+ public <S extends Subject, E> ListSubject<S, E> that(
+ List<E> list,
+ BiFunction<StandardSubjectBuilder, ? super E, ? extends S> elementSubjectCreator) {
return new ListSubject<>(metadata(), list, elementSubjectCreator);
}
}
diff --git a/java/com/google/gerrit/truth/OptionalSubject.java b/java/com/google/gerrit/truth/OptionalSubject.java
index b5fc5d0472..2023765d17 100644
--- a/java/com/google/gerrit/truth/OptionalSubject.java
+++ b/java/com/google/gerrit/truth/OptionalSubject.java
@@ -18,39 +18,24 @@ import static com.google.common.truth.Fact.fact;
import static com.google.common.truth.Truth.assertAbout;
import com.google.common.truth.CustomSubjectBuilder;
-import com.google.common.truth.DefaultSubject;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.StandardSubjectBuilder;
import com.google.common.truth.Subject;
import java.util.Optional;
import java.util.function.BiFunction;
-import java.util.function.Function;
-public class OptionalSubject<S extends Subject<S, ? super T>, T>
- extends Subject<OptionalSubject<S, T>, Optional<T>> {
+public class OptionalSubject<S extends Subject, T> extends Subject {
+ private final Optional<T> optional;
private final BiFunction<StandardSubjectBuilder, ? super T, ? extends S> valueSubjectCreator;
- // TODO(aliceks): Remove when all relevant usages are adapted to new check()/factory approach.
- public static <S extends Subject<S, T>, T> OptionalSubject<S, T> assertThat(
- Optional<T> optional, Function<? super T, ? extends S> elementAssertThatFunction) {
- Subject.Factory<S, T> valueSubjectFactory =
- (metadata, value) -> elementAssertThatFunction.apply(value);
- return assertThat(optional, valueSubjectFactory);
- }
-
- public static <S extends Subject<S, T>, T> OptionalSubject<S, T> assertThat(
- Optional<T> optional, Subject.Factory<S, T> valueSubjectFactory) {
+ public static <S extends Subject, T> OptionalSubject<S, T> assertThat(
+ Optional<T> optional, Subject.Factory<? extends S, ? super T> valueSubjectFactory) {
return assertAbout(optionals()).thatCustom(optional, valueSubjectFactory);
}
- public static OptionalSubject<DefaultSubject, ?> assertThat(Optional<?> optional) {
- // Unfortunately, we need to cast to DefaultSubject as StandardSubjectBuilder#that
- // only returns Subject<DefaultSubject, Object>. There shouldn't be a way
- // for that method not to return a DefaultSubject because the generic type
- // definitions of a Subject are quite strict.
- return assertAbout(optionals())
- .that(optional, (builder, value) -> (DefaultSubject) builder.that(value));
+ public static OptionalSubject<Subject, ?> assertThat(Optional<?> optional) {
+ return assertAbout(optionals()).that(optional);
}
public static CustomSubjectBuilder.Factory<OptionalSubjectBuilder> optionals() {
@@ -62,12 +47,12 @@ public class OptionalSubject<S extends Subject<S, ? super T>, T>
Optional<T> optional,
BiFunction<StandardSubjectBuilder, ? super T, ? extends S> valueSubjectCreator) {
super(failureMetadata, optional);
+ this.optional = optional;
this.valueSubjectCreator = valueSubjectCreator;
}
public void isPresent() {
isNotNull();
- Optional<T> optional = actual();
if (!optional.isPresent()) {
failWithoutActual(fact("expected to have", "value"));
}
@@ -75,7 +60,6 @@ public class OptionalSubject<S extends Subject<S, ? super T>, T>
public void isAbsent() {
isNotNull();
- Optional<T> optional = actual();
if (optional.isPresent()) {
failWithoutActual(fact("expected not to have", "value"));
}
@@ -88,7 +72,6 @@ public class OptionalSubject<S extends Subject<S, ? super T>, T>
public S value() {
isNotNull();
isPresent();
- Optional<T> optional = actual();
return valueSubjectCreator.apply(check("value()"), optional.get());
}
@@ -98,16 +81,16 @@ public class OptionalSubject<S extends Subject<S, ? super T>, T>
super(failureMetadata);
}
- public <S extends Subject<S, T>, T> OptionalSubject<S, T> thatCustom(
- Optional<T> optional, Subject.Factory<S, T> valueSubjectFactory) {
+ public <S extends Subject, T> OptionalSubject<S, T> thatCustom(
+ Optional<T> optional, Subject.Factory<? extends S, ? super T> valueSubjectFactory) {
return that(optional, (builder, value) -> builder.about(valueSubjectFactory).that(value));
}
- public OptionalSubject<DefaultSubject, ?> that(Optional<?> optional) {
- return that(optional, (builder, value) -> (DefaultSubject) builder.that(value));
+ public OptionalSubject<Subject, ?> that(Optional<?> optional) {
+ return that(optional, StandardSubjectBuilder::that);
}
- public <S extends Subject<S, ? super T>, T> OptionalSubject<S, T> that(
+ public <S extends Subject, T> OptionalSubject<S, T> that(
Optional<T> optional,
BiFunction<StandardSubjectBuilder, ? super T, ? extends S> valueSubjectCreator) {
return new OptionalSubject<>(metadata(), optional, valueSubjectCreator);
diff --git a/java/com/google/gerrit/util/cli/CmdLineParser.java b/java/com/google/gerrit/util/cli/CmdLineParser.java
index 1c16133a6d..162f324f3d 100644
--- a/java/com/google/gerrit/util/cli/CmdLineParser.java
+++ b/java/com/google/gerrit/util/cli/CmdLineParser.java
@@ -35,13 +35,14 @@
package com.google.gerrit.util.cli;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.util.cli.Localizable.localizable;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
-import com.google.common.collect.MultimapBuilder;
import com.google.common.flogger.FluentLogger;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -313,23 +314,13 @@ public class CmdLineParser {
parser.parseArgument(tmp.toArray(new String[tmp.size()]));
}
- public void parseOptionMap(Map<String, String[]> parameters) throws CmdLineException {
- ListMultimap<String, String> map = MultimapBuilder.hashKeys().arrayListValues().build();
- for (Map.Entry<String, String[]> ent : parameters.entrySet()) {
- for (String val : ent.getValue()) {
- map.put(ent.getKey(), val);
- }
- }
- parseOptionMap(map);
- }
-
public void parseOptionMap(ListMultimap<String, String> params) throws CmdLineException {
logger.atFinest().log("Command-line parameters: %s", params.keySet());
List<String> tmp = Lists.newArrayListWithCapacity(2 * params.size());
for (String key : params.keySet()) {
String name = makeOption(key);
- if (isBoolean(name)) {
+ if (isBooleanOption(name)) {
boolean on = false;
for (String value : params.get(key)) {
on = toBoolean(key, value);
@@ -347,10 +338,6 @@ public class CmdLineParser {
parser.parseArgument(tmp.toArray(new String[tmp.size()]));
}
- public boolean isBoolean(String name) {
- return findHandler(makeOption(name)) instanceof BooleanOptionHandler;
- }
-
public void parseWithPrefix(String prefix, Object bean) {
parser.parseWithPrefix(prefix, bean);
}
@@ -359,6 +346,10 @@ public class CmdLineParser {
parser.addOptionsWithMetRequirements();
}
+ private boolean isBooleanOption(String name) {
+ return findHandler(makeOption(name)) instanceof BooleanOptionHandler;
+ }
+
private String makeOption(String name) {
if (!name.startsWith("-")) {
if (name.length() == 1) {
@@ -422,7 +413,8 @@ public class CmdLineParser {
private static Option newPrefixedOption(String prefix, Option o) {
requireNonNull(prefix);
checkArgument(o.name().startsWith("-"), "Option name must start with '-': %s", o);
- String[] aliases = Arrays.stream(o.aliases()).map(prefix::concat).toArray(String[]::new);
+ ImmutableList<String> aliases =
+ Arrays.stream(o.aliases()).map(prefix::concat).collect(toImmutableList());
return OptionUtil.newOption(
prefix + o.name(),
aliases,
@@ -432,8 +424,8 @@ public class CmdLineParser {
false,
o.hidden(),
o.handler(),
- o.depends(),
- new String[0]);
+ ImmutableList.copyOf(o.depends()),
+ ImmutableList.of());
}
public class MyParser extends org.kohsuke.args4j.CmdLineParser {
@@ -625,15 +617,15 @@ public class CmdLineParser {
private Option newHelpOption() {
return OptionUtil.newOption(
"--help",
- new String[] {"-h"},
+ ImmutableList.of("-h"),
"display this help text",
"",
false,
false,
false,
BooleanOptionHandler.class,
- new String[0],
- new String[0]);
+ ImmutableList.of(),
+ ImmutableList.of());
}
private boolean isHandlerSpecified(OptionDef option) {
diff --git a/java/com/google/gerrit/util/cli/OptionUtil.java b/java/com/google/gerrit/util/cli/OptionUtil.java
index 1125a0d367..68cd7170cd 100644
--- a/java/com/google/gerrit/util/cli/OptionUtil.java
+++ b/java/com/google/gerrit/util/cli/OptionUtil.java
@@ -15,6 +15,7 @@
package com.google.gerrit.util.cli;
import com.google.auto.value.AutoAnnotation;
+import com.google.common.collect.ImmutableList;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.spi.OptionHandler;
@@ -24,15 +25,15 @@ public class OptionUtil {
@SuppressWarnings("rawtypes")
public static Option newOption(
String name,
- String[] aliases,
+ ImmutableList<String> aliases,
String usage,
String metaVar,
boolean required,
boolean help,
boolean hidden,
Class<? extends OptionHandler> handler,
- String[] depends,
- String[] forbids) {
+ ImmutableList<String> depends,
+ ImmutableList<String> forbids) {
return new AutoAnnotation_OptionUtil_newOption(
name, aliases, usage, metaVar, required, help, hidden, handler, depends, forbids);
}
diff --git a/java/com/google/gwtorm/BUILD b/java/com/google/gwtorm/BUILD
deleted file mode 100644
index baf7a8cb76..0000000000
--- a/java/com/google/gwtorm/BUILD
+++ /dev/null
@@ -1,7 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-
-java_library(
- name = "gwtorm",
- srcs = glob(["**/*.java"]),
- visibility = ["//visibility:public"],
-)
diff --git a/java/com/google/gwtorm/client/CompoundKey.java b/java/com/google/gwtorm/client/CompoundKey.java
deleted file mode 100644
index 1c66d186c3..0000000000
--- a/java/com/google/gwtorm/client/CompoundKey.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gwtorm.client;
-
-import java.io.Serializable;
-
-/**
- * Abstract key type composed of other keys.
- *
- * <p>Applications should subclass this type to create their own entity-specific key classes.
- *
- * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
- */
-@SuppressWarnings("serial")
-public abstract class CompoundKey<P extends Key<?>> implements Key<P>, Serializable {
- /** @return the member key components, minus the parent key. */
- public abstract Key<?>[] members();
-
- /** @return the parent key instance; null if this is a root level key. */
- @Override
- public P getParentKey() {
- return null;
- }
-
- @Override
- public int hashCode() {
- int hc = 0;
- if (getParentKey() != null) {
- hc = getParentKey().hashCode();
- }
- for (final Key<?> k : members()) {
- hc *= 31;
- hc += k.hashCode();
- }
- return hc;
- }
-
- @Override
- public boolean equals(final Object b) {
- if (b == null || b.getClass() != getClass()) {
- return false;
- }
-
- final CompoundKey<P> q = cast(b);
- if (getParentKey() != null && !getParentKey().equals(q.getParentKey())) {
- return false;
- }
-
- final Key<?>[] aMembers = members();
- final Key<?>[] bMembers = q.members();
- if (aMembers.length != bMembers.length) {
- return false;
- }
- for (int i = 0; i < aMembers.length; i++) {
- if (!aMembers[i].equals(bMembers[i])) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- final StringBuffer r = new StringBuffer();
- boolean first = true;
- if (getParentKey() != null) {
- r.append(KeyUtil.encode(getParentKey().toString()));
- first = false;
- }
- for (final Key<?> k : members()) {
- if (!first) {
- r.append(',');
- }
- r.append(KeyUtil.encode(k.toString()));
- first = false;
- }
- return r.toString();
- }
-
- @Override
- public void fromString(final String in) {
- final String[] parts = in.split(",");
- int p = 0;
- if (getParentKey() != null) {
- getParentKey().fromString(KeyUtil.decode(parts[p++]));
- }
- for (final Key<?> k : members()) {
- k.fromString(KeyUtil.decode(parts[p++]));
- }
- }
-
- @SuppressWarnings("unchecked")
- private static <A extends Key<?>> CompoundKey<A> cast(final Object b) {
- return (CompoundKey<A>) b;
- }
-}
diff --git a/java/com/google/gwtorm/client/IntKey.java b/java/com/google/gwtorm/client/IntKey.java
deleted file mode 100644
index 08c90e08f8..0000000000
--- a/java/com/google/gwtorm/client/IntKey.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gwtorm.client;
-
-import java.io.Serializable;
-
-/**
- * Abstract key type using a single integer value.
- *
- * <p>Applications should subclass this type to create their own entity-specific key classes.
- *
- * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
- */
-@SuppressWarnings("serial")
-public abstract class IntKey<P extends Key<?>> implements Key<P>, Serializable {
- /** @return id of the entity instance. */
- public abstract int get();
-
- /** @param newValue the new value of this key. */
- protected abstract void set(int newValue);
-
- /** @return the parent key instance; null if this is a root level key. */
- @Override
- public P getParentKey() {
- return null;
- }
-
- @Override
- public int hashCode() {
- int hc = get();
- if (getParentKey() != null) {
- hc *= 31;
- hc += getParentKey().hashCode();
- }
- return hc;
- }
-
- @Override
- public boolean equals(final Object b) {
- if (b == null || b.getClass() != getClass()) {
- return false;
- }
-
- final IntKey<P> q = cast(b);
- return get() == q.get() && KeyUtil.eq(getParentKey(), q.getParentKey());
- }
-
- @Override
- public String toString() {
- final StringBuffer r = new StringBuffer();
- if (getParentKey() != null) {
- r.append(getParentKey().toString());
- r.append(',');
- }
- r.append(get());
- return r.toString();
- }
-
- @Override
- public void fromString(final String in) {
- set(Integer.parseInt(KeyUtil.parseFromString(getParentKey(), in)));
- }
-
- @SuppressWarnings("unchecked")
- private static <A extends Key<?>> IntKey<A> cast(final Object b) {
- return (IntKey<A>) b;
- }
-}
diff --git a/java/com/google/gwtorm/client/Key.java b/java/com/google/gwtorm/client/Key.java
deleted file mode 100644
index 69a224864e..0000000000
--- a/java/com/google/gwtorm/client/Key.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gwtorm.client;
-
-/**
- * Generic type for an entity key.
- *
- * <p>Although not required, entities should make their primary key type implement this interface,
- * permitting traversal up through the containment hierarchy of the entity keys.
- *
- * @param <P> type of the parent key. If no parent, use {@link Key} itself.
- */
-public interface Key<P extends Key<?>> {
- /**
- * Get the parent key instance.
- *
- * @return the parent key; null if this entity key is a root-level key.
- */
- public P getParentKey();
-
- @Override
- public int hashCode();
-
- @Override
- public boolean equals(Object o);
-
- /** @return the key, encoded in a string format . */
- @Override
- public String toString();
-
- /** Reset this key instance to represent the data in the supplied string. */
- public void fromString(String in);
-}
diff --git a/java/com/google/gwtorm/client/KeyUtil.java b/java/com/google/gwtorm/client/KeyUtil.java
deleted file mode 100644
index e236d370cb..0000000000
--- a/java/com/google/gwtorm/client/KeyUtil.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gwtorm.client;
-
-/** Common utility functions for {@link Key} implementors. */
-public class KeyUtil {
- private static Encoder ENCODER_IMPL = new StandardKeyEncoder();
-
- /**
- * Determine if two keys are equal, supporting null references.
- *
- * @param <T> type of the key entity.
- * @param a first key to test; may be null.
- * @param b second key to test; may be null.
- * @return true if both <code>a</code> and <code>b</code> are null, or if both are not-null and
- * <code>a.equals(b)</code> is true. Otherwise false.
- */
- public static <T extends Key<?>> boolean eq(final T a, final T b) {
- if (a == b) {
- return true;
- }
- if (a == null || b == null) {
- return false;
- }
- return a.equals(b);
- }
-
- /**
- * Encode a string to be safe for use within a URL like string.
- *
- * <p>The returned encoded string has URL component characters escaped with hex escapes (e.g. ' '
- * is '+' and '%' is '%25'). The special character '/' is left literal. The comma character (',')
- * is always encoded, permitting multiple encoded string values to be joined together safely.
- *
- * @param e the string to encode, must not be null.
- * @return the encoded string.
- */
- public static String encode(final String e) {
- return ENCODER_IMPL.encode(e);
- }
-
- /**
- * Decode a string previously encoded by {@link #encode(String)}.
- *
- * @param e the string to decode, must not be null.
- * @return the decoded string.
- */
- public static String decode(final String e) {
- return ENCODER_IMPL.decode(e);
- }
-
- /**
- * Split a string along the last comma and parse into the parent.
- *
- * @param parent parent key; <code>parent.fromString(in[0..comma])</code>.
- * @param in the input string.
- * @return text (if any) after the last comma in the input.
- */
- public static String parseFromString(final Key<?> parent, final String in) {
- final int comma = in.lastIndexOf(',');
- if (comma < 0 && parent == null) {
- return decode(in);
- }
- if (comma < 0 && parent != null) {
- throw new IllegalArgumentException("Not enough components: " + in);
- }
- assert (parent != null);
- parent.fromString(in.substring(0, comma));
- return decode(in.substring(comma + 1));
- }
-
- public abstract static class Encoder {
- public abstract String encode(String e);
-
- public abstract String decode(String e);
- }
-
- private KeyUtil() {}
-}
diff --git a/java/com/google/gwtorm/client/StringKey.java b/java/com/google/gwtorm/client/StringKey.java
deleted file mode 100644
index e56661ff1c..0000000000
--- a/java/com/google/gwtorm/client/StringKey.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gwtorm.client;
-
-import java.io.Serializable;
-
-/**
- * Abstract key type using a single string value.
- *
- * <p>Applications should subclass this type to create their own entity-specific key classes.
- *
- * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
- */
-@SuppressWarnings("serial")
-public abstract class StringKey<P extends Key<?>>
- implements Key<P>, Serializable, Comparable<StringKey<?>> {
- /** @return name of the entity instance. */
- public abstract String get();
-
- /** @param newValue the new value of this key. */
- protected abstract void set(String newValue);
-
- /** @return the parent key instance; null if this is a root level key. */
- @Override
- public P getParentKey() {
- return null;
- }
-
- @Override
- public int hashCode() {
- int hc = get() != null ? get().hashCode() : 0;
- if (getParentKey() != null) {
- hc *= 31;
- hc += getParentKey().hashCode();
- }
- return hc;
- }
-
- @Override
- public boolean equals(final Object b) {
- if (b == null || get() == null || b.getClass() != getClass()) {
- return false;
- }
-
- final StringKey<P> q = cast(b);
- return get().equals(q.get()) && KeyUtil.eq(getParentKey(), q.getParentKey());
- }
-
- @Override
- public int compareTo(final StringKey<?> other) {
- return get().compareTo(other.get());
- }
-
- @Override
- public String toString() {
- final StringBuffer r = new StringBuffer();
- if (getParentKey() != null) {
- r.append(getParentKey().toString());
- r.append(',');
- }
- r.append(KeyUtil.encode(get()));
- return r.toString();
- }
-
- @Override
- public void fromString(final String in) {
- set(KeyUtil.parseFromString(getParentKey(), in));
- }
-
- @SuppressWarnings("unchecked")
- private static <A extends Key<?>> StringKey<A> cast(final Object b) {
- return (StringKey<A>) b;
- }
-}
diff --git a/java/gerrit/AbstractCommitUserIdentityPredicate.java b/java/gerrit/AbstractCommitUserIdentityPredicate.java
index 1bfc95c46c..bd8cf1af44 100644
--- a/java/gerrit/AbstractCommitUserIdentityPredicate.java
+++ b/java/gerrit/AbstractCommitUserIdentityPredicate.java
@@ -15,7 +15,7 @@
package gerrit;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.rules.PrologEnvironment;
import com.googlecode.prolog_cafe.exceptions.PrologException;
diff --git a/java/gerrit/BUILD b/java/gerrit/BUILD
index d7e23069ba..7dbf751494 100644
--- a/java/gerrit/BUILD
+++ b/java/gerrit/BUILD
@@ -6,12 +6,11 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/exceptions",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
+ "//lib:jgit",
"//lib/flogger:api",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/prolog:runtime",
"@guava//jar",
],
diff --git a/java/gerrit/PRED__load_commit_labels_1.java b/java/gerrit/PRED__load_commit_labels_1.java
index 693c89e4d6..90a2cbf1d4 100644
--- a/java/gerrit/PRED__load_commit_labels_1.java
+++ b/java/gerrit/PRED__load_commit_labels_1.java
@@ -4,7 +4,7 @@ package gerrit;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.rules.StoredValues;
import com.googlecode.prolog_cafe.exceptions.PrologException;
@@ -38,16 +38,15 @@ class PRED__load_commit_labels_1 extends Predicate.P1 {
LabelTypes types = cd.getLabelTypes();
for (PatchSetApproval a : cd.currentApprovals()) {
- LabelType t = types.byLabel(a.getLabelId());
+ LabelType t = types.byLabel(a.labelId());
if (t == null) {
continue;
}
StructureTerm labelTerm =
- new StructureTerm(
- sym_label, SymbolTerm.intern(t.getName()), new IntegerTerm(a.getValue()));
+ new StructureTerm(sym_label, SymbolTerm.intern(t.getName()), new IntegerTerm(a.value()));
- StructureTerm userTerm = new StructureTerm(sym_user, new IntegerTerm(a.getAccountId().get()));
+ StructureTerm userTerm = new StructureTerm(sym_user, new IntegerTerm(a.accountId().get()));
listHead = new ListTerm(new StructureTerm(sym_commit_label, labelTerm, userTerm), listHead);
}
diff --git a/java/gerrit/PRED_change_branch_1.java b/java/gerrit/PRED_change_branch_1.java
index 0a7bb74184..4501169c88 100644
--- a/java/gerrit/PRED_change_branch_1.java
+++ b/java/gerrit/PRED_change_branch_1.java
@@ -14,7 +14,7 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.server.rules.StoredValues;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.Operation;
@@ -34,9 +34,9 @@ public class PRED_change_branch_1 extends Predicate.P1 {
engine.setB0();
Term a1 = arg1.dereference();
- Branch.NameKey name = StoredValues.getChange(engine).getDest();
+ BranchNameKey name = StoredValues.getChange(engine).getDest();
- if (!a1.unify(SymbolTerm.create(name.get()), engine.trail)) {
+ if (!a1.unify(SymbolTerm.create(name.branch()), engine.trail)) {
return engine.fail();
}
return cont;
diff --git a/java/gerrit/PRED_change_owner_1.java b/java/gerrit/PRED_change_owner_1.java
index 937b761979..d42c0e1e38 100644
--- a/java/gerrit/PRED_change_owner_1.java
+++ b/java/gerrit/PRED_change_owner_1.java
@@ -14,7 +14,7 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.rules.StoredValues;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
diff --git a/java/gerrit/PRED_change_project_1.java b/java/gerrit/PRED_change_project_1.java
index 28e637a40e..a973e1c93a 100644
--- a/java/gerrit/PRED_change_project_1.java
+++ b/java/gerrit/PRED_change_project_1.java
@@ -14,7 +14,7 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.rules.StoredValues;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.Operation;
diff --git a/java/gerrit/PRED_change_topic_1.java b/java/gerrit/PRED_change_topic_1.java
index 564878fe02..11d737ab54 100644
--- a/java/gerrit/PRED_change_topic_1.java
+++ b/java/gerrit/PRED_change_topic_1.java
@@ -14,7 +14,7 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.rules.StoredValues;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.Operation;
diff --git a/java/gerrit/PRED_commit_delta_4.java b/java/gerrit/PRED_commit_delta_4.java
index d2634ea81a..6e971fcc71 100644
--- a/java/gerrit/PRED_commit_delta_4.java
+++ b/java/gerrit/PRED_commit_delta_4.java
@@ -14,7 +14,7 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListEntry;
import com.google.gerrit.server.rules.StoredValues;
diff --git a/java/gerrit/PRED_commit_edits_2.java b/java/gerrit/PRED_commit_edits_2.java
index 6ca533874d..12e7086fab 100644
--- a/java/gerrit/PRED_commit_edits_2.java
+++ b/java/gerrit/PRED_commit_edits_2.java
@@ -14,7 +14,7 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListEntry;
import com.google.gerrit.server.patch.Text;
diff --git a/java/gerrit/PRED_commit_stats_3.java b/java/gerrit/PRED_commit_stats_3.java
index c1666d849a..286bc2c42e 100644
--- a/java/gerrit/PRED_commit_stats_3.java
+++ b/java/gerrit/PRED_commit_stats_3.java
@@ -14,7 +14,7 @@
package gerrit;
-import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListEntry;
import com.google.gerrit.server.rules.StoredValues;
diff --git a/java/gerrit/PRED_uploader_1.java b/java/gerrit/PRED_uploader_1.java
index 029b84af09..681d86cace 100644
--- a/java/gerrit/PRED_uploader_1.java
+++ b/java/gerrit/PRED_uploader_1.java
@@ -15,8 +15,8 @@
package gerrit;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.server.rules.StoredValues;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
@@ -50,7 +50,7 @@ public class PRED_uploader_1 extends Predicate.P1 {
return engine.fail();
}
- Account.Id uploaderId = patchSet.getUploader();
+ Account.Id uploaderId = patchSet.uploader();
if (!a1.unify(new StructureTerm(user, new IntegerTerm(uploaderId.get())), engine.trail)) {
return engine.fail();
diff --git a/javatests/com/google/gerrit/acceptance/BUILD b/javatests/com/google/gerrit/acceptance/BUILD
index 54b3626b50..75c90f249e 100644
--- a/javatests/com/google/gerrit/acceptance/BUILD
+++ b/javatests/com/google/gerrit/acceptance/BUILD
@@ -7,8 +7,9 @@ junit_tests(
"//java/com/google/gerrit/acceptance:lib",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//java/com/google/gerrit/truth",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java b/javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java
index 69fdc7e6ba..3d17de06c9 100644
--- a/javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java
+++ b/javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java
@@ -15,17 +15,17 @@
package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.truth.ConfigSubject.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.File;
import java.nio.file.Files;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.util.FS;
import org.junit.Test;
-public class MergeableFileBasedConfigTest extends GerritBaseTests {
+public class MergeableFileBasedConfigTest {
@Test
public void mergeNull() throws Exception {
MergeableFileBasedConfig cfg = newConfig();
@@ -112,7 +112,7 @@ public class MergeableFileBasedConfigTest extends GerritBaseTests {
}
private void assertConfig(MergeableFileBasedConfig cfg, String expected) throws Exception {
- assertThat(cfg.toText()).isEqualTo(expected);
+ assertThat(cfg).text().isEqualTo(expected);
cfg.save();
assertThat(new String(Files.readAllBytes(cfg.getFile().toPath()), UTF_8)).isEqualTo(expected);
}
diff --git a/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java b/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java
index 53d8ef8799..1a09aa17f1 100644
--- a/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java
+++ b/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java
@@ -15,13 +15,17 @@
package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupIncludeCache;
@@ -31,12 +35,10 @@ import com.google.gerrit.server.index.account.AccountIndexer;
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.TestTimeUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
-import org.easymock.EasyMock;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -50,7 +52,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class ProjectResetterTest extends GerritBaseTests {
+public class ProjectResetterTest {
private InMemoryRepositoryManager repoManager;
private Project.NameKey project;
private Repository repo;
@@ -58,7 +60,7 @@ public class ProjectResetterTest extends GerritBaseTests {
@Before
public void setUp() throws Exception {
repoManager = new InMemoryRepositoryManager();
- project = new Project.NameKey("foo");
+ project = Project.nameKey("foo");
repo = repoManager.createRepository(project);
}
@@ -135,7 +137,7 @@ public class ProjectResetterTest extends GerritBaseTests {
@Test
public void onlyResetMatchingRefsMultipleProjects() throws Exception {
- Project.NameKey project2 = new Project.NameKey("bar");
+ Project.NameKey project2 = Project.nameKey("bar");
Repository repo2 = repoManager.createRepository(project2);
Ref matchingRefProject1 = createRef("refs/foo/test");
@@ -170,7 +172,7 @@ public class ProjectResetterTest extends GerritBaseTests {
@Test
public void onlyDeleteNewlyCreatedMatchingRefsMultipleProjects() throws Exception {
- Project.NameKey project2 = new Project.NameKey("bar");
+ Project.NameKey project2 = Project.nameKey("bar");
Repository repo2 = repoManager.createRepository(project2);
Ref matchingRefProject1;
@@ -216,14 +218,11 @@ public class ProjectResetterTest extends GerritBaseTests {
@Test
public void projectEvictionIfRefsMetaConfigIsReset() throws Exception {
- Project.NameKey project2 = new Project.NameKey("bar");
+ Project.NameKey project2 = Project.nameKey("bar");
Repository repo2 = repoManager.createRepository(project2);
Ref metaConfig = createRef(repo2, RefNames.REFS_CONFIG);
- ProjectCache projectCache = EasyMock.createNiceMock(ProjectCache.class);
- projectCache.evict(project2);
- EasyMock.expectLastCall();
- EasyMock.replay(projectCache);
+ ProjectCache projectCache = mock(ProjectCache.class);
Ref nonMetaConfig = createRef("refs/heads/master");
@@ -234,18 +233,15 @@ public class ProjectResetterTest extends GerritBaseTests {
updateRef(repo2, metaConfig);
}
- EasyMock.verify(projectCache);
+ verify(projectCache, only()).evict(project2);
}
@Test
public void projectEvictionIfRefsMetaConfigIsDeleted() throws Exception {
- Project.NameKey project2 = new Project.NameKey("bar");
+ Project.NameKey project2 = Project.nameKey("bar");
Repository repo2 = repoManager.createRepository(project2);
- ProjectCache projectCache = EasyMock.createNiceMock(ProjectCache.class);
- projectCache.evict(project2);
- EasyMock.expectLastCall();
- EasyMock.replay(projectCache);
+ ProjectCache projectCache = mock(ProjectCache.class);
try (ProjectResetter resetProject =
builder(null, null, null, null, null, null, projectCache)
@@ -254,28 +250,21 @@ public class ProjectResetterTest extends GerritBaseTests {
createRef(repo2, RefNames.REFS_CONFIG);
}
- EasyMock.verify(projectCache);
+ verify(projectCache, only()).evict(project2);
}
@Test
public void accountEvictionIfUserBranchIsReset() throws Exception {
- Account.Id accountId = new Account.Id(1);
- Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT);
+ Account.Id accountId = Account.id(1);
+ Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
Repository allUsersRepo = repoManager.createRepository(allUsers);
Ref userBranch = createRef(allUsersRepo, RefNames.refsUsers(accountId));
- AccountCache accountCache = EasyMock.createNiceMock(AccountCache.class);
- accountCache.evict(accountId);
- EasyMock.expectLastCall();
- EasyMock.replay(accountCache);
-
- AccountIndexer accountIndexer = EasyMock.createNiceMock(AccountIndexer.class);
- accountIndexer.index(accountId);
- EasyMock.expectLastCall();
- EasyMock.replay(accountIndexer);
+ AccountCache accountCache = mock(AccountCache.class);
+ AccountIndexer accountIndexer = mock(AccountIndexer.class);
// Non-user branch because it's not in All-Users.
- Ref nonUserBranch = createRef(RefNames.refsUsers(new Account.Id(2)));
+ Ref nonUserBranch = createRef(RefNames.refsUsers(Account.id(2)));
try (ProjectResetter resetProject =
builder(null, accountCache, accountIndexer, null, null, null, null)
@@ -284,63 +273,47 @@ public class ProjectResetterTest extends GerritBaseTests {
updateRef(allUsersRepo, userBranch);
}
- EasyMock.verify(accountCache, accountIndexer);
+ verify(accountCache, only()).evict(accountId);
+ verify(accountIndexer, only()).index(accountId);
}
@Test
public void accountEvictionIfUserBranchIsDeleted() throws Exception {
- Account.Id accountId = new Account.Id(1);
- Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT);
+ Account.Id accountId = Account.id(1);
+ Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
Repository allUsersRepo = repoManager.createRepository(allUsers);
- AccountCache accountCache = EasyMock.createNiceMock(AccountCache.class);
- accountCache.evict(accountId);
- EasyMock.expectLastCall();
- EasyMock.replay(accountCache);
-
- AccountIndexer accountIndexer = EasyMock.createNiceMock(AccountIndexer.class);
- accountIndexer.index(accountId);
- EasyMock.expectLastCall();
- EasyMock.replay(accountIndexer);
+ AccountCache accountCache = mock(AccountCache.class);
+ AccountIndexer accountIndexer = mock(AccountIndexer.class);
try (ProjectResetter resetProject =
builder(null, accountCache, accountIndexer, null, null, null, null)
.build(new ProjectResetter.Config().reset(project).reset(allUsers))) {
// Non-user branch because it's not in All-Users.
- createRef(RefNames.refsUsers(new Account.Id(2)));
+ createRef(RefNames.refsUsers(Account.id(2)));
createRef(allUsersRepo, RefNames.refsUsers(accountId));
}
- EasyMock.verify(accountCache, accountIndexer);
+ verify(accountCache, only()).evict(accountId);
+ verify(accountIndexer, only()).index(accountId);
}
@Test
public void accountEvictionIfExternalIdsBranchIsReset() throws Exception {
- Account.Id accountId = new Account.Id(1);
- Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT);
+ Account.Id accountId = Account.id(1);
+ Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
Repository allUsersRepo = repoManager.createRepository(allUsers);
Ref externalIds = createRef(allUsersRepo, RefNames.REFS_EXTERNAL_IDS);
createRef(allUsersRepo, RefNames.refsUsers(accountId));
- Account.Id accountId2 = new Account.Id(2);
-
- AccountCache accountCache = EasyMock.createNiceMock(AccountCache.class);
- accountCache.evict(accountId);
- EasyMock.expectLastCall();
- accountCache.evict(accountId2);
- EasyMock.expectLastCall();
- EasyMock.replay(accountCache);
+ Account.Id accountId2 = Account.id(2);
- AccountIndexer accountIndexer = EasyMock.createNiceMock(AccountIndexer.class);
- accountIndexer.index(accountId);
- EasyMock.expectLastCall();
- accountIndexer.index(accountId2);
- EasyMock.expectLastCall();
- EasyMock.replay(accountIndexer);
+ AccountCache accountCache = mock(AccountCache.class);
+ AccountIndexer accountIndexer = mock(AccountIndexer.class);
// Non-user branch because it's not in All-Users.
- Ref nonUserBranch = createRef(RefNames.refsUsers(new Account.Id(3)));
+ Ref nonUserBranch = createRef(RefNames.refsUsers(Account.id(3)));
try (ProjectResetter resetProject =
builder(null, accountCache, accountIndexer, null, null, null, null)
@@ -350,34 +323,27 @@ public class ProjectResetterTest extends GerritBaseTests {
createRef(allUsersRepo, RefNames.refsUsers(accountId2));
}
- EasyMock.verify(accountCache, accountIndexer);
+ verify(accountCache).evict(accountId);
+ verify(accountCache).evict(accountId2);
+ verify(accountIndexer).index(accountId);
+ verify(accountIndexer).index(accountId2);
+ verifyNoMoreInteractions(accountCache, accountIndexer);
}
@Test
public void accountEvictionIfExternalIdsBranchIsDeleted() throws Exception {
- Account.Id accountId = new Account.Id(1);
- Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT);
+ Account.Id accountId = Account.id(1);
+ Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
Repository allUsersRepo = repoManager.createRepository(allUsers);
createRef(allUsersRepo, RefNames.refsUsers(accountId));
- Account.Id accountId2 = new Account.Id(2);
+ Account.Id accountId2 = Account.id(2);
- AccountCache accountCache = EasyMock.createNiceMock(AccountCache.class);
- accountCache.evict(accountId);
- EasyMock.expectLastCall();
- accountCache.evict(accountId2);
- EasyMock.expectLastCall();
- EasyMock.replay(accountCache);
-
- AccountIndexer accountIndexer = EasyMock.createNiceMock(AccountIndexer.class);
- accountIndexer.index(accountId);
- EasyMock.expectLastCall();
- accountIndexer.index(accountId2);
- EasyMock.expectLastCall();
- EasyMock.replay(accountIndexer);
+ AccountCache accountCache = mock(AccountCache.class);
+ AccountIndexer accountIndexer = mock(AccountIndexer.class);
// Non-user branch because it's not in All-Users.
- Ref nonUserBranch = createRef(RefNames.refsUsers(new Account.Id(3)));
+ Ref nonUserBranch = createRef(RefNames.refsUsers(Account.id(3)));
try (ProjectResetter resetProject =
builder(null, accountCache, accountIndexer, null, null, null, null)
@@ -387,19 +353,20 @@ public class ProjectResetterTest extends GerritBaseTests {
createRef(allUsersRepo, RefNames.refsUsers(accountId2));
}
- EasyMock.verify(accountCache, accountIndexer);
+ verify(accountCache).evict(accountId);
+ verify(accountCache).evict(accountId2);
+ verify(accountIndexer).index(accountId);
+ verify(accountIndexer).index(accountId2);
+ verifyNoMoreInteractions(accountCache, accountIndexer);
}
@Test
public void accountEvictionFromAccountCreatorIfUserBranchIsDeleted() throws Exception {
- Account.Id accountId = new Account.Id(1);
- Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT);
+ Account.Id accountId = Account.id(1);
+ Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
Repository allUsersRepo = repoManager.createRepository(allUsers);
- AccountCreator accountCreator = EasyMock.createNiceMock(AccountCreator.class);
- accountCreator.evict(ImmutableSet.of(accountId));
- EasyMock.expectLastCall();
- EasyMock.replay(accountCreator);
+ AccountCreator accountCreator = mock(AccountCreator.class);
try (ProjectResetter resetProject =
builder(accountCreator, null, null, null, null, null, null)
@@ -407,29 +374,20 @@ public class ProjectResetterTest extends GerritBaseTests {
createRef(allUsersRepo, RefNames.refsUsers(accountId));
}
- EasyMock.verify(accountCreator);
+ verify(accountCreator, only()).evict(ImmutableSet.of(accountId));
}
@Test
public void groupEviction() throws Exception {
- AccountGroup.UUID uuid1 = new AccountGroup.UUID("abcd1");
- AccountGroup.UUID uuid2 = new AccountGroup.UUID("abcd2");
- AccountGroup.UUID uuid3 = new AccountGroup.UUID("abcd3");
- Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT);
+ AccountGroup.UUID uuid1 = AccountGroup.uuid("abcd1");
+ AccountGroup.UUID uuid2 = AccountGroup.uuid("abcd2");
+ AccountGroup.UUID uuid3 = AccountGroup.uuid("abcd3");
+ Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
Repository allUsersRepo = repoManager.createRepository(allUsers);
- GroupCache cache = EasyMock.createNiceMock(GroupCache.class);
- GroupIndexer indexer = EasyMock.createNiceMock(GroupIndexer.class);
- GroupIncludeCache includeCache = EasyMock.createNiceMock(GroupIncludeCache.class);
- cache.evict(uuid2);
- indexer.index(uuid2);
- includeCache.evictParentGroupsOf(uuid2);
- cache.evict(uuid3);
- indexer.index(uuid3);
- includeCache.evictParentGroupsOf(uuid3);
- EasyMock.expectLastCall();
-
- EasyMock.replay(cache, indexer);
+ GroupCache cache = mock(GroupCache.class);
+ GroupIndexer indexer = mock(GroupIndexer.class);
+ GroupIncludeCache includeCache = mock(GroupIncludeCache.class);
createRef(allUsersRepo, RefNames.refsGroups(uuid1));
Ref ref2 = createRef(allUsersRepo, RefNames.refsGroups(uuid2));
@@ -440,7 +398,13 @@ public class ProjectResetterTest extends GerritBaseTests {
createRef(allUsersRepo, RefNames.refsGroups(uuid3));
}
- EasyMock.verify(cache, indexer);
+ verify(cache).evict(uuid2);
+ verify(indexer).index(uuid2);
+ verify(includeCache).evictParentGroupsOf(uuid2);
+ verify(cache).evict(uuid3);
+ verify(indexer).index(uuid3);
+ verify(includeCache).evictParentGroupsOf(uuid3);
+ verifyNoMoreInteractions(cache, indexer, includeCache);
}
private Ref createRef(String ref) throws IOException {
diff --git a/javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java b/javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java
index bf387fdf12..448629c893 100644
--- a/javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java
+++ b/javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java
@@ -16,21 +16,19 @@ package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.UniversalGroupBackend;
import com.google.gerrit.server.group.testing.TestGroupBackend;
import com.google.inject.Inject;
import org.junit.Test;
public class TestGroupBackendTest extends AbstractDaemonTest {
- @Inject private DynamicSet<GroupBackend> groupBackends;
@Inject private UniversalGroupBackend universalGroupBackend;
+ @Inject private ExtensionRegistry extensionRegistry;
private final TestGroupBackend testGroupBackend = new TestGroupBackend();
- private final AccountGroup.UUID testUUID = new AccountGroup.UUID("testbackend:test");
+ private final AccountGroup.UUID testUUID = AccountGroup.uuid("testbackend:test");
@Test
public void handlesTestGroup() throws Exception {
@@ -39,17 +37,14 @@ public class TestGroupBackendTest extends AbstractDaemonTest {
@Test
public void universalGroupBackendHandlesTestGroup() throws Exception {
- RegistrationHandle registrationHandle = groupBackends.add("gerrit", testGroupBackend);
- try {
+ try (Registration registration = extensionRegistry.newRegistration().add(testGroupBackend)) {
assertThat(universalGroupBackend.handles(testUUID)).isTrue();
- } finally {
- registrationHandle.remove();
}
}
@Test
public void doesNotHandleLDAP() throws Exception {
- assertThat(testGroupBackend.handles(new AccountGroup.UUID("ldap:1234"))).isFalse();
+ assertThat(testGroupBackend.handles(AccountGroup.uuid("ldap:1234"))).isFalse();
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/annotation/BUILD b/javatests/com/google/gerrit/acceptance/annotation/BUILD
index 5476bb6baf..8dd99c9b77 100644
--- a/javatests/com/google/gerrit/acceptance/annotation/BUILD
+++ b/javatests/com/google/gerrit/acceptance/annotation/BUILD
@@ -4,4 +4,5 @@ acceptance_tests(
srcs = glob(["*.java"]),
group = "annotation",
labels = ["annotation"],
+ deps = ["//java/com/google/gerrit/server/util/time"],
)
diff --git a/javatests/com/google/gerrit/acceptance/annotation/UseClockStepTest.java b/javatests/com/google/gerrit/acceptance/annotation/UseClockStepTest.java
new file mode 100644
index 0000000000..ecfe3f58ba
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/annotation/UseClockStepTest.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.annotation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.server.util.time.TimeUtil;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+
+public class UseClockStepTest extends AbstractDaemonTest {
+ @Test
+ @UseClockStep
+ public void useClockStepWithDefaults() {
+ long firstTimestamp = TimeUtil.nowMs();
+ long secondTimestamp = TimeUtil.nowMs();
+ assertThat(secondTimestamp - firstTimestamp).isEqualTo(1000);
+ }
+
+ @Test
+ @UseClockStep(clockStepUnit = TimeUnit.MINUTES)
+ public void useClockStepWithTimeUnit() {
+ long firstTimestamp = TimeUtil.nowMs();
+ long secondTimestamp = TimeUtil.nowMs();
+ assertThat(secondTimestamp - firstTimestamp).isEqualTo(60 * 1000);
+ }
+
+ @Test
+ @UseClockStep(clockStep = 5)
+ public void useClockStepWithClockStep() {
+ long firstTimestamp = TimeUtil.nowMs();
+ long secondTimestamp = TimeUtil.nowMs();
+ assertThat(secondTimestamp - firstTimestamp).isEqualTo(5 * 1000);
+ }
+
+ @Test
+ @UseClockStep(startAtEpoch = true)
+ public void useClockStepWithStartAtEpoch() {
+ assertThat(TimeUtil.nowTs()).isEqualTo(Timestamp.from(Instant.EPOCH));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/annotation/UseSystemTimeTest.java b/javatests/com/google/gerrit/acceptance/annotation/UseSystemTimeTest.java
new file mode 100644
index 0000000000..7480200907
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/annotation/UseSystemTimeTest.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.annotation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.UseSystemTime;
+import com.google.gerrit.server.util.time.TimeUtil;
+import org.junit.Test;
+
+@UseClockStep
+public class UseSystemTimeTest extends AbstractDaemonTest {
+ @Test
+ @UseSystemTime
+ public void useSystemTimeAlthoughClassIsAnnotatedWithUseClockStep() {
+ long firstTimestamp = TimeUtil.nowMs();
+ long secondTimestamp = TimeUtil.nowMs();
+ assertThat(secondTimestamp - firstTimestamp).isLessThan(1000);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/annotation/UseTimezoneTest.java b/javatests/com/google/gerrit/acceptance/annotation/UseTimezoneTest.java
new file mode 100644
index 0000000000..abf5eda63d
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/annotation/UseTimezoneTest.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.annotation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.UseTimezone;
+import org.junit.Test;
+
+public class UseTimezoneTest extends AbstractDaemonTest {
+ @Test
+ @UseTimezone(timezone = "US/Eastern")
+ public void usEastern() {
+ assertThat(System.getProperty("user.timezone")).isEqualTo("US/Eastern");
+ }
+
+ @Test
+ @UseTimezone(timezone = "UTC")
+ public void utc() {
+ assertThat(System.getProperty("user.timezone")).isEqualTo("UTC");
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 7dcff20820..5719c7ffd9 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -17,10 +17,14 @@ package com.google.gerrit.acceptance.api.accounts;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.common.truth.Truth.assert_;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.acceptance.GitUtil.deleteRef;
import static com.google.gerrit.acceptance.GitUtil.fetch;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
import static com.google.gerrit.gpg.testing.TestKeys.allValidKeys;
@@ -33,11 +37,17 @@ import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPG
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.truth.ConfigSubject.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.SECONDS;
+import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import com.github.rholder.retry.StopStrategies;
import com.google.common.cache.LoadingCache;
@@ -48,14 +58,17 @@ import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.io.BaseEncoding;
import com.google.common.truth.Correspondence;
-import com.google.common.truth.Correspondence.BinaryPredicate;
import com.google.common.util.concurrent.AtomicLongMap;
import com.google.common.util.concurrent.Runnables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountIndexedCounter;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.UseClockStep;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
@@ -68,6 +81,12 @@ import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.accounts.AccountApi;
import com.google.gerrit.extensions.api.accounts.AccountInput;
@@ -90,10 +109,9 @@ import com.google.gerrit.extensions.common.EmailInfo;
import com.google.gerrit.extensions.common.GpgKeyInfo;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.SshKeyInfo;
-import com.google.gerrit.extensions.events.AccountIndexedListener;
+import com.google.gerrit.extensions.events.AccountActivationListener;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -106,19 +124,11 @@ import com.google.gerrit.gpg.PublicKeyStore;
import com.google.gerrit.gpg.testing.TestKey;
import com.google.gerrit.httpd.CacheBasedWebSession;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountProperties;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.Emails;
-import com.google.gerrit.server.account.ProjectWatches;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
@@ -128,17 +138,17 @@ import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.index.account.AccountIndexer;
import com.google.gerrit.server.index.account.StalenessChecker;
import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.server.plugincontext.PluginContext.PluginMetrics;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.RefPattern;
import com.google.gerrit.server.query.account.InternalAccountQuery;
import com.google.gerrit.server.update.RetryHelper;
-import com.google.gerrit.server.util.MagicBranch;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.server.validators.AccountActivationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.FakeEmailSender.Message;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;
@@ -147,7 +157,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -170,6 +179,7 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
@@ -208,8 +218,6 @@ public class AccountIT extends AbstractDaemonTest {
@Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
@Inject private AccountIndexer accountIndexer;
- @Inject private DynamicSet<AccountIndexedListener> accountIndexedListeners;
- @Inject private DynamicSet<GitReferenceUpdatedListener> refUpdateListeners;
@Inject private ExternalIdNotes.Factory extIdNotesFactory;
@Inject private ExternalIds externalIds;
@Inject private GitReferenceUpdated gitReferenceUpdated;
@@ -222,53 +230,21 @@ public class AccountIT extends AbstractDaemonTest {
@Inject private Sequences seq;
@Inject private StalenessChecker stalenessChecker;
@Inject private VersionedAuthorizedKeys.Accessor authorizedKeys;
+ @Inject private ExtensionRegistry extensionRegistry;
@Inject protected Emails emails;
@Inject
@Named("accounts")
- private LoadingCache<Account.Id, Optional<AccountState>> accountsCache;
+ private LoadingCache<Account.Id, AccountState> accountsCache;
@Inject private AccountOperations accountOperations;
- @Inject
- private DynamicSet<AccountActivationValidationListener> accountActivationValidationListeners;
-
@Inject protected GroupOperations groupOperations;
- private AccountIndexedCounter accountIndexedCounter;
- private RegistrationHandle accountIndexEventCounterHandle;
- private RefUpdateCounter refUpdateCounter;
- private RegistrationHandle refUpdateCounterHandle;
private BasicCookieStore httpCookieStore;
private CloseableHttpClient httpclient;
- @Before
- public void addAccountIndexEventCounter() {
- accountIndexedCounter = new AccountIndexedCounter();
- accountIndexEventCounterHandle = accountIndexedListeners.add("gerrit", accountIndexedCounter);
- }
-
- @After
- public void removeAccountIndexEventCounter() {
- if (accountIndexEventCounterHandle != null) {
- accountIndexEventCounterHandle.remove();
- }
- }
-
- @Before
- public void addRefUpdateCounter() {
- refUpdateCounter = new RefUpdateCounter();
- refUpdateCounterHandle = refUpdateListeners.add("gerrit", refUpdateCounter);
- }
-
- @After
- public void removeRefUpdateCounter() {
- if (refUpdateCounterHandle != null) {
- refUpdateCounterHandle.remove();
- }
- }
-
@After
public void clearPublicKeyStore() throws Exception {
try (Repository repo = repoManager.openRepository(allUsers)) {
@@ -327,11 +303,14 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void createByAccountCreator() throws Exception {
- Account.Id accountId = createByAccountCreator(1);
- refUpdateCounter.assertRefUpdateFor(
- RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)),
- RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS),
- RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS));
+ RefUpdateCounter refUpdateCounter = new RefUpdateCounter();
+ try (Registration registration = extensionRegistry.newRegistration().add(refUpdateCounter)) {
+ Account.Id accountId = createByAccountCreator(1);
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)),
+ RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS),
+ RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS));
+ }
}
@Test
@@ -350,42 +329,54 @@ public class AccountIT extends AbstractDaemonTest {
}
private Account.Id createByAccountCreator(int expectedAccountReindexCalls) throws Exception {
- String name = "foo";
- TestAccount foo = accountCreator.create(name);
- AccountInfo info = gApi.accounts().id(foo.id().get()).get();
- assertThat(info.username).isEqualTo(name);
- assertThat(info.name).isEqualTo(name);
- accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
- assertUserBranch(foo.id(), name, null);
- return foo.id();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String name = "foo";
+ TestAccount foo = accountCreator.create(name);
+ AccountInfo info = gApi.accounts().id(foo.id().get()).get();
+ assertThat(info.username).isEqualTo(name);
+ assertThat(info.name).isEqualTo(name);
+ accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
+ assertUserBranch(foo.id(), name, null);
+ return foo.id();
+ }
}
@Test
public void createAnonymousCowardByAccountCreator() throws Exception {
- TestAccount anonymousCoward = accountCreator.create();
- accountIndexedCounter.assertReindexOf(anonymousCoward);
- assertUserBranchWithoutAccountConfig(anonymousCoward.id());
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestAccount anonymousCoward = accountCreator.create();
+ accountIndexedCounter.assertReindexOf(anonymousCoward);
+ assertUserBranchWithoutAccountConfig(anonymousCoward.id());
+ }
}
@Test
public void create() throws Exception {
- AccountInput input = new AccountInput();
- input.username = "foo";
- input.name = "Foo";
- input.email = "foo@example.com";
- AccountInfo accountInfo = gApi.accounts().create(input).get();
- assertThat(accountInfo._accountId).isNotNull();
- assertThat(accountInfo.username).isEqualTo(input.username);
- assertThat(accountInfo.name).isEqualTo(input.name);
- assertThat(accountInfo.email).isEqualTo(input.email);
- assertThat(accountInfo.status).isNull();
-
- Account.Id accountId = new Account.Id(accountInfo._accountId);
- accountIndexedCounter.assertReindexOf(accountId, 1);
- assertThat(externalIds.byAccount(accountId))
- .containsExactly(
- ExternalId.createUsername(input.username, accountId, null),
- ExternalId.createEmail(accountId, input.email));
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ AccountInput input = new AccountInput();
+ input.username = "foo";
+ input.name = "Foo";
+ input.email = "foo@example.com";
+ AccountInfo accountInfo = gApi.accounts().create(input).get();
+ assertThat(accountInfo._accountId).isNotNull();
+ assertThat(accountInfo.username).isEqualTo(input.username);
+ assertThat(accountInfo.name).isEqualTo(input.name);
+ assertThat(accountInfo.email).isEqualTo(input.email);
+ assertThat(accountInfo.status).isNull();
+
+ Account.Id accountId = Account.id(accountInfo._accountId);
+ accountIndexedCounter.assertReindexOf(accountId, 1);
+ assertThat(externalIds.byAccount(accountId))
+ .containsExactly(
+ ExternalId.createUsername(input.username, accountId, null),
+ ExternalId.createEmail(accountId, input.email));
+ }
}
@Test
@@ -393,9 +384,11 @@ public class AccountIT extends AbstractDaemonTest {
AccountInput input = new AccountInput();
input.username = admin.username();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("username '" + admin.username() + "' already exists");
- gApi.accounts().create(input);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.accounts().create(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("username '" + admin.username() + "' already exists");
}
@Test
@@ -404,15 +397,15 @@ public class AccountIT extends AbstractDaemonTest {
input.username = "foo";
input.email = admin.email();
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("email '" + admin.email() + "' already exists");
- gApi.accounts().create(input);
+ UnprocessableEntityException thrown =
+ assertThrows(UnprocessableEntityException.class, () -> gApi.accounts().create(input));
+ assertThat(thrown).hasMessageThat().contains("email '" + admin.email() + "' already exists");
}
@Test
public void commitMessageOnAccountUpdates() throws Exception {
AccountsUpdate au = accountsUpdateProvider.get();
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
au.insert("Create Test Account", accountId, u -> {});
assertLastCommitMessageOfUserBranch(accountId, "Create Test Account");
@@ -431,39 +424,37 @@ public class AccountIT extends AbstractDaemonTest {
}
@Test
+ @UseClockStep
public void createAtomically() throws Exception {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- try {
- Account.Id accountId = new Account.Id(seq.nextAccountId());
- String fullName = "Foo";
- ExternalId extId = ExternalId.createEmail(accountId, "foo@example.com");
- AccountState accountState =
- accountsUpdateProvider
- .get()
- .insert(
- "Create Account Atomically",
- accountId,
- u -> u.setFullName(fullName).addExternalId(extId));
- assertThat(accountState.getAccount().getFullName()).isEqualTo(fullName);
-
- AccountInfo info = gApi.accounts().id(accountId.get()).get();
- assertThat(info.name).isEqualTo(fullName);
-
- List<EmailInfo> emails = gApi.accounts().id(accountId.get()).getEmails();
- assertThat(emails.stream().map(e -> e.email).collect(toSet())).containsExactly(extId.email());
-
- RevCommit commitUserBranch = getRemoteHead(allUsers, RefNames.refsUsers(accountId));
- RevCommit commitRefsMetaExternalIds = getRemoteHead(allUsers, RefNames.REFS_EXTERNAL_IDS);
- assertThat(commitUserBranch.getCommitTime())
- .isEqualTo(commitRefsMetaExternalIds.getCommitTime());
- } finally {
- TestTimeUtil.useSystemTime();
- }
+ Account.Id accountId = Account.id(seq.nextAccountId());
+ String fullName = "Foo";
+ ExternalId extId = ExternalId.createEmail(accountId, "foo@example.com");
+ AccountState accountState =
+ accountsUpdateProvider
+ .get()
+ .insert(
+ "Create Account Atomically",
+ accountId,
+ u -> u.setFullName(fullName).addExternalId(extId));
+ assertThat(accountState.account().fullName()).isEqualTo(fullName);
+
+ AccountInfo info = gApi.accounts().id(accountId.get()).get();
+ assertThat(info.name).isEqualTo(fullName);
+
+ List<EmailInfo> emails = gApi.accounts().id(accountId.get()).getEmails();
+ assertThat(emails.stream().map(e -> e.email).collect(toSet())).containsExactly(extId.email());
+
+ RevCommit commitUserBranch =
+ projectOperations.project(allUsers).getHead(RefNames.refsUsers(accountId));
+ RevCommit commitRefsMetaExternalIds =
+ projectOperations.project(allUsers).getHead(RefNames.REFS_EXTERNAL_IDS);
+ assertThat(commitUserBranch.getCommitTime())
+ .isEqualTo(commitRefsMetaExternalIds.getCommitTime());
}
@Test
public void updateNonExistingAccount() throws Exception {
- Account.Id nonExistingAccountId = new Account.Id(999999);
+ Account.Id nonExistingAccountId = Account.id(999999);
AtomicBoolean consumerCalled = new AtomicBoolean();
Optional<AccountState> accountState =
accountsUpdateProvider
@@ -485,9 +476,9 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update("Set status", anonymousCoward.id(), u -> u.setStatus(status));
assertThat(accountState).isPresent();
- Account account = accountState.get().getAccount();
- assertThat(account.getFullName()).isNull();
- assertThat(account.getStatus()).isEqualTo(status);
+ Account account = accountState.get().account();
+ assertThat(account.fullName()).isNull();
+ assertThat(account.status()).isEqualTo(status);
assertUserBranch(anonymousCoward.id(), null, status);
}
@@ -504,7 +495,7 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(ref).isNotNull();
RevCommit c = rw.parseCommit(ref.getObjectId());
long timestampDiffMs =
- Math.abs(c.getCommitTime() * 1000L - getAccount(accountId).getRegisteredOn().getTime());
+ Math.abs(c.getCommitTime() * 1000L - getAccount(accountId).registeredOn().getTime());
assertThat(timestampDiffMs).isAtMost(SECONDS.toMillis(1));
// Check the 'account.config' file.
@@ -513,10 +504,11 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(tw).isNotNull();
Config cfg = new Config();
cfg.fromText(new String(or.open(tw.getObjectId(0), OBJ_BLOB).getBytes(), UTF_8));
- assertThat(
- cfg.getString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_FULL_NAME))
+ assertThat(cfg)
+ .stringValue(AccountProperties.ACCOUNT, null, AccountProperties.KEY_FULL_NAME)
.isEqualTo(name);
- assertThat(cfg.getString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_STATUS))
+ assertThat(cfg)
+ .stringValue(AccountProperties.ACCOUNT, null, AccountProperties.KEY_STATUS)
.isEqualTo(status);
} else {
// No account properties were set, hence an 'account.config' file was not created.
@@ -528,76 +520,98 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void get() throws Exception {
- AccountInfo info = gApi.accounts().id("admin").get();
- assertThat(info.name).isEqualTo("Administrator");
- assertThat(info.email).isEqualTo("admin@example.com");
- assertThat(info.username).isEqualTo("admin");
- accountIndexedCounter.assertNoReindex();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ AccountInfo info = gApi.accounts().id("admin").get();
+ assertThat(info.name).isEqualTo("Administrator");
+ assertThat(info.email).isEqualTo("admin@example.com");
+ assertThat(info.username).isEqualTo("admin");
+ accountIndexedCounter.assertNoReindex();
+ }
}
@Test
public void getByIntId() throws Exception {
- AccountInfo info = gApi.accounts().id("admin").get();
- AccountInfo infoByIntId = gApi.accounts().id(info._accountId).get();
- assertThat(info.name).isEqualTo(infoByIntId.name);
- accountIndexedCounter.assertNoReindex();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ AccountInfo info = gApi.accounts().id("admin").get();
+ AccountInfo infoByIntId = gApi.accounts().id(info._accountId).get();
+ assertThat(info.name).isEqualTo(infoByIntId.name);
+ accountIndexedCounter.assertNoReindex();
+ }
}
@Test
public void self() throws Exception {
- AccountInfo info = gApi.accounts().self().get();
- assertUser(info, admin);
-
- info = gApi.accounts().id("self").get();
- assertUser(info, admin);
- accountIndexedCounter.assertNoReindex();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ AccountInfo info = gApi.accounts().self().get();
+ assertUser(info, admin);
+
+ info = gApi.accounts().id("self").get();
+ assertUser(info, admin);
+ accountIndexedCounter.assertNoReindex();
+ }
}
@Test
public void active() throws Exception {
- int id = gApi.accounts().id("user").get()._accountId;
- assertThat(gApi.accounts().id("user").getActive()).isTrue();
- gApi.accounts().id("user").setActive(false);
- accountIndexedCounter.assertReindexOf(user);
-
- // Inactive users may only be resolved by ID.
- try {
- gApi.accounts().id("user");
- assert_().fail("expected ResourceNotFoundException");
- } catch (ResourceNotFoundException e) {
- assertThat(e)
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ int id = gApi.accounts().id("user").get()._accountId;
+ assertThat(gApi.accounts().id("user").getActive()).isTrue();
+ gApi.accounts().id("user").setActive(false);
+ accountIndexedCounter.assertReindexOf(user);
+
+ // Inactive users may only be resolved by ID.
+ ResourceNotFoundException thrown =
+ assertThrows(ResourceNotFoundException.class, () -> gApi.accounts().id("user"));
+ assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"Account 'user' only matches inactive accounts. To use an inactive account, retry"
+ " with one of the following exact account IDs:\n"
+ id
+ ": User <user@example.com>");
- }
- assertThat(gApi.accounts().id(id).getActive()).isFalse();
+ assertThat(gApi.accounts().id(id).getActive()).isFalse();
- gApi.accounts().id(id).setActive(true);
- assertThat(gApi.accounts().id("user").getActive()).isTrue();
- accountIndexedCounter.assertReindexOf(user);
+ gApi.accounts().id(id).setActive(true);
+ assertThat(gApi.accounts().id("user").getActive()).isTrue();
+ accountIndexedCounter.assertReindexOf(user);
+ }
}
@Test
public void shouldAllowQueryByEmailForInactiveUser() throws Exception {
- Account.Id activatableAccountId =
- accountOperations.newAccount().inactive().preferredEmail("foo@activatable.com").create();
- accountIndexedCounter.assertReindexOf(activatableAccountId, 1);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ Account.Id activatableAccountId =
+ accountOperations.newAccount().inactive().preferredEmail("foo@activatable.com").create();
+ accountIndexedCounter.assertReindexOf(activatableAccountId, 1);
+ }
gApi.changes().query("owner:foo@activatable.com").get();
}
@Test
public void shouldAllowQueryByUserNameForInactiveUser() throws Exception {
- Account.Id activatableAccountId =
- accountOperations.newAccount().inactive().username("foo").create();
- accountIndexedCounter.assertReindexOf(activatableAccountId, 1);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ Account.Id activatableAccountId =
+ accountOperations.newAccount().inactive().username("foo").create();
+ accountIndexedCounter.assertReindexOf(activatableAccountId, 1);
+ }
gApi.changes().query("owner:foo").get();
}
+ @Test
@GerritConfig(name = "auth.type", value = "DEVELOPMENT_BECOME_ANY_ACCOUNT")
public void activeUserGetSessionCookieOnLogin() throws Exception {
Integer accountId = accountIdApi().get()._accountId;
@@ -640,90 +654,98 @@ public class AccountIT extends AbstractDaemonTest {
accountOperations.newAccount().inactive().preferredEmail("foo@activatable.com").create();
Account.Id deactivatableAccountId =
accountOperations.newAccount().preferredEmail("foo@deactivatable.com").create();
- RegistrationHandle registrationHandle =
- accountActivationValidationListeners.add(
- "gerrit",
- new AccountActivationValidationListener() {
- @Override
- public void validateActivation(AccountState account) throws ValidationException {
- String preferredEmail = account.getAccount().getPreferredEmail();
- if (preferredEmail == null || !preferredEmail.endsWith("@activatable.com")) {
- throw new ValidationException("not allowed to active account");
- }
- }
- @Override
- public void validateDeactivation(AccountState account) throws ValidationException {
- String preferredEmail = account.getAccount().getPreferredEmail();
- if (preferredEmail == null || !preferredEmail.endsWith("@deactivatable.com")) {
- throw new ValidationException("not allowed to deactive account");
- }
- }
- });
- try {
+ AccountActivationValidationListener validationListener =
+ new AccountActivationValidationListener() {
+ @Override
+ public void validateActivation(AccountState account) throws ValidationException {
+ String preferredEmail = account.account().preferredEmail();
+ if (preferredEmail == null || !preferredEmail.endsWith("@activatable.com")) {
+ throw new ValidationException("not allowed to active account");
+ }
+ }
+
+ @Override
+ public void validateDeactivation(AccountState account) throws ValidationException {
+ String preferredEmail = account.account().preferredEmail();
+ if (preferredEmail == null || !preferredEmail.endsWith("@deactivatable.com")) {
+ throw new ValidationException("not allowed to deactive account");
+ }
+ }
+ };
+
+ AccountActivationListener listener = mock(AccountActivationListener.class);
+
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(validationListener).add(listener)) {
/* Test account that can be activated, but not deactivated */
// Deactivate account that is already inactive
- try {
- gApi.accounts().id(activatableAccountId.get()).setActive(false);
- fail("Expected exception");
- } catch (ResourceConflictException e) {
- assertThat(e.getMessage()).isEqualTo("account not active");
- }
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.accounts().id(activatableAccountId.get()).setActive(false));
+ assertThat(thrown).hasMessageThat().isEqualTo("account not active");
assertThat(accountOperations.account(activatableAccountId).get().active()).isFalse();
+ verifyZeroInteractions(listener);
// Activate account that can be activated
gApi.accounts().id(activatableAccountId.get()).setActive(true);
assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
+ verify(listener).onAccountActivated(activatableAccountId.get());
+ verifyNoMoreInteractions(listener);
// Activate account that is already active
gApi.accounts().id(activatableAccountId.get()).setActive(true);
assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
+ verifyZeroInteractions(listener);
// Try deactivating account that cannot be deactivated
- try {
- gApi.accounts().id(activatableAccountId.get()).setActive(false);
- fail("Expected exception");
- } catch (ResourceConflictException e) {
- assertThat(e.getMessage()).isEqualTo("not allowed to deactive account");
- }
+ thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.accounts().id(activatableAccountId.get()).setActive(false));
+ assertThat(thrown).hasMessageThat().isEqualTo("not allowed to deactive account");
assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
+ verifyZeroInteractions(listener);
/* Test account that can be deactivated, but not activated */
// Activate account that is already inactive
gApi.accounts().id(deactivatableAccountId.get()).setActive(true);
assertThat(accountOperations.account(deactivatableAccountId).get().active()).isTrue();
+ verifyZeroInteractions(listener);
// Deactivate account that can be deactivated
gApi.accounts().id(deactivatableAccountId.get()).setActive(false);
assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
+ verify(listener).onAccountDeactivated(deactivatableAccountId.get());
+ verifyNoMoreInteractions(listener);
// Deactivate account that is already inactive
- try {
- gApi.accounts().id(deactivatableAccountId.get()).setActive(false);
- fail("Expected exception");
- } catch (ResourceConflictException e) {
- assertThat(e.getMessage()).isEqualTo("account not active");
- }
+ thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.accounts().id(deactivatableAccountId.get()).setActive(false));
+ assertThat(thrown).hasMessageThat().isEqualTo("account not active");
assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
+ verifyZeroInteractions(listener);
// Try activating account that cannot be activated
- try {
- gApi.accounts().id(deactivatableAccountId.get()).setActive(true);
- fail("Expected exception");
- } catch (ResourceConflictException e) {
- assertThat(e.getMessage()).isEqualTo("not allowed to active account");
- }
+ thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.accounts().id(deactivatableAccountId.get()).setActive(true));
+ assertThat(thrown).hasMessageThat().isEqualTo("not allowed to active account");
assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
- } finally {
- registrationHandle.remove();
+ verifyZeroInteractions(listener);
}
}
@Test
public void deactivateSelf() throws Exception {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("cannot deactivate own account");
- gApi.accounts().self().setActive(false);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.accounts().self().setActive(false));
+ assertThat(thrown).hasMessageThat().contains("cannot deactivate own account");
}
@Test
@@ -732,107 +754,125 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(gApi.accounts().id("user").getActive()).isTrue();
gApi.accounts().id("user").setActive(false);
assertThat(gApi.accounts().id(id).getActive()).isFalse();
- try {
- gApi.accounts().id(id).setActive(false);
- fail("Expected exception");
- } catch (ResourceConflictException e) {
- assertThat(e.getMessage()).isEqualTo("account not active");
- }
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.accounts().id(id).setActive(false));
+ assertThat(thrown).hasMessageThat().isEqualTo("account not active");
gApi.accounts().id(id).setActive(true);
}
@Test
public void starUnstarChange() throws Exception {
- PushOneCommit.Result r = createChange();
- String triplet = project.get() + "~master~" + r.getChangeId();
- refUpdateCounter.clear();
-
- gApi.accounts().self().starChange(triplet);
- ChangeInfo change = info(triplet);
- assertThat(change.starred).isTrue();
- assertThat(change.stars).contains(DEFAULT_LABEL);
- refUpdateCounter.assertRefUpdateFor(
- RefUpdateCounter.projectRef(
- allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
-
- gApi.accounts().self().unstarChange(triplet);
- change = info(triplet);
- assertThat(change.starred).isNull();
- assertThat(change.stars).isNull();
- refUpdateCounter.assertRefUpdateFor(
- RefUpdateCounter.projectRef(
- allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
-
- accountIndexedCounter.assertNoReindex();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ RefUpdateCounter refUpdateCounter = new RefUpdateCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter).add(refUpdateCounter)) {
+ PushOneCommit.Result r = createChange();
+ String triplet = project.get() + "~master~" + r.getChangeId();
+ refUpdateCounter.clear();
+
+ gApi.accounts().self().starChange(triplet);
+ ChangeInfo change = info(triplet);
+ assertThat(change.starred).isTrue();
+ assertThat(change.stars).contains(DEFAULT_LABEL);
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.refsStarredChanges(Change.id(change._number), admin.id())));
+
+ gApi.accounts().self().unstarChange(triplet);
+ change = info(triplet);
+ assertThat(change.starred).isNull();
+ assertThat(change.stars).isNull();
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.refsStarredChanges(Change.id(change._number), admin.id())));
+
+ accountIndexedCounter.assertNoReindex();
+ }
}
@Test
public void starUnstarChangeWithLabels() throws Exception {
- PushOneCommit.Result r = createChange();
- String triplet = project.get() + "~master~" + r.getChangeId();
- refUpdateCounter.clear();
-
- assertThat(gApi.accounts().self().getStars(triplet)).isEmpty();
- assertThat(gApi.accounts().self().getStarredChanges()).isEmpty();
-
- gApi.accounts()
- .self()
- .setStars(triplet, new StarsInput(ImmutableSet.of(DEFAULT_LABEL, "red", "blue")));
- ChangeInfo change = info(triplet);
- assertThat(change.starred).isTrue();
- assertThat(change.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder();
- assertThat(gApi.accounts().self().getStars(triplet))
- .containsExactly("blue", "red", DEFAULT_LABEL)
- .inOrder();
- List<ChangeInfo> starredChanges = gApi.accounts().self().getStarredChanges();
- assertThat(starredChanges).hasSize(1);
- ChangeInfo starredChange = starredChanges.get(0);
- assertThat(starredChange._number).isEqualTo(r.getChange().getId().get());
- assertThat(starredChange.starred).isTrue();
- assertThat(starredChange.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder();
- refUpdateCounter.assertRefUpdateFor(
- RefUpdateCounter.projectRef(
- allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
-
- gApi.accounts()
- .self()
- .setStars(
- triplet,
- new StarsInput(ImmutableSet.of("yellow"), ImmutableSet.of(DEFAULT_LABEL, "blue")));
- change = info(triplet);
- assertThat(change.starred).isNull();
- assertThat(change.stars).containsExactly("red", "yellow").inOrder();
- assertThat(gApi.accounts().self().getStars(triplet)).containsExactly("red", "yellow").inOrder();
- starredChanges = gApi.accounts().self().getStarredChanges();
- assertThat(starredChanges).hasSize(1);
- starredChange = starredChanges.get(0);
- assertThat(starredChange._number).isEqualTo(r.getChange().getId().get());
- assertThat(starredChange.starred).isNull();
- assertThat(starredChange.stars).containsExactly("red", "yellow").inOrder();
- refUpdateCounter.assertRefUpdateFor(
- RefUpdateCounter.projectRef(
- allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
-
- accountIndexedCounter.assertNoReindex();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ RefUpdateCounter refUpdateCounter = new RefUpdateCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter).add(refUpdateCounter)) {
+ PushOneCommit.Result r = createChange();
+ String triplet = project.get() + "~master~" + r.getChangeId();
+ refUpdateCounter.clear();
+
+ assertThat(gApi.accounts().self().getStars(triplet)).isEmpty();
+ assertThat(gApi.accounts().self().getStarredChanges()).isEmpty();
+
+ gApi.accounts()
+ .self()
+ .setStars(triplet, new StarsInput(ImmutableSet.of(DEFAULT_LABEL, "red", "blue")));
+ ChangeInfo change = info(triplet);
+ assertThat(change.starred).isTrue();
+ assertThat(change.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder();
+ assertThat(gApi.accounts().self().getStars(triplet))
+ .containsExactly("blue", "red", DEFAULT_LABEL)
+ .inOrder();
+ List<ChangeInfo> starredChanges = gApi.accounts().self().getStarredChanges();
+ assertThat(starredChanges).hasSize(1);
+ ChangeInfo starredChange = starredChanges.get(0);
+ assertThat(starredChange._number).isEqualTo(r.getChange().getId().get());
+ assertThat(starredChange.starred).isTrue();
+ assertThat(starredChange.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder();
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.refsStarredChanges(Change.id(change._number), admin.id())));
+
+ gApi.accounts()
+ .self()
+ .setStars(
+ triplet,
+ new StarsInput(ImmutableSet.of("yellow"), ImmutableSet.of(DEFAULT_LABEL, "blue")));
+ change = info(triplet);
+ assertThat(change.starred).isNull();
+ assertThat(change.stars).containsExactly("red", "yellow").inOrder();
+ assertThat(gApi.accounts().self().getStars(triplet))
+ .containsExactly("red", "yellow")
+ .inOrder();
+ starredChanges = gApi.accounts().self().getStarredChanges();
+ assertThat(starredChanges).hasSize(1);
+ starredChange = starredChanges.get(0);
+ assertThat(starredChange._number).isEqualTo(r.getChange().getId().get());
+ assertThat(starredChange.starred).isNull();
+ assertThat(starredChange.stars).containsExactly("red", "yellow").inOrder();
+ refUpdateCounter.assertRefUpdateFor(
+ RefUpdateCounter.projectRef(
+ allUsers, RefNames.refsStarredChanges(Change.id(change._number), admin.id())));
+
+ accountIndexedCounter.assertNoReindex();
- requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("not allowed to get stars of another account");
- gApi.accounts().id(Integer.toString((admin.id().get()))).getStars(triplet);
+ requestScopeOperations.setApiUser(user.id());
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.accounts().id(Integer.toString((admin.id().get()))).getStars(triplet));
+ assertThat(thrown).hasMessageThat().contains("not allowed to get stars of another account");
+ }
}
@Test
public void starWithInvalidLabels() throws Exception {
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
- exception.expect(BadRequestException.class);
- exception.expectMessage("invalid labels: another invalid label, invalid label");
- gApi.accounts()
- .self()
- .setStars(
- triplet,
- new StarsInput(
- ImmutableSet.of(DEFAULT_LABEL, "invalid label", "blue", "another invalid label")));
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.accounts()
+ .self()
+ .setStars(
+ triplet,
+ new StarsInput(
+ ImmutableSet.of(
+ DEFAULT_LABEL, "invalid label", "blue", "another invalid label"))));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("invalid labels: another invalid label, invalid label");
}
@Test
@@ -850,65 +890,84 @@ public class AccountIT extends AbstractDaemonTest {
public void starWithDefaultAndIgnoreLabel() throws Exception {
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- "The labels "
- + DEFAULT_LABEL
- + " and "
- + IGNORE_LABEL
- + " are mutually exclusive."
- + " Only one of them can be set.");
- gApi.accounts()
- .self()
- .setStars(triplet, new StarsInput(ImmutableSet.of(DEFAULT_LABEL, "blue", IGNORE_LABEL)));
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.accounts()
+ .self()
+ .setStars(
+ triplet,
+ new StarsInput(ImmutableSet.of(DEFAULT_LABEL, "blue", IGNORE_LABEL))));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "The labels "
+ + DEFAULT_LABEL
+ + " and "
+ + IGNORE_LABEL
+ + " are mutually exclusive."
+ + " Only one of them can be set.");
}
@Test
public void ignoreChangeBySetStars() throws Exception {
- TestAccount user2 = accountCreator.user2();
- accountIndexedCounter.clear();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestAccount user2 = accountCreator.user2();
+ accountIndexedCounter.clear();
- PushOneCommit.Result r = createChange();
+ PushOneCommit.Result r = createChange();
- AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email();
- gApi.changes().id(r.getChangeId()).addReviewer(in);
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = user.email();
+ gApi.changes().id(r.getChangeId()).addReviewer(in);
- in = new AddReviewerInput();
- in.reviewer = user2.email();
- gApi.changes().id(r.getChangeId()).addReviewer(in);
+ in = new AddReviewerInput();
+ in.reviewer = user2.email();
+ gApi.changes().id(r.getChangeId()).addReviewer(in);
- requestScopeOperations.setApiUser(user.id());
- gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
+ requestScopeOperations.setApiUser(user.id());
+ gApi.accounts()
+ .self()
+ .setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
- sender.clear();
- requestScopeOperations.setApiUser(admin.id());
- gApi.changes().id(r.getChangeId()).abandon();
- List<Message> messages = sender.getMessages();
- assertThat(messages).hasSize(1);
- assertThat(messages.get(0).rcpt()).containsExactly(user2.getEmailAddress());
- accountIndexedCounter.assertNoReindex();
+ sender.clear();
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.changes().id(r.getChangeId()).abandon();
+ List<Message> messages = sender.getMessages();
+ assertThat(messages).hasSize(1);
+ assertThat(messages.get(0).rcpt()).containsExactly(user2.getEmailAddress());
+ accountIndexedCounter.assertNoReindex();
+ }
}
@Test
public void addReviewerToIgnoredChange() throws Exception {
- PushOneCommit.Result r = createChange();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ PushOneCommit.Result r = createChange();
- requestScopeOperations.setApiUser(user.id());
- gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
+ requestScopeOperations.setApiUser(user.id());
+ gApi.accounts()
+ .self()
+ .setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
- sender.clear();
- requestScopeOperations.setApiUser(admin.id());
+ sender.clear();
+ requestScopeOperations.setApiUser(admin.id());
- AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email();
- gApi.changes().id(r.getChangeId()).addReviewer(in);
- List<Message> messages = sender.getMessages();
- assertThat(messages).hasSize(1);
- Message message = messages.get(0);
- assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
- assertMailReplyTo(message, admin.email());
- accountIndexedCounter.assertNoReindex();
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = user.email();
+ gApi.changes().id(r.getChangeId()).addReviewer(in);
+ List<Message> messages = sender.getMessages();
+ assertThat(messages).hasSize(1);
+ Message message = messages.get(0);
+ assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertMailReplyTo(message, admin.email());
+ accountIndexedCounter.assertNoReindex();
+ }
}
@Test
@@ -1006,17 +1065,21 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void suggestAccounts() throws Exception {
- String adminUsername = "admin";
- List<AccountInfo> result = gApi.accounts().suggestAccounts().withQuery(adminUsername).get();
- assertThat(result).hasSize(1);
- assertThat(result.get(0).username).isEqualTo(adminUsername);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String adminUsername = "admin";
+ List<AccountInfo> result = gApi.accounts().suggestAccounts().withQuery(adminUsername).get();
+ assertThat(result).hasSize(1);
+ assertThat(result.get(0).username).isEqualTo(adminUsername);
- List<AccountInfo> resultShortcutApi = gApi.accounts().suggestAccounts(adminUsername).get();
- assertThat(resultShortcutApi).hasSize(result.size());
+ List<AccountInfo> resultShortcutApi = gApi.accounts().suggestAccounts(adminUsername).get();
+ assertThat(resultShortcutApi).hasSize(result.size());
- List<AccountInfo> emptyResult = gApi.accounts().suggestAccounts("unknown").get();
- assertThat(emptyResult).isEmpty();
- accountIndexedCounter.assertNoReindex();
+ List<AccountInfo> emptyResult = gApi.accounts().suggestAccounts("unknown").get();
+ assertThat(emptyResult).isEmpty();
+ accountIndexedCounter.assertNoReindex();
+ }
}
@Test
@@ -1040,7 +1103,7 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(detail.email).isEqualTo(email);
assertThat(detail.secondaryEmails).containsExactly(secondaryEmail);
assertThat(detail.status).isEqualTo(status);
- assertThat(detail.registeredOn).isEqualTo(getAccount(foo.id()).getRegisteredOn());
+ assertThat(detail.registeredOn).isEqualTo(getAccount(foo.id()).registeredOn());
assertThat(detail.inactive).isNull();
assertThat(detail._moreAccounts).isNull();
}
@@ -1082,7 +1145,7 @@ public class AccountIT extends AbstractDaemonTest {
requestScopeOperations.setApiUser(admin.id());
String secondaryEmail = "secondary@example.com";
EmailInput input = newEmailInput(secondaryEmail);
- gApi.accounts().id(foo.id().hashCode()).addEmail(input);
+ gApi.accounts().id(foo.id().get()).addEmail(input);
requestScopeOperations.setApiUser(foo.id());
assertThat(getEmails()).containsExactly(email, secondaryEmail);
@@ -1094,9 +1157,9 @@ public class AccountIT extends AbstractDaemonTest {
TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("modify account not permitted");
- gApi.accounts().id(foo.id().get()).getEmails();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.accounts().id(foo.id().get()).getEmails());
+ assertThat(thrown).hasMessageThat().contains("modify account not permitted");
}
@Test
@@ -1105,7 +1168,7 @@ public class AccountIT extends AbstractDaemonTest {
String secondaryEmail = "secondary3@example.com";
TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
EmailInput input = newEmailInput(secondaryEmail);
- gApi.accounts().id(foo.id().hashCode()).addEmail(input);
+ gApi.accounts().id(foo.id().get()).addEmail(input);
assertThat(
gApi.accounts().id(foo.id().get()).getEmails().stream()
@@ -1116,17 +1179,21 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void addEmail() throws Exception {
- List<String> emails = ImmutableList.of("new.email@example.com", "new.email@example.systems");
- Set<String> currentEmails = getEmails();
- for (String email : emails) {
- assertThat(currentEmails).doesNotContain(email);
- EmailInput input = newEmailInput(email);
- gApi.accounts().self().addEmail(input);
- accountIndexedCounter.assertReindexOf(admin);
- }
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ List<String> emails = ImmutableList.of("new.email@example.com", "new.email@example.systems");
+ Set<String> currentEmails = getEmails();
+ for (String email : emails) {
+ assertThat(currentEmails).doesNotContain(email);
+ EmailInput input = newEmailInput(email);
+ gApi.accounts().self().addEmail(input);
+ accountIndexedCounter.assertReindexOf(admin);
+ }
- requestScopeOperations.resetCurrentApiUser();
- assertThat(getEmails()).containsAtLeastElementsIn(emails);
+ requestScopeOperations.resetCurrentApiUser();
+ assertThat(getEmails()).containsAtLeastElementsIn(emails);
+ }
}
@Test
@@ -1144,16 +1211,17 @@ public class AccountIT extends AbstractDaemonTest {
// Non-supported TLD (see tlds-alpha-by-domain.txt)
"new.email@example.africa");
- for (String email : emails) {
- EmailInput input = newEmailInput(email);
- try {
- gApi.accounts().self().addEmail(input);
- fail("Expected BadRequestException for invalid email address: " + email);
- } catch (BadRequestException e) {
- assertThat(e).hasMessageThat().isEqualTo("invalid email address");
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ for (String email : emails) {
+ EmailInput input = newEmailInput(email);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.accounts().self().addEmail(input));
+ assertWithMessage(email).that(thrown).hasMessageThat().isEqualTo("invalid email address");
}
+ accountIndexedCounter.assertNoReindex();
}
- accountIndexedCounter.assertNoReindex();
}
@Test
@@ -1161,8 +1229,7 @@ public class AccountIT extends AbstractDaemonTest {
TestAccount account = accountCreator.create(name("user"));
EmailInput input = newEmailInput("test@test.com");
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.accounts().id(account.username()).addEmail(input);
+ assertThrows(AuthException.class, () -> gApi.accounts().id(account.username()).addEmail(input));
}
@Test
@@ -1170,9 +1237,13 @@ public class AccountIT extends AbstractDaemonTest {
String email = "new.email@example.com";
EmailInput input = newEmailInput(email);
gApi.accounts().self().addEmail(input);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Identity 'mailto:" + email + "' in use by another account");
- gApi.accounts().id(user.username()).addEmail(input);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.accounts().id(user.username()).addEmail(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Identity 'mailto:" + email + "' in use by another account");
}
@Test
@@ -1208,9 +1279,14 @@ public class AccountIT extends AbstractDaemonTest {
TestAccount user = accountCreator.create();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("modify account not permitted");
- gApi.accounts().id(admin.id().get()).addEmail(newEmailInput("foo@example.com", false));
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () ->
+ gApi.accounts()
+ .id(admin.id().get())
+ .addEmail(newEmailInput("foo@example.com", false)));
+ assertThat(thrown).hasMessageThat().contains("modify account not permitted");
}
@Test
@@ -1225,62 +1301,74 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void addEmailAndSetPreferred() throws Exception {
- String email = "foo.bar@example.com";
- EmailInput input = new EmailInput();
- input.email = email;
- input.noConfirmation = true;
- input.preferred = true;
- gApi.accounts().self().addEmail(input);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String email = "foo.bar@example.com";
+ EmailInput input = new EmailInput();
+ input.email = email;
+ input.noConfirmation = true;
+ input.preferred = true;
+ gApi.accounts().self().addEmail(input);
- // Account is reindexed twice; once on adding the new email,
- // and then again on setting the email preferred.
- accountIndexedCounter.assertReindexOf(admin, 2);
+ // Account is reindexed twice; once on adding the new email,
+ // and then again on setting the email preferred.
+ accountIndexedCounter.assertReindexOf(admin, 2);
- String preferred = gApi.accounts().self().get().email;
- assertThat(preferred).isEqualTo(email);
+ String preferred = gApi.accounts().self().get().email;
+ assertThat(preferred).isEqualTo(email);
+ }
}
@Test
public void deleteEmail() throws Exception {
- String email = "foo.bar@example.com";
- EmailInput input = newEmailInput(email);
- gApi.accounts().self().addEmail(input);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String email = "foo.bar@example.com";
+ EmailInput input = newEmailInput(email);
+ gApi.accounts().self().addEmail(input);
- requestScopeOperations.resetCurrentApiUser();
- assertThat(getEmails()).contains(email);
+ requestScopeOperations.resetCurrentApiUser();
+ assertThat(getEmails()).contains(email);
- accountIndexedCounter.clear();
- gApi.accounts().self().deleteEmail(input.email);
- accountIndexedCounter.assertReindexOf(admin);
+ accountIndexedCounter.clear();
+ gApi.accounts().self().deleteEmail(input.email);
+ accountIndexedCounter.assertReindexOf(admin);
- requestScopeOperations.resetCurrentApiUser();
- assertThat(getEmails()).doesNotContain(email);
+ requestScopeOperations.resetCurrentApiUser();
+ assertThat(getEmails()).doesNotContain(email);
+ }
}
@Test
public void deletePreferredEmail() throws Exception {
- String previous = gApi.accounts().self().get().email;
- String email = "foo.bar.baz@example.com";
- EmailInput input = new EmailInput();
- input.email = email;
- input.noConfirmation = true;
- input.preferred = true;
- gApi.accounts().self().addEmail(input);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String previous = gApi.accounts().self().get().email;
+ String email = "foo.bar.baz@example.com";
+ EmailInput input = new EmailInput();
+ input.email = email;
+ input.noConfirmation = true;
+ input.preferred = true;
+ gApi.accounts().self().addEmail(input);
- // Account is reindexed twice; once on adding the new email,
- // and then again on setting the email preferred.
- accountIndexedCounter.assertReindexOf(admin, 2);
+ // Account is reindexed twice; once on adding the new email,
+ // and then again on setting the email preferred.
+ accountIndexedCounter.assertReindexOf(admin, 2);
- // The new preferred email is set
- assertThat(gApi.accounts().self().get().email).isEqualTo(email);
+ // The new preferred email is set
+ assertThat(gApi.accounts().self().get().email).isEqualTo(email);
- accountIndexedCounter.clear();
- gApi.accounts().self().deleteEmail(input.email);
- accountIndexedCounter.assertReindexOf(admin);
+ accountIndexedCounter.clear();
+ gApi.accounts().self().deleteEmail(input.email);
+ accountIndexedCounter.assertReindexOf(admin);
- requestScopeOperations.resetCurrentApiUser();
- assertThat(getEmails()).containsExactly(previous);
- assertThat(gApi.accounts().self().get().email).isNull();
+ requestScopeOperations.resetCurrentApiUser();
+ assertThat(getEmails()).containsExactly(previous);
+ assertThat(gApi.accounts().self().get().email).isNull();
+ }
}
@Test
@@ -1306,36 +1394,45 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void deleteEmailFromCustomExternalIdSchemes() throws Exception {
- String email = "foo.bar@example.com";
- String extId1 = "foo:bar";
- String extId2 = "foo:baz";
- accountsUpdateProvider
- .get()
- .update(
- "Add External IDs",
- admin.id(),
- u ->
- u.addExternalId(
- ExternalId.createWithEmail(ExternalId.Key.parse(extId1), admin.id(), email))
- .addExternalId(
- ExternalId.createWithEmail(
- ExternalId.Key.parse(extId2), admin.id(), email)));
- accountIndexedCounter.assertReindexOf(admin);
- assertThat(
- gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
- .containsAtLeast(extId1, extId2);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String email = "foo.bar@example.com";
+ String extId1 = "foo:bar";
+ String extId2 = "foo:baz";
+ accountsUpdateProvider
+ .get()
+ .update(
+ "Add External IDs",
+ admin.id(),
+ u ->
+ u.addExternalId(
+ ExternalId.createWithEmail(
+ ExternalId.Key.parse(extId1), admin.id(), email))
+ .addExternalId(
+ ExternalId.createWithEmail(
+ ExternalId.Key.parse(extId2), admin.id(), email)));
+ accountIndexedCounter.assertReindexOf(admin);
+ assertThat(
+ gApi.accounts().self().getExternalIds().stream()
+ .map(e -> e.identity)
+ .collect(toSet()))
+ .containsAtLeast(extId1, extId2);
- requestScopeOperations.resetCurrentApiUser();
- assertThat(getEmails()).contains(email);
+ requestScopeOperations.resetCurrentApiUser();
+ assertThat(getEmails()).contains(email);
- gApi.accounts().self().deleteEmail(email);
- accountIndexedCounter.assertReindexOf(admin);
+ gApi.accounts().self().deleteEmail(email);
+ accountIndexedCounter.assertReindexOf(admin);
- requestScopeOperations.resetCurrentApiUser();
- assertThat(getEmails()).doesNotContain(email);
- assertThat(
- gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
- .containsNoneOf(extId1, extId2);
+ requestScopeOperations.resetCurrentApiUser();
+ assertThat(getEmails()).doesNotContain(email);
+ assertThat(
+ gApi.accounts().self().getExternalIds().stream()
+ .map(e -> e.identity)
+ .collect(toSet()))
+ .containsNoneOf(extId1, extId2);
+ }
}
@Test
@@ -1352,7 +1449,6 @@ public class AccountIT extends AbstractDaemonTest {
u.addExternalId(
ExternalId.createWithEmail(
ExternalId.Key.parse(ldapExternalId), admin.id(), ldapEmail)));
- accountIndexedCounter.assertReindexOf(admin);
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
.contains(ldapExternalId);
@@ -1391,7 +1487,6 @@ public class AccountIT extends AbstractDaemonTest {
.addExternalId(
ExternalId.createWithEmail(
ExternalId.Key.parse(ldapExternalId), admin.id(), ldapEmail)));
- accountIndexedCounter.assertReindexOf(admin);
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
.containsAtLeast(ldapExternalId, nonLdapExternalId);
@@ -1410,28 +1505,34 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void deleteEmailOfOtherUser() throws Exception {
- String email = "foo.bar@example.com";
- EmailInput input = new EmailInput();
- input.email = email;
- input.noConfirmation = true;
- gApi.accounts().id(user.id().get()).addEmail(input);
- accountIndexedCounter.assertReindexOf(user);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String email = "foo.bar@example.com";
+ EmailInput input = new EmailInput();
+ input.email = email;
+ input.noConfirmation = true;
+ gApi.accounts().id(user.id().get()).addEmail(input);
+ accountIndexedCounter.assertReindexOf(user);
- requestScopeOperations.setApiUser(user.id());
- assertThat(getEmails()).contains(email);
-
- // admin can delete email of user
- requestScopeOperations.setApiUser(admin.id());
- gApi.accounts().id(user.id().get()).deleteEmail(email);
- accountIndexedCounter.assertReindexOf(user);
+ requestScopeOperations.setApiUser(user.id());
+ assertThat(getEmails()).contains(email);
- requestScopeOperations.setApiUser(user.id());
- assertThat(getEmails()).doesNotContain(email);
+ // admin can delete email of user
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.accounts().id(user.id().get()).deleteEmail(email);
+ accountIndexedCounter.assertReindexOf(user);
- // user cannot delete email of admin
- exception.expect(AuthException.class);
- exception.expectMessage("modify account not permitted");
- gApi.accounts().id(admin.id().get()).deleteEmail(admin.email());
+ requestScopeOperations.setApiUser(user.id());
+ assertThat(getEmails()).doesNotContain(email);
+
+ // user cannot delete email of admin
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.accounts().id(admin.id().get()).deleteEmail(admin.email()));
+ assertThat(thrown).hasMessageThat().contains("modify account not permitted");
+ }
}
@Test
@@ -1497,17 +1598,21 @@ public class AccountIT extends AbstractDaemonTest {
public void putStatus() throws Exception {
List<String> statuses = ImmutableList.of("OOO", "Busy");
AccountInfo info;
- for (String status : statuses) {
- gApi.accounts().self().setStatus(status);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ for (String status : statuses) {
+ gApi.accounts().self().setStatus(status);
+ info = gApi.accounts().self().get();
+ assertUser(info, admin, status);
+ accountIndexedCounter.assertReindexOf(admin);
+ }
+
+ gApi.accounts().self().setStatus(null);
info = gApi.accounts().self().get();
- assertUser(info, admin, status);
+ assertUser(info, admin);
accountIndexedCounter.assertReindexOf(admin);
}
-
- gApi.accounts().self().setStatus(null);
- info = gApi.accounts().self().get();
- assertUser(info, admin);
- accountIndexedCounter.assertReindexOf(admin);
}
@Test
@@ -1525,617 +1630,92 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void userCannotSetNameOfOtherUser() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.accounts().id(admin.username()).setName("Admin McAdminface");
+ assertThrows(
+ AuthException.class,
+ () -> gApi.accounts().id(admin.username()).setName("Admin McAdminface"));
}
@Test
@Sandboxed
public void userCanSetNameOfOtherUserWithModifyAccountPermission() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.MODIFY_ACCOUNT);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.MODIFY_ACCOUNT).group(REGISTERED_USERS))
+ .update();
gApi.accounts().id(admin.username()).setName("Admin McAdminface");
assertThat(gApi.accounts().id(admin.username()).get().name).isEqualTo("Admin McAdminface");
}
@Test
public void fetchUserBranch() throws Exception {
- requestScopeOperations.setApiUser(user.id());
-
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user);
- String userRefName = RefNames.refsUsers(user.id());
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ requestScopeOperations.setApiUser(user.id());
- // remove default READ permissions
- try (ProjectConfigUpdate u = updateProject(allUsers)) {
- u.getConfig()
- .getAccessSection(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", true)
- .remove(new Permission(Permission.READ));
- u.save();
- }
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user);
+ String userRefName = RefNames.refsUsers(user.id());
- // deny READ permission that is inherited from All-Projects
- deny(allUsers, RefNames.REFS + "*", Permission.READ, ANONYMOUS_USERS);
+ // remove default READ permissions
+ try (ProjectConfigUpdate u = updateProject(allUsers)) {
+ u.getConfig()
+ .getAccessSection(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", true)
+ .remove(new Permission(Permission.READ));
+ u.save();
+ }
- // fetching user branch without READ permission fails
- try {
+ // deny READ permission that is inherited from All-Projects
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(deny(Permission.READ).ref(RefNames.REFS + "*").group(ANONYMOUS_USERS))
+ .update();
+
+ // fetching user branch without READ permission fails
+ assertThrows(TransportException.class, () -> fetch(allUsersRepo, userRefName + ":userRef"));
+
+ // allow each user to read its own user branch
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(
+ allow(Permission.READ)
+ .ref(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}")
+ .group(REGISTERED_USERS))
+ .update();
+
+ // fetch user branch using refs/users/YY/XXXXXXX
fetch(allUsersRepo, userRefName + ":userRef");
- fail("user branch is visible although no READ permission is granted");
- } catch (TransportException e) {
- // expected because no READ granted on user branch
- }
-
- // allow each user to read its own user branch
- grant(
- allUsers,
- RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}",
- Permission.READ,
- false,
- REGISTERED_USERS);
-
- // fetch user branch using refs/users/YY/XXXXXXX
- fetch(allUsersRepo, userRefName + ":userRef");
- Ref userRef = allUsersRepo.getRepository().exactRef("userRef");
- assertThat(userRef).isNotNull();
-
- // fetch user branch using refs/users/self
- fetch(allUsersRepo, RefNames.REFS_USERS_SELF + ":userSelfRef");
- Ref userSelfRef = allUsersRepo.getRepository().getRefDatabase().exactRef("userSelfRef");
- assertThat(userSelfRef).isNotNull();
- assertThat(userSelfRef.getObjectId()).isEqualTo(userRef.getObjectId());
-
- accountIndexedCounter.assertNoReindex();
-
- // fetching user branch of another user fails
- String otherUserRefName = RefNames.refsUsers(admin.id());
- exception.expect(TransportException.class);
- exception.expectMessage("Remote does not have " + otherUserRefName + " available for fetch.");
- fetch(allUsersRepo, otherUserRefName + ":otherUserRef");
- }
-
- @Test
- public void pushToUserBranch() throws Exception {
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
- allUsersRepo.reset("userRef");
- PushOneCommit push = pushFactory.create(admin.newIdent(), allUsersRepo);
- push.to(RefNames.refsUsers(admin.id())).assertOkStatus();
- accountIndexedCounter.assertReindexOf(admin);
-
- push = pushFactory.create(admin.newIdent(), allUsersRepo);
- push.to(RefNames.REFS_USERS_SELF).assertOkStatus();
- accountIndexedCounter.assertReindexOf(admin);
- }
-
- @Test
- public void pushToUserBranchForReview() throws Exception {
- String userRefName = RefNames.refsUsers(admin.id());
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, userRefName + ":userRef");
- allUsersRepo.reset("userRef");
- PushOneCommit push = pushFactory.create(admin.newIdent(), allUsersRepo);
- PushOneCommit.Result r = push.to(MagicBranch.NEW_CHANGE + userRefName);
- r.assertOkStatus();
- accountIndexedCounter.assertNoReindex();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).current().submit();
- accountIndexedCounter.assertReindexOf(admin);
-
- push = pushFactory.create(admin.newIdent(), allUsersRepo);
- r = push.to(MagicBranch.NEW_CHANGE + RefNames.REFS_USERS_SELF);
- r.assertOkStatus();
- accountIndexedCounter.assertNoReindex();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).current().submit();
- accountIndexedCounter.assertReindexOf(admin);
- }
-
- @Test
- public void pushAccountConfigToUserBranchForReviewAndSubmit() throws Exception {
- String userRef = RefNames.refsUsers(admin.id());
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- Config ac = getAccountConfig(allUsersRepo);
- ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_STATUS, "out-of-office");
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(MagicBranch.NEW_CHANGE + userRef);
- r.assertOkStatus();
- accountIndexedCounter.assertNoReindex();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef);
-
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).current().submit();
- accountIndexedCounter.assertReindexOf(admin);
-
- AccountInfo info = gApi.accounts().self().get();
- assertThat(info.email).isEqualTo(admin.email());
- assertThat(info.name).isEqualTo(admin.fullName());
- assertThat(info.status).isEqualTo("out-of-office");
- }
-
- @Test
- public void pushAccountConfigWithPrefEmailThatDoesNotExistAsExtIdToUserBranchForReviewAndSubmit()
- throws Exception {
- TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
- String userRef = RefNames.refsUsers(foo.id());
- accountIndexedCounter.clear();
-
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- String email = "some.email@example.com";
- Config ac = getAccountConfig(allUsersRepo);
- ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_PREFERRED_EMAIL, email);
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- foo.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(MagicBranch.NEW_CHANGE + userRef);
- r.assertOkStatus();
- accountIndexedCounter.assertNoReindex();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef);
-
- requestScopeOperations.setApiUser(foo.id());
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).current().submit();
-
- accountIndexedCounter.assertReindexOf(foo);
-
- AccountInfo info = gApi.accounts().self().get();
- assertThat(info.email).isEqualTo(email);
- assertThat(info.name).isEqualTo(foo.fullName());
- }
-
- @Test
- public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfConfigIsInvalid()
- throws Exception {
- String userRef = RefNames.refsUsers(admin.id());
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- "invalid config")
- .to(MagicBranch.NEW_CHANGE + userRef);
- r.assertOkStatus();
- accountIndexedCounter.assertNoReindex();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef);
-
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- String.format(
- "invalid account configuration: commit '%s' has an invalid '%s' file for account '%s':"
- + " Invalid config file %s in commit %s",
- r.getCommit().name(),
- AccountProperties.ACCOUNT_CONFIG,
- admin.id(),
- AccountProperties.ACCOUNT_CONFIG,
- r.getCommit().name()));
- gApi.changes().id(r.getChangeId()).current().submit();
- }
-
- @Test
- public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfPreferredEmailIsInvalid()
- throws Exception {
- String userRef = RefNames.refsUsers(admin.id());
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- String noEmail = "no.email";
- Config ac = getAccountConfig(allUsersRepo);
- ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_PREFERRED_EMAIL, noEmail);
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(MagicBranch.NEW_CHANGE + userRef);
- r.assertOkStatus();
- accountIndexedCounter.assertNoReindex();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef);
-
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- String.format(
- "invalid account configuration: invalid preferred email '%s' for account '%s'",
- noEmail, admin.id()));
- gApi.changes().id(r.getChangeId()).current().submit();
- }
-
- @Test
- public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfOwnAccountIsDeactivated()
- throws Exception {
- String userRef = RefNames.refsUsers(admin.id());
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- Config ac = getAccountConfig(allUsersRepo);
- ac.setBoolean(AccountProperties.ACCOUNT, null, AccountProperties.KEY_ACTIVE, false);
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(MagicBranch.NEW_CHANGE + userRef);
- r.assertOkStatus();
- accountIndexedCounter.assertNoReindex();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef);
-
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("invalid account configuration: cannot deactivate own account");
- gApi.changes().id(r.getChangeId()).current().submit();
- }
-
- @Test
- public void pushAccountConfigToUserBranchForReviewDeactivateOtherAccount() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
-
- TestAccount foo = accountCreator.create(name("foo"));
- assertThat(gApi.accounts().id(foo.id().get()).getActive()).isTrue();
- String userRef = RefNames.refsUsers(foo.id());
- accountIndexedCounter.clear();
-
- grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
- grantLabel("Code-Review", -2, 2, allUsers, userRef, false, adminGroupUuid(), false);
- grant(allUsers, userRef, Permission.SUBMIT, false, adminGroupUuid());
-
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- Config ac = getAccountConfig(allUsersRepo);
- ac.setBoolean(AccountProperties.ACCOUNT, null, AccountProperties.KEY_ACTIVE, false);
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(MagicBranch.NEW_CHANGE + userRef);
- r.assertOkStatus();
- accountIndexedCounter.assertNoReindex();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef);
-
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).current().submit();
- accountIndexedCounter.assertReindexOf(foo);
-
- assertThat(gApi.accounts().id(foo.id().get()).getActive()).isFalse();
- }
-
- @Test
- public void pushWatchConfigToUserBranch() throws Exception {
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
- allUsersRepo.reset("userRef");
-
- Config wc = new Config();
- wc.setString(
- ProjectWatches.PROJECT,
- project.get(),
- ProjectWatches.KEY_NOTIFY,
- ProjectWatches.NotifyValue.create(null, EnumSet.of(NotifyType.ALL_COMMENTS)).toString());
- PushOneCommit push =
- pushFactory.create(
- admin.newIdent(),
- allUsersRepo,
- "Add project watch",
- ProjectWatches.WATCH_CONFIG,
- wc.toText());
- push.to(RefNames.REFS_USERS_SELF).assertOkStatus();
- accountIndexedCounter.assertReindexOf(admin);
-
- String invalidNotifyValue = "]invalid[";
- wc.setString(
- ProjectWatches.PROJECT, project.get(), ProjectWatches.KEY_NOTIFY, invalidNotifyValue);
- push =
- pushFactory.create(
- admin.newIdent(),
- allUsersRepo,
- "Add invalid project watch",
- ProjectWatches.WATCH_CONFIG,
- wc.toText());
- PushOneCommit.Result r = push.to(RefNames.REFS_USERS_SELF);
- r.assertErrorStatus("invalid account configuration");
- r.assertMessage(
- String.format(
- "%s: Invalid project watch of account %d for project %s: %s",
- ProjectWatches.WATCH_CONFIG, admin.id().get(), project.get(), invalidNotifyValue));
- }
-
- @Test
- public void pushAccountConfigToUserBranch() throws Exception {
- TestAccount oooUser = accountCreator.create("away", "away@mail.invalid", "Ambrose Way");
- requestScopeOperations.setApiUser(oooUser.id());
-
- // Must clone as oooUser to ensure the push is allowed.
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, oooUser);
- fetch(allUsersRepo, RefNames.refsUsers(oooUser.id()) + ":userRef");
- allUsersRepo.reset("userRef");
-
- Config ac = getAccountConfig(allUsersRepo);
- ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_STATUS, "out-of-office");
-
- accountIndexedCounter.clear();
- pushFactory
- .create(
- oooUser.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(RefNames.refsUsers(oooUser.id()))
- .assertOkStatus();
-
- accountIndexedCounter.assertReindexOf(oooUser);
-
- AccountInfo info = gApi.accounts().self().get();
- assertThat(info.email).isEqualTo(oooUser.email());
- assertThat(info.name).isEqualTo(oooUser.fullName());
- assertThat(info.status).isEqualTo("out-of-office");
- }
-
- @Test
- public void pushAccountConfigToUserBranchIsRejectedIfConfigIsInvalid() throws Exception {
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
- allUsersRepo.reset("userRef");
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- "invalid config")
- .to(RefNames.REFS_USERS_SELF);
- r.assertErrorStatus("invalid account configuration");
- r.assertMessage(
- String.format(
- "commit '%s' has an invalid '%s' file for account '%s':"
- + " Invalid config file %s in commit %s",
- r.getCommit().name(),
- AccountProperties.ACCOUNT_CONFIG,
- admin.id(),
- AccountProperties.ACCOUNT_CONFIG,
- r.getCommit().name()));
- accountIndexedCounter.assertNoReindex();
- }
-
- @Test
- public void pushAccountConfigToUserBranchIsRejectedIfPreferredEmailIsInvalid() throws Exception {
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
- allUsersRepo.reset("userRef");
-
- String noEmail = "no.email";
- Config ac = getAccountConfig(allUsersRepo);
- ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_PREFERRED_EMAIL, noEmail);
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(RefNames.REFS_USERS_SELF);
- r.assertErrorStatus("invalid account configuration");
- r.assertMessage(
- String.format("invalid preferred email '%s' for account '%s'", noEmail, admin.id()));
- accountIndexedCounter.assertNoReindex();
- }
-
- @Test
- public void pushAccountConfigToUserBranchInvalidPreferredEmailButNotChanged() throws Exception {
- TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
- String userRef = RefNames.refsUsers(foo.id());
-
- String noEmail = "no.email";
- accountsUpdateProvider
- .get()
- .update("Set Preferred Email", foo.id(), u -> u.setPreferredEmail(noEmail));
- accountIndexedCounter.clear();
-
- grant(allUsers, userRef, Permission.PUSH, false, REGISTERED_USERS);
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- String status = "in vacation";
- Config ac = getAccountConfig(allUsersRepo);
- ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_STATUS, status);
-
- pushFactory
- .create(
- foo.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(userRef)
- .assertOkStatus();
- accountIndexedCounter.assertReindexOf(foo);
-
- AccountInfo info = gApi.accounts().id(foo.id().get()).get();
- assertThat(info.email).isEqualTo(noEmail);
- assertThat(info.name).isEqualTo(foo.fullName());
- assertThat(info.status).isEqualTo(status);
- }
-
- @Test
- public void pushAccountConfigToUserBranchIfPreferredEmailDoesNotExistAsExtId() throws Exception {
- TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
- String userRef = RefNames.refsUsers(foo.id());
- accountIndexedCounter.clear();
-
- grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
-
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- String email = "some.email@example.com";
- Config ac = getAccountConfig(allUsersRepo);
- ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_PREFERRED_EMAIL, email);
-
- pushFactory
- .create(
- foo.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(userRef)
- .assertOkStatus();
- accountIndexedCounter.assertReindexOf(foo);
-
- AccountInfo info = gApi.accounts().id(foo.id().get()).get();
- assertThat(info.email).isEqualTo(email);
- assertThat(info.name).isEqualTo(foo.fullName());
- }
-
- @Test
- public void pushAccountConfigToUserBranchIsRejectedIfOwnAccountIsDeactivated() throws Exception {
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
- allUsersRepo.reset("userRef");
-
- Config ac = getAccountConfig(allUsersRepo);
- ac.setBoolean(AccountProperties.ACCOUNT, null, AccountProperties.KEY_ACTIVE, false);
-
- PushOneCommit.Result r =
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(RefNames.REFS_USERS_SELF);
- r.assertErrorStatus("invalid account configuration");
- r.assertMessage("cannot deactivate own account");
- accountIndexedCounter.assertNoReindex();
- }
-
- @Test
- public void pushAccountConfigToUserBranchDeactivateOtherAccount() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
-
- TestAccount foo = accountCreator.create(name("foo"));
- assertThat(gApi.accounts().id(foo.id().get()).getActive()).isTrue();
- String userRef = RefNames.refsUsers(foo.id());
- accountIndexedCounter.clear();
-
- grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
-
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, userRef + ":userRef");
- allUsersRepo.reset("userRef");
-
- Config ac = getAccountConfig(allUsersRepo);
- ac.setBoolean(AccountProperties.ACCOUNT, null, AccountProperties.KEY_ACTIVE, false);
-
- pushFactory
- .create(
- admin.newIdent(),
- allUsersRepo,
- "Update account config",
- AccountProperties.ACCOUNT_CONFIG,
- ac.toText())
- .to(userRef)
- .assertOkStatus();
- accountIndexedCounter.assertReindexOf(foo);
-
- assertThat(gApi.accounts().id(foo.id().get()).getActive()).isFalse();
- }
-
- @Test
- public void cannotCreateUserBranch() throws Exception {
- grant(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE);
- grant(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH);
-
- String userRef = RefNames.refsUsers(new Account.Id(seq.nextAccountId()));
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef);
- r.assertErrorStatus();
- assertThat(r.getMessage()).contains("Not allowed to create user branch.");
-
- try (Repository repo = repoManager.openRepository(allUsers)) {
- assertThat(repo.exactRef(userRef)).isNull();
+ Ref userRef = allUsersRepo.getRepository().exactRef("userRef");
+ assertThat(userRef).isNotNull();
+
+ // fetch user branch using refs/users/self
+ fetch(allUsersRepo, RefNames.REFS_USERS_SELF + ":userSelfRef");
+ Ref userSelfRef = allUsersRepo.getRepository().getRefDatabase().exactRef("userSelfRef");
+ assertThat(userSelfRef).isNotNull();
+ assertThat(userSelfRef.getObjectId()).isEqualTo(userRef.getObjectId());
+
+ accountIndexedCounter.assertNoReindex();
+
+ // fetching user branch of another user fails
+ String otherUserRefName = RefNames.refsUsers(admin.id());
+ TransportException thrown =
+ assertThrows(
+ TransportException.class,
+ () -> fetch(allUsersRepo, otherUserRefName + ":otherUserRef"));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Remote does not have " + otherUserRefName + " available for fetch.");
}
}
@Test
- public void createUserBranchWithAccessDatabaseCapability() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- grant(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE);
- grant(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH);
-
- String userRef = RefNames.refsUsers(new Account.Id(seq.nextAccountId()));
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef).assertOkStatus();
-
- try (Repository repo = repoManager.openRepository(allUsers)) {
- assertThat(repo.exactRef(userRef)).isNotNull();
- }
- }
-
- @Test
- public void cannotCreateNonUserBranchUnderRefsUsersWithAccessDatabaseCapability()
- throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- grant(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE);
- grant(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH);
-
- String userRef = RefNames.REFS_USERS + "foo";
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef);
- r.assertErrorStatus();
- assertThat(r.getMessage()).contains("Not allowed to create non-user branch under refs/users/.");
-
- try (Repository repo = repoManager.openRepository(allUsers)) {
- assertThat(repo.exactRef(userRef)).isNull();
+ public void refsUsersSelfIsAdvertised() throws Exception {
+ TestRepository<?> testRepository = cloneProject(allUsers, user);
+ try (Git git = testRepository.git()) {
+ List<String> advertisedRefs =
+ git.lsRemote().call().stream().map(Ref::getName).collect(toList());
+ assertThat(advertisedRefs).contains(RefNames.REFS_USERS_SELF);
}
}
@@ -2145,8 +1725,12 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(repo.exactRef(RefNames.REFS_USERS_DEFAULT)).isNull();
}
- grant(allUsers, RefNames.REFS_USERS_DEFAULT, Permission.CREATE);
- grant(allUsers, RefNames.REFS_USERS_DEFAULT, Permission.PUSH);
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_USERS_DEFAULT).group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_USERS_DEFAULT).group(adminGroupUuid()))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
pushFactory
@@ -2161,12 +1745,15 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void cannotDeleteUserBranch() throws Exception {
- grant(
- allUsers,
- RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}",
- Permission.DELETE,
- true,
- REGISTERED_USERS);
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(
+ allow(Permission.DELETE)
+ .ref(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}")
+ .group(REGISTERED_USERS)
+ .force(true))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
String userRef = RefNames.refsUsers(admin.id());
@@ -2182,13 +1769,19 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void deleteUserBranchWithAccessDatabaseCapability() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- grant(
- allUsers,
- RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}",
- Permission.DELETE,
- true,
- REGISTERED_USERS);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(
+ allow(Permission.DELETE)
+ .ref(RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}")
+ .group(REGISTERED_USERS)
+ .force(true))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
String userRef = RefNames.refsUsers(admin.id());
@@ -2217,9 +1810,10 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(sender.getMessages().get(0).body()).contains("new GPG keys have been added");
requestScopeOperations.setApiUser(user.id());
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage(id);
- gApi.accounts().self().gpgKey(id).get();
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.accounts().self().gpgKey(id).get());
+ assertThat(thrown).hasMessageThat().contains(id);
}
@Test
@@ -2229,8 +1823,7 @@ public class AccountIT extends AbstractDaemonTest {
sender.clear();
requestScopeOperations.setApiUser(admin.id());
- exception.expect(ResourceNotFoundException.class);
- addGpgKey(user, key.getPublicKeyArmored());
+ assertThrows(ResourceNotFoundException.class, () -> addGpgKey(user, key.getPublicKeyArmored()));
}
@Test
@@ -2259,212 +1852,251 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void addOtherUsersGpgKey_Conflict() throws Exception {
- // Both users have a matching external ID for this key.
- addExternalIdEmail(admin, "test5@example.com");
- accountsUpdateProvider
- .get()
- .update(
- "Add External ID",
- user.id(),
- u -> u.addExternalId(ExternalId.create("foo", "myId", user.id())));
- accountIndexedCounter.assertReindexOf(user);
-
- TestKey key = validKeyWithSecondUserId();
- addGpgKey(key.getPublicKeyArmored());
- requestScopeOperations.setApiUser(user.id());
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ // Both users have a matching external ID for this key.
+ addExternalIdEmail(admin, "test5@example.com");
+ accountIndexedCounter.clear();
+ accountsUpdateProvider
+ .get()
+ .update(
+ "Add External ID",
+ user.id(),
+ u -> u.addExternalId(ExternalId.create("foo", "myId", user.id())));
+ accountIndexedCounter.assertReindexOf(user);
+
+ TestKey key = validKeyWithSecondUserId();
+ addGpgKey(key.getPublicKeyArmored());
+ requestScopeOperations.setApiUser(user.id());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("GPG key already associated with another account");
- addGpgKey(user, key.getPublicKeyArmored());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> addGpgKey(user, key.getPublicKeyArmored()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("GPG key already associated with another account");
+ }
}
@Test
public void listGpgKeys() throws Exception {
- List<TestKey> keys = allValidKeys();
- List<String> toAdd = new ArrayList<>(keys.size());
- for (TestKey key : keys) {
- addExternalIdEmail(admin, PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress());
- toAdd.add(key.getPublicKeyArmored());
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ List<TestKey> keys = allValidKeys();
+ List<String> toAdd = new ArrayList<>(keys.size());
+ for (TestKey key : keys) {
+ addExternalIdEmail(
+ admin, PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress());
+ toAdd.add(key.getPublicKeyArmored());
+ }
+ accountIndexedCounter.clear();
+ gApi.accounts().self().putGpgKeys(toAdd, ImmutableList.of());
+ assertKeys(keys);
+ accountIndexedCounter.assertReindexOf(admin);
}
- gApi.accounts().self().putGpgKeys(toAdd, ImmutableList.of());
- assertKeys(keys);
- accountIndexedCounter.assertReindexOf(admin);
}
@Test
public void deleteGpgKey() throws Exception {
- TestKey key = validKeyWithoutExpiration();
- String id = key.getKeyIdString();
- addExternalIdEmail(admin, "test1@example.com");
- addGpgKey(key.getPublicKeyArmored());
- assertKeys(key);
-
- sender.clear();
- gApi.accounts().self().gpgKey(id).delete();
- accountIndexedCounter.assertReindexOf(admin);
- assertKeys();
- assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).body()).contains("GPG keys have been deleted");
-
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage(id);
- gApi.accounts().self().gpgKey(id).get();
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestKey key = validKeyWithoutExpiration();
+ String id = key.getKeyIdString();
+ addExternalIdEmail(admin, "test1@example.com");
+ addGpgKey(key.getPublicKeyArmored());
+ assertKeys(key);
+ accountIndexedCounter.clear();
+
+ sender.clear();
+ gApi.accounts().self().gpgKey(id).delete();
+ accountIndexedCounter.assertReindexOf(admin);
+ assertKeys();
+ assertThat(sender.getMessages()).hasSize(1);
+ assertThat(sender.getMessages().get(0).body()).contains("GPG keys have been deleted");
+
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.accounts().self().gpgKey(id).get());
+ assertThat(thrown).hasMessageThat().contains(id);
+ }
}
@Test
public void addAndRemoveGpgKeys() throws Exception {
- for (TestKey key : allValidKeys()) {
- addExternalIdEmail(admin, PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress());
- }
- TestKey key1 = validKeyWithoutExpiration();
- TestKey key2 = validKeyWithExpiration();
- TestKey key5 = validKeyWithSecondUserId();
-
- Map<String, GpgKeyInfo> infos =
- gApi.accounts()
- .self()
- .putGpgKeys(
- ImmutableList.of(key1.getPublicKeyArmored(), key2.getPublicKeyArmored()),
- ImmutableList.of(key5.getKeyIdString()));
- assertThat(infos.keySet()).containsExactly(key1.getKeyIdString(), key2.getKeyIdString());
- assertKeys(key1, key2);
- accountIndexedCounter.assertReindexOf(admin);
-
- infos =
- gApi.accounts()
- .self()
- .putGpgKeys(
- ImmutableList.of(key5.getPublicKeyArmored()),
- ImmutableList.of(key1.getKeyIdString()));
- assertThat(infos.keySet()).containsExactly(key1.getKeyIdString(), key5.getKeyIdString());
- assertKeyMapContains(key5, infos);
- assertThat(infos.get(key1.getKeyIdString()).key).isNull();
- assertKeys(key2, key5);
- accountIndexedCounter.assertReindexOf(admin);
-
- exception.expect(BadRequestException.class);
- exception.expectMessage("Cannot both add and delete key: " + keyToString(key2.getPublicKey()));
- gApi.accounts()
- .self()
- .putGpgKeys(
- ImmutableList.of(key2.getPublicKeyArmored()), ImmutableList.of(key2.getKeyIdString()));
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ for (TestKey key : allValidKeys()) {
+ addExternalIdEmail(
+ admin, PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress());
+ }
+ accountIndexedCounter.clear();
+ TestKey key1 = validKeyWithoutExpiration();
+ TestKey key2 = validKeyWithExpiration();
+ TestKey key5 = validKeyWithSecondUserId();
+
+ Map<String, GpgKeyInfo> infos =
+ gApi.accounts()
+ .self()
+ .putGpgKeys(
+ ImmutableList.of(key1.getPublicKeyArmored(), key2.getPublicKeyArmored()),
+ ImmutableList.of(key5.getKeyIdString()));
+ assertThat(infos.keySet()).containsExactly(key1.getKeyIdString(), key2.getKeyIdString());
+ assertKeys(key1, key2);
+ accountIndexedCounter.assertReindexOf(admin);
+
+ infos =
+ gApi.accounts()
+ .self()
+ .putGpgKeys(
+ ImmutableList.of(key5.getPublicKeyArmored()),
+ ImmutableList.of(key1.getKeyIdString()));
+ assertThat(infos.keySet()).containsExactly(key1.getKeyIdString(), key5.getKeyIdString());
+ assertKeyMapContains(key5, infos);
+ assertThat(infos.get(key1.getKeyIdString()).key).isNull();
+ assertKeys(key2, key5);
+ accountIndexedCounter.assertReindexOf(admin);
+
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.accounts()
+ .self()
+ .putGpgKeys(
+ ImmutableList.of(key2.getPublicKeyArmored()),
+ ImmutableList.of(key2.getKeyIdString())));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Cannot both add and delete key: " + keyToString(key2.getPublicKey()));
+ }
}
@Test
public void addMalformedGpgKey() throws Exception {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\ntest\n-----END PGP PUBLIC KEY BLOCK-----";
- exception.expect(BadRequestException.class);
- exception.expectMessage("Failed to parse GPG keys");
- addGpgKey(key);
+ BadRequestException thrown = assertThrows(BadRequestException.class, () -> addGpgKey(key));
+ assertThat(thrown).hasMessageThat().contains("Failed to parse GPG keys");
}
@Test
@UseSsh
public void sshKeys() throws Exception {
- // The test account should initially have exactly one ssh key
- List<SshKeyInfo> info = gApi.accounts().self().listSshKeys();
- assertThat(info).hasSize(1);
- assertSequenceNumbers(info);
- SshKeyInfo key = info.get(0);
- KeyPair keyPair = sshKeys.getKeyPair(admin);
- String initial = TestSshKeys.publicKey(keyPair, admin.email());
- assertThat(key.sshPublicKey).isEqualTo(initial);
- accountIndexedCounter.assertNoReindex();
-
- // Add a new key
- sender.clear();
- String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
- gApi.accounts().self().addSshKey(newKey);
- info = gApi.accounts().self().listSshKeys();
- assertThat(info).hasSize(2);
- assertSequenceNumbers(info);
- accountIndexedCounter.assertReindexOf(admin);
- assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).body()).contains("new SSH keys have been added");
-
- // Add an existing key (the request succeeds, but the key isn't added again)
- sender.clear();
- gApi.accounts().self().addSshKey(initial);
- info = gApi.accounts().self().listSshKeys();
- assertThat(info).hasSize(2);
- assertSequenceNumbers(info);
- accountIndexedCounter.assertNoReindex();
- // TODO: Issue 10769: Adding an already existing key should not result in a notification email
- assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).body()).contains("new SSH keys have been added");
-
- // Add another new key
- sender.clear();
- String newKey2 = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
- gApi.accounts().self().addSshKey(newKey2);
- info = gApi.accounts().self().listSshKeys();
- assertThat(info).hasSize(3);
- assertSequenceNumbers(info);
- accountIndexedCounter.assertReindexOf(admin);
- assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).body()).contains("new SSH keys have been added");
-
- // Delete second key
- sender.clear();
- gApi.accounts().self().deleteSshKey(2);
- info = gApi.accounts().self().listSshKeys();
- assertThat(info).hasSize(2);
- assertThat(info.get(0).seq).isEqualTo(1);
- assertThat(info.get(1).seq).isEqualTo(3);
- accountIndexedCounter.assertReindexOf(admin);
-
- assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).body()).contains("SSH keys have been deleted");
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ // The test account should initially have exactly one ssh key
+ List<SshKeyInfo> info = gApi.accounts().self().listSshKeys();
+ assertThat(info).hasSize(1);
+ assertSequenceNumbers(info);
+ SshKeyInfo key = info.get(0);
+ KeyPair keyPair = sshKeys.getKeyPair(admin);
+ String initial = TestSshKeys.publicKey(keyPair, admin.email());
+ assertThat(key.sshPublicKey).isEqualTo(initial);
+ accountIndexedCounter.assertNoReindex();
+
+ // Add a new key
+ sender.clear();
+ String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
+ gApi.accounts().self().addSshKey(newKey);
+ info = gApi.accounts().self().listSshKeys();
+ assertThat(info).hasSize(2);
+ assertSequenceNumbers(info);
+ accountIndexedCounter.assertReindexOf(admin);
+ assertThat(sender.getMessages()).hasSize(1);
+ assertThat(sender.getMessages().get(0).body()).contains("new SSH keys have been added");
+
+ // Add an existing key (the request succeeds, but the key isn't added again)
+ sender.clear();
+ gApi.accounts().self().addSshKey(initial);
+ info = gApi.accounts().self().listSshKeys();
+ assertThat(info).hasSize(2);
+ assertSequenceNumbers(info);
+ accountIndexedCounter.assertNoReindex();
+ // TODO: Issue 10769: Adding an already existing key should not result in a notification email
+ assertThat(sender.getMessages()).hasSize(1);
+ assertThat(sender.getMessages().get(0).body()).contains("new SSH keys have been added");
+
+ // Add another new key
+ sender.clear();
+ String newKey2 = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
+ gApi.accounts().self().addSshKey(newKey2);
+ info = gApi.accounts().self().listSshKeys();
+ assertThat(info).hasSize(3);
+ assertSequenceNumbers(info);
+ accountIndexedCounter.assertReindexOf(admin);
+ assertThat(sender.getMessages()).hasSize(1);
+ assertThat(sender.getMessages().get(0).body()).contains("new SSH keys have been added");
+
+ // Delete second key
+ sender.clear();
+ gApi.accounts().self().deleteSshKey(2);
+ info = gApi.accounts().self().listSshKeys();
+ assertThat(info).hasSize(2);
+ assertThat(info.get(0).seq).isEqualTo(1);
+ assertThat(info.get(1).seq).isEqualTo(3);
+ accountIndexedCounter.assertReindexOf(admin);
- // Mark first key as invalid
- assertThat(info.get(0).valid).isTrue();
- authorizedKeys.markKeyInvalid(admin.id(), 1);
- info = gApi.accounts().self().listSshKeys();
- assertThat(info).hasSize(2);
- assertThat(info.get(0).seq).isEqualTo(1);
- assertThat(info.get(0).valid).isFalse();
- assertThat(info.get(1).seq).isEqualTo(3);
- accountIndexedCounter.assertReindexOf(admin);
+ assertThat(sender.getMessages()).hasSize(1);
+ assertThat(sender.getMessages().get(0).body()).contains("SSH keys have been deleted");
+
+ // Mark first key as invalid
+ assertThat(info.get(0).valid).isTrue();
+ authorizedKeys.markKeyInvalid(admin.id(), 1);
+ info = gApi.accounts().self().listSshKeys();
+ assertThat(info).hasSize(2);
+ assertThat(info.get(0).seq).isEqualTo(1);
+ assertThat(info.get(0).valid).isFalse();
+ assertThat(info.get(1).seq).isEqualTo(3);
+ accountIndexedCounter.assertReindexOf(admin);
+ }
}
@Test
@UseSsh
public void adminCanAddOrRemoveSshKeyOnOtherAccount() throws Exception {
- // The test account should initially have exactly one ssh key
- List<SshKeyInfo> info = gApi.accounts().self().listSshKeys();
- assertThat(info).hasSize(1);
- assertSequenceNumbers(info);
- SshKeyInfo key = info.get(0);
- KeyPair keyPair = sshKeys.getKeyPair(admin);
- String initial = TestSshKeys.publicKey(keyPair, admin.email());
- assertThat(key.sshPublicKey).isEqualTo(initial);
- accountIndexedCounter.assertNoReindex();
-
- // Add a new key
- sender.clear();
- String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), user.email());
- gApi.accounts().id(user.username()).addSshKey(newKey);
- info = gApi.accounts().id(user.username()).listSshKeys();
- assertThat(info).hasSize(2);
- assertSequenceNumbers(info);
- accountIndexedCounter.assertReindexOf(user);
-
- assertThat(sender.getMessages()).hasSize(1);
- Message message = sender.getMessages().get(0);
- assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
- assertThat(message.body()).contains("new SSH keys have been added");
-
- // Delete key
- sender.clear();
- gApi.accounts().id(user.username()).deleteSshKey(1);
- info = gApi.accounts().id(user.username()).listSshKeys();
- assertThat(info).hasSize(1);
- accountIndexedCounter.assertReindexOf(user);
-
- assertThat(sender.getMessages()).hasSize(1);
- message = sender.getMessages().get(0);
- assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
- assertThat(message.body()).contains("SSH keys have been deleted");
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ // The test account should initially have exactly one ssh key
+ List<SshKeyInfo> info = gApi.accounts().self().listSshKeys();
+ assertThat(info).hasSize(1);
+ assertSequenceNumbers(info);
+ SshKeyInfo key = info.get(0);
+ KeyPair keyPair = sshKeys.getKeyPair(admin);
+ String initial = TestSshKeys.publicKey(keyPair, admin.email());
+ assertThat(key.sshPublicKey).isEqualTo(initial);
+ accountIndexedCounter.assertNoReindex();
+
+ // Add a new key
+ sender.clear();
+ String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), user.email());
+ gApi.accounts().id(user.username()).addSshKey(newKey);
+ info = gApi.accounts().id(user.username()).listSshKeys();
+ assertThat(info).hasSize(2);
+ assertSequenceNumbers(info);
+ accountIndexedCounter.assertReindexOf(user);
+
+ assertThat(sender.getMessages()).hasSize(1);
+ Message message = sender.getMessages().get(0);
+ assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(message.body()).contains("new SSH keys have been added");
+
+ // Delete key
+ sender.clear();
+ gApi.accounts().id(user.username()).deleteSshKey(1);
+ info = gApi.accounts().id(user.username()).listSshKeys();
+ assertThat(info).hasSize(1);
+ accountIndexedCounter.assertReindexOf(user);
+
+ assertThat(sender.getMessages()).hasSize(1);
+ message = sender.getMessages().get(0);
+ assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(message.body()).contains("SSH keys have been deleted");
+ }
}
@Test
@@ -2472,40 +2104,47 @@ public class AccountIT extends AbstractDaemonTest {
public void userCannotAddSshKeyToOtherAccount() throws Exception {
String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.accounts().id(admin.username()).addSshKey(newKey);
+ assertThrows(AuthException.class, () -> gApi.accounts().id(admin.username()).addSshKey(newKey));
}
@Test
@UseSsh
public void userCannotDeleteSshKeyOfOtherAccount() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(ResourceNotFoundException.class);
- gApi.accounts().id(admin.username()).deleteSshKey(0);
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.accounts().id(admin.username()).deleteSshKey(0));
}
// reindex is tested by {@link AbstractQueryAccountsTest#reindex}
@Test
public void reindexPermissions() throws Exception {
- // admin can reindex any account
- requestScopeOperations.setApiUser(admin.id());
- gApi.accounts().id(user.username()).index();
- accountIndexedCounter.assertReindexOf(user);
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ // admin can reindex any account
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.accounts().id(user.username()).index();
+ accountIndexedCounter.assertReindexOf(user);
- // user can reindex own account
- requestScopeOperations.setApiUser(user.id());
- gApi.accounts().self().index();
- accountIndexedCounter.assertReindexOf(user);
+ // user can reindex own account
+ requestScopeOperations.setApiUser(user.id());
+ gApi.accounts().self().index();
+ accountIndexedCounter.assertReindexOf(user);
- // user cannot reindex any account
- exception.expect(AuthException.class);
- exception.expectMessage("modify account not permitted");
- gApi.accounts().id(admin.username()).index();
+ // user cannot reindex any account
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.accounts().id(admin.username()).index());
+ assertThat(thrown).hasMessageThat().contains("modify account not permitted");
+ }
}
@Test
public void checkConsistency() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
requestScopeOperations.resetCurrentApiUser();
// Create an account with a preferred email.
@@ -2560,22 +2199,21 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void checkMetaId() throws Exception {
// metaId is set when account is loaded
- assertThat(accounts.get(admin.id()).get().getAccount().getMetaId())
- .isEqualTo(getMetaId(admin.id()));
+ assertThat(accounts.get(admin.id()).get().account().metaId()).isEqualTo(getMetaId(admin.id()));
// metaId is set when account is created
AccountsUpdate au = accountsUpdateProvider.get();
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
AccountState accountState = au.insert("Create Test Account", accountId, u -> {});
- assertThat(accountState.getAccount().getMetaId()).isEqualTo(getMetaId(accountId));
+ assertThat(accountState.account().metaId()).isEqualTo(getMetaId(accountId));
// metaId is set when account is updated
Optional<AccountState> updatedAccountState =
au.update("Set Full Name", accountId, u -> u.setFullName("foo"));
assertThat(updatedAccountState).isPresent();
- Account updatedAccount = updatedAccountState.get().getAccount();
- assertThat(accountState.getAccount().getMetaId()).isNotEqualTo(updatedAccount.getMetaId());
- assertThat(updatedAccount.getMetaId()).isEqualTo(getMetaId(accountId));
+ Account updatedAccount = updatedAccountState.get().account();
+ assertThat(accountState.account().metaId()).isNotEqualTo(updatedAccount.metaId());
+ assertThat(updatedAccount.metaId()).isEqualTo(getMetaId(accountId));
}
private EmailInput newEmailInput(String email, boolean noConfirmation) {
@@ -2629,12 +2267,9 @@ public class AccountIT extends AbstractDaemonTest {
"@", "@foo", "-", "-foo", "_", "_foo", "!", "+", "{", "}", "*", "%", "#", "$", "&", "’",
"^", "=", "~");
for (String name : invalidNames) {
- try {
- gApi.accounts().create(name);
- fail(String.format("Expected BadRequestException for username [%s]", name));
- } catch (BadRequestException e) {
- assertThat(e).hasMessageThat().isEqualTo(String.format("Invalid username '%s'", name));
- }
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.accounts().create(name));
+ assertThat(thrown).hasMessageThat().isEqualTo(String.format("Invalid username '%s'", name));
}
}
@@ -2693,7 +2328,11 @@ public class AccountIT extends AbstractDaemonTest {
externalIds,
metaDataUpdateInternalFactory,
new RetryHelper(
- cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
+ cfg,
+ retryMetrics,
+ null,
+ new PluginSetContext<>(DynamicSet.emptySet(), PluginMetrics.DISABLED_INSTANCE),
+ r -> r.withBlockStrategy(noSleepBlockStrategy)),
extIdNotesFactory,
ident,
ident,
@@ -2719,9 +2358,9 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(doneBgUpdate.get()).isTrue();
assertThat(updatedAccountState).isPresent();
- Account updatedAccount = updatedAccountState.get().getAccount();
- assertThat(updatedAccount.getStatus()).isEqualTo(status);
- assertThat(updatedAccount.getFullName()).isEqualTo(fullName);
+ Account updatedAccount = updatedAccountState.get().account();
+ assertThat(updatedAccount.status()).isEqualTo(status);
+ assertThat(updatedAccount.fullName()).isEqualTo(fullName);
accountInfo = gApi.accounts().id(admin.id().get()).get();
assertThat(accountInfo.status).isEqualTo(status);
@@ -2746,6 +2385,7 @@ public class AccountIT extends AbstractDaemonTest {
cfg,
retryMetrics,
null,
+ new PluginSetContext<>(DynamicSet.emptySet(), PluginMetrics.DISABLED_INSTANCE),
r ->
r.withStopStrategy(StopStrategies.stopAfterAttempt(status.size()))
.withBlockStrategy(noSleepBlockStrategy)),
@@ -2770,17 +2410,14 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(accountInfo.status).isNull();
assertThat(accountInfo.name).isNotEqualTo(fullName);
- try {
- update.update("Set Full Name", admin.id(), u -> u.setFullName(fullName));
- fail("expected LockFailureException");
- } catch (LockFailureException e) {
- // Ignore, expected
- }
+ assertThrows(
+ LockFailureException.class,
+ () -> update.update("Set Full Name", admin.id(), u -> u.setFullName(fullName)));
assertThat(bgCounter.get()).isEqualTo(status.size());
- Account updatedAccount = accounts.get(admin.id()).get().getAccount();
- assertThat(updatedAccount.getStatus()).isEqualTo(Iterables.getLast(status));
- assertThat(updatedAccount.getFullName()).isEqualTo(admin.fullName());
+ Account updatedAccount = accounts.get(admin.id()).get().account();
+ assertThat(updatedAccount.status()).isEqualTo(Iterables.getLast(status));
+ assertThat(updatedAccount.fullName()).isEqualTo(admin.fullName());
accountInfo = gApi.accounts().id(admin.id().get()).get();
assertThat(accountInfo.status).isEqualTo(Iterables.getLast(status));
@@ -2803,7 +2440,11 @@ public class AccountIT extends AbstractDaemonTest {
externalIds,
metaDataUpdateInternalFactory,
new RetryHelper(
- cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
+ cfg,
+ retryMetrics,
+ null,
+ new PluginSetContext<>(DynamicSet.emptySet(), PluginMetrics.DISABLED_INSTANCE),
+ r -> r.withBlockStrategy(noSleepBlockStrategy)),
extIdNotesFactory,
ident,
ident,
@@ -2826,12 +2467,12 @@ public class AccountIT extends AbstractDaemonTest {
"Set Status",
admin.id(),
(a, u) -> {
- if ("A-1".equals(a.getAccount().getStatus())) {
+ if ("A-1".equals(a.account().status())) {
bgCounterA1.getAndIncrement();
u.setStatus("B-1");
}
- if ("A-2".equals(a.getAccount().getStatus())) {
+ if ("A-2".equals(a.account().status())) {
bgCounterA2.getAndIncrement();
u.setStatus("B-2");
}
@@ -2841,16 +2482,19 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(bgCounterA2.get()).isEqualTo(1);
assertThat(updatedAccountState).isPresent();
- assertThat(updatedAccountState.get().getAccount().getStatus()).isEqualTo("B-2");
- assertThat(accounts.get(admin.id()).get().getAccount().getStatus()).isEqualTo("B-2");
+ assertThat(updatedAccountState.get().account().status()).isEqualTo("B-2");
+ assertThat(accounts.get(admin.id()).get().account().status()).isEqualTo("B-2");
assertThat(gApi.accounts().id(admin.id().get()).get().status).isEqualTo("B-2");
}
@Test
public void atomicReadMofifyWriteExternalIds() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId extIdA1 = ExternalId.create("foo", "A-1", accountId);
accountsUpdateProvider
.get()
@@ -2869,7 +2513,11 @@ public class AccountIT extends AbstractDaemonTest {
externalIds,
metaDataUpdateInternalFactory,
new RetryHelper(
- cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
+ cfg,
+ retryMetrics,
+ null,
+ new PluginSetContext<>(DynamicSet.emptySet(), PluginMetrics.DISABLED_INSTANCE),
+ r -> r.withBlockStrategy(noSleepBlockStrategy)),
extIdNotesFactory,
ident,
ident,
@@ -2901,12 +2549,12 @@ public class AccountIT extends AbstractDaemonTest {
"Update External ID",
accountId,
(a, u) -> {
- if (a.getExternalIds().contains(extIdA1)) {
+ if (a.externalIds().contains(extIdA1)) {
bgCounterA1.getAndIncrement();
u.replaceExternalId(extIdA1, extIdB1);
}
- if (a.getExternalIds().contains(extIdA2)) {
+ if (a.externalIds().contains(extIdA2)) {
bgCounterA2.getAndIncrement();
u.replaceExternalId(extIdA2, extIdB2);
}
@@ -2916,8 +2564,8 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(bgCounterA2.get()).isEqualTo(1);
assertThat(updatedAccount).isPresent();
- assertThat(updatedAccount.get().getExternalIds()).containsExactly(extIdB2);
- assertThat(accounts.get(accountId).get().getExternalIds()).containsExactly(extIdB2);
+ assertThat(updatedAccount.get().externalIds()).containsExactly(extIdB2);
+ assertThat(accounts.get(accountId).get().externalIds()).containsExactly(extIdB2);
assertThat(
gApi.accounts().id(accountId.get()).getExternalIds().stream()
.map(i -> i.identity)
@@ -2929,7 +2577,7 @@ public class AccountIT extends AbstractDaemonTest {
public void stalenessChecker() throws Exception {
// Newly created account is not stale.
AccountInfo accountInfo = gApi.accounts().create(name("foo")).get();
- Account.Id accountId = new Account.Id(accountInfo._accountId);
+ Account.Id accountId = Account.id(accountInfo._accountId);
assertThat(stalenessChecker.isStale(accountId)).isFalse();
// Manually updating the user ref makes the index document stale.
@@ -3006,9 +2654,9 @@ public class AccountIT extends AbstractDaemonTest {
}
@Test
+ @UseClockStep
public void deleteAllDraftComments() throws Exception {
try {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
Project.NameKey project2 = projectOperations.newProject().create();
PushOneCommit.Result r1 = createChange();
@@ -3093,12 +2741,14 @@ public class AccountIT extends AbstractDaemonTest {
requestScopeOperations.setApiUser(user.id());
createDraft(r, PushOneCommit.FILE_NAME, "draft");
requestScopeOperations.setApiUser(admin.id());
- try {
- gApi.accounts().id(user.id().get()).deleteDraftComments(new DeleteDraftCommentsInput());
- assert_().fail("expected AuthException");
- } catch (AuthException e) {
- assertThat(e).hasMessageThat().isEqualTo("Cannot delete drafts of other user");
- }
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () ->
+ gApi.accounts()
+ .id(user.id().get())
+ .deleteDraftComments(new DeleteDraftCommentsInput()));
+ assertThat(thrown).hasMessageThat().isEqualTo("Cannot delete drafts of other user");
} finally {
cleanUpDrafts();
}
@@ -3107,7 +2757,7 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void deleteDraftCommentsSkipsInvisibleChanges() throws Exception {
try {
- createBranch(new Branch.NameKey(project, "secret"));
+ createBranch(BranchNameKey.create(project, "secret"));
PushOneCommit.Result r1 = createChange();
PushOneCommit.Result r2 = createChange("refs/for/secret");
@@ -3117,14 +2767,22 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).hasSize(1);
assertThat(gApi.changes().id(r2.getChangeId()).current().draftsAsList()).hasSize(1);
- block(project, "refs/heads/secret", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/secret").group(REGISTERED_USERS))
+ .update();
List<DeletedDraftCommentInfo> result =
gApi.accounts().self().deleteDraftComments(new DeleteDraftCommentsInput());
assertThat(result).hasSize(1);
assertThat(result.get(0).change.changeId).isEqualTo(r1.getChangeId());
assertThat(result.get(0).deleted.stream().map(c -> c.message)).containsExactly("draft a");
- removePermission(project, "refs/heads/secret", Permission.READ);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(permissionKey(Permission.READ).ref("refs/heads/secret"))
+ .update();
assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).isEmpty();
// Draft still exists since change wasn't visible when drafts where deleted.
assertThat(gApi.changes().id(r2.getChangeId()).current().draftsAsList()).hasSize(1);
@@ -3155,22 +2813,23 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void userCannotGenerateNewHttpPasswordForOtherUser() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.accounts().id(admin.username()).generateHttpPassword();
+ assertThrows(
+ AuthException.class, () -> gApi.accounts().id(admin.username()).generateHttpPassword());
}
@Test
public void userCannotExplicitlySetHttpPassword() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.accounts().self().setHttpPassword("my-new-password");
+ assertThrows(
+ AuthException.class, () -> gApi.accounts().self().setHttpPassword("my-new-password"));
}
@Test
public void userCannotExplicitlySetHttpPasswordForOtherUser() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.accounts().id(admin.username()).setHttpPassword("my-new-password");
+ assertThrows(
+ AuthException.class,
+ () -> gApi.accounts().id(admin.username()).setHttpPassword("my-new-password"));
}
@Test
@@ -3185,8 +2844,8 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void userCannotRemoveHttpPasswordForOtherUser() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.accounts().id(admin.username()).setHttpPassword(null);
+ assertThrows(
+ AuthException.class, () -> gApi.accounts().id(admin.username()).setHttpPassword(null));
}
@Test
@@ -3214,9 +2873,11 @@ public class AccountIT extends AbstractDaemonTest {
requestScopeOperations.setApiUser(admin.id());
int userId = accountCreator.create().id().get();
assertThat(gApi.accounts().id(userId).get().username).isNull();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("username");
- gApi.accounts().id(userId).generateHttpPassword();
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.accounts().id(userId).generateHttpPassword());
+ assertThat(thrown).hasMessageThat().contains("username");
}
private void createDraft(PushOneCommit.Result r, String path, String message) throws Exception {
@@ -3243,12 +2904,9 @@ public class AccountIT extends AbstractDaemonTest {
private static Correspondence<GroupInfo, String> getGroupToNameCorrespondence() {
return Correspondence.from(
- new BinaryPredicate<GroupInfo, String>() {
- @Override
- public boolean apply(GroupInfo actualGroup, String expectedName) {
- String groupName = actualGroup == null ? null : actualGroup.name;
- return Objects.equals(groupName, expectedName);
- }
+ (actualGroup, expectedName) -> {
+ String groupName = actualGroup == null ? null : actualGroup.name;
+ return Objects.equals(groupName, expectedName);
},
"has name");
}
@@ -3297,8 +2955,8 @@ public class AccountIT extends AbstractDaemonTest {
// Check via API.
FluentIterable<TestKey> expected = FluentIterable.from(expectedKeys);
Map<String, GpgKeyInfo> keyMap = gApi.accounts().self().listGpgKeys();
- assertThat(keyMap.keySet())
- .named("keys returned by listGpgKeys()")
+ assertWithMessage("keys returned by listGpgKeys()")
+ .that(keyMap.keySet())
.containsExactlyElementsIn(expected.transform(TestKey::getKeyIdString));
for (TestKey key : expected) {
@@ -3320,7 +2978,9 @@ public class AccountIT extends AbstractDaemonTest {
externalIds.byAccount(currAccountId, SCHEME_GPGKEY).stream()
.map(e -> e.key().id())
.collect(toSet());
- assertThat(actualFps).named("external IDs in database").containsExactlyElementsIn(expectedFps);
+ assertWithMessage("external IDs in database")
+ .that(actualFps)
+ .containsExactlyElementsIn(expectedFps);
// Check raw stored keys.
for (TestKey key : expected) {
@@ -3330,31 +2990,35 @@ public class AccountIT extends AbstractDaemonTest {
private static void assertKeyEquals(TestKey expected, GpgKeyInfo actual) {
String id = expected.getKeyIdString();
- assertThat(actual.id).named(id).isEqualTo(id);
- assertThat(actual.fingerprint)
- .named(id)
+ assertWithMessage(id).that(actual.id).isEqualTo(id);
+ assertWithMessage(id)
+ .that(actual.fingerprint)
.isEqualTo(Fingerprint.toString(expected.getPublicKey().getFingerprint()));
List<String> userIds = ImmutableList.copyOf(expected.getPublicKey().getUserIDs());
- assertThat(actual.userIds).named(id).containsExactlyElementsIn(userIds);
+ assertWithMessage(id).that(actual.userIds).containsExactlyElementsIn(userIds);
String key = actual.key;
- assertThat(key).named(id).startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n");
- assertThat(key).named(id).endsWith("-----END PGP PUBLIC KEY BLOCK-----\n");
+ assertWithMessage(id).that(key).startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n");
+ assertWithMessage(id).that(key).endsWith("-----END PGP PUBLIC KEY BLOCK-----\n");
assertThat(actual.status).isEqualTo(GpgKeyInfo.Status.TRUSTED);
assertThat(actual.problems).isEmpty();
}
private void addExternalIdEmail(TestAccount account, String email) throws Exception {
- requireNonNull(email);
- accountsUpdateProvider
- .get()
- .update(
- "Add Email",
- account.id(),
- u ->
- u.addExternalId(
- ExternalId.createWithEmail(name("test"), email, account.id(), email)));
- accountIndexedCounter.assertReindexOf(account);
- requestScopeOperations.setApiUser(account.id());
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ requireNonNull(email);
+ accountsUpdateProvider
+ .get()
+ .update(
+ "Add Email",
+ account.id(),
+ u ->
+ u.addExternalId(
+ ExternalId.createWithEmail(name("test"), email, account.id(), email)));
+ accountIndexedCounter.assertReindexOf(account);
+ requestScopeOperations.setApiUser(account.id());
+ }
}
private Map<String, GpgKeyInfo> addGpgKey(String armored) throws Exception {
@@ -3362,12 +3026,16 @@ public class AccountIT extends AbstractDaemonTest {
}
private Map<String, GpgKeyInfo> addGpgKey(TestAccount account, String armored) throws Exception {
- Map<String, GpgKeyInfo> gpgKeys =
- gApi.accounts()
- .id(account.username())
- .putGpgKeys(ImmutableList.of(armored), ImmutableList.<String>of());
- accountIndexedCounter.assertReindexOf(gApi.accounts().id(account.username()).get());
- return gpgKeys;
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ Map<String, GpgKeyInfo> gpgKeys =
+ gApi.accounts()
+ .id(account.username())
+ .putGpgKeys(ImmutableList.of(armored), ImmutableList.<String>of());
+ accountIndexedCounter.assertReindexOf(gApi.accounts().id(account.username()).get());
+ return gpgKeys;
+ }
}
private Map<String, GpgKeyInfo> addGpgKeyNoReindex(String armored) throws Exception {
@@ -3395,26 +3063,6 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.id());
}
- private Config getAccountConfig(TestRepository<?> allUsersRepo) throws Exception {
- Config ac = new Config();
- try (TreeWalk tw =
- TreeWalk.forPath(
- allUsersRepo.getRepository(),
- AccountProperties.ACCOUNT_CONFIG,
- getHead(allUsersRepo.getRepository(), "HEAD").getTree())) {
- assertThat(tw).isNotNull();
- ac.fromText(
- new String(
- allUsersRepo
- .getRevWalk()
- .getObjectReader()
- .open(tw.getObjectId(0), OBJ_BLOB)
- .getBytes(),
- UTF_8));
- }
- return ac;
- }
-
private AccountApi accountIdApi() throws RestApiException {
return gApi.accounts().id(user.id().get());
}
@@ -3439,47 +3087,6 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(loginResponse.getStatusLine().getStatusCode()).isEqualTo(expectedHttpStatus);
}
- /** Checks if an account is indexed the correct number of times. */
- private static class AccountIndexedCounter implements AccountIndexedListener {
- private final AtomicLongMap<Integer> countsByAccount = AtomicLongMap.create();
-
- @Override
- public void onAccountIndexed(int id) {
- countsByAccount.incrementAndGet(id);
- }
-
- void clear() {
- countsByAccount.clear();
- }
-
- long getCount(Account.Id accountId) {
- return countsByAccount.get(accountId.get());
- }
-
- void assertReindexOf(TestAccount testAccount) {
- assertReindexOf(testAccount, 1);
- }
-
- void assertReindexOf(AccountInfo accountInfo) {
- assertReindexOf(new Account.Id(accountInfo._accountId), 1);
- }
-
- void assertReindexOf(TestAccount testAccount, int expectedCount) {
- assertThat(getCount(testAccount.id())).isEqualTo(expectedCount);
- assertThat(countsByAccount).hasSize(1);
- clear();
- }
-
- void assertReindexOf(Account.Id accountId, int expectedCount) {
- assertThat(getCount(accountId)).isEqualTo(expectedCount);
- countsByAccount.remove(accountId.get());
- }
-
- void assertNoReindex() {
- assertThat(countsByAccount).isEmpty();
- }
- }
-
private static class RefUpdateCounter implements GitReferenceUpdatedListener {
private final AtomicLongMap<String> countsByProjectRefs = AtomicLongMap.create();
@@ -3500,23 +3107,17 @@ public class AccountIT extends AbstractDaemonTest {
countsByProjectRefs.clear();
}
- long getCount(String projectRef) {
- return countsByProjectRefs.get(projectRef);
- }
-
void assertRefUpdateFor(String... projectRefs) {
- Map<String, Integer> expectedRefUpdateCounts = new HashMap<>();
+ Map<String, Long> expectedRefUpdateCounts = new HashMap<>();
for (String projectRef : projectRefs) {
- expectedRefUpdateCounts.put(projectRef, 1);
+ expectedRefUpdateCounts.put(projectRef, 1L);
}
assertRefUpdateFor(expectedRefUpdateCounts);
}
- void assertRefUpdateFor(Map<String, Integer> expectedProjectRefUpdateCounts) {
- for (Map.Entry<String, Integer> e : expectedProjectRefUpdateCounts.entrySet()) {
- assertThat(getCount(e.getKey())).isEqualTo(e.getValue());
- }
- assertThat(countsByProjectRefs).hasSize(expectedProjectRefUpdateCounts.size());
+ void assertRefUpdateFor(Map<String, Long> expectedProjectRefUpdateCounts) {
+ assertThat(countsByProjectRefs.asMap())
+ .containsExactlyEntriesIn(expectedProjectRefUpdateCounts);
clear();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
index 60a61d1788..72a826425f 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
@@ -17,10 +17,10 @@ package com.google.gerrit.acceptance.api.accounts;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountConfig;
@@ -66,7 +66,7 @@ public class AccountIndexerIT {
List<AccountState> matchedAccountStates =
accountQueryProvider.get().byPreferredEmail(preferredEmail);
assertThat(matchedAccountStates).hasSize(1);
- assertThat(matchedAccountStates.get(0).getAccount().getId()).isEqualTo(accountId);
+ assertThat(matchedAccountStates.get(0).account().id()).isEqualTo(accountId);
}
@Test
@@ -82,7 +82,7 @@ public class AccountIndexerIT {
List<AccountState> matchedAccountStates =
accountQueryProvider.get().byPreferredEmail(preferredEmail);
assertThat(matchedAccountStates).hasSize(1);
- assertThat(matchedAccountStates.get(0).getAccount().getId()).isEqualTo(accountId);
+ assertThat(matchedAccountStates.get(0).account().id()).isEqualTo(accountId);
}
@Test
@@ -91,10 +91,10 @@ public class AccountIndexerIT {
loadAccountToCache(accountId);
String status = "ooo";
updateAccountWithoutCacheOrIndex(accountId, newAccountUpdate().setStatus(status).build());
- assertThat(accountCache.get(accountId).get().getAccount().getStatus()).isNull();
+ assertThat(accountCache.get(accountId).get().account().status()).isNull();
accountIndexer.index(accountId);
- assertThat(accountCache.get(accountId).get().getAccount().getStatus()).isEqualTo(status);
+ assertThat(accountCache.get(accountId).get().account().status()).isEqualTo(status);
}
@Test
@@ -109,7 +109,7 @@ public class AccountIndexerIT {
List<AccountState> matchedAccountStates =
accountQueryProvider.get().byPreferredEmail(preferredEmail);
assertThat(matchedAccountStates).hasSize(1);
- assertThat(matchedAccountStates.get(0).getAccount().getId()).isEqualTo(accountId);
+ assertThat(matchedAccountStates.get(0).account().id()).isEqualTo(accountId);
}
@Test
@@ -136,7 +136,7 @@ public class AccountIndexerIT {
private Account.Id createAccount(String name) throws RestApiException {
AccountInfo account = gApi.accounts().create(name).get();
- return new Account.Id(account._accountId);
+ return Account.id(account._accountId);
}
private void reloadAccountToCache(Account.Id accountId) {
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountListenersIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountListenersIT.java
new file mode 100644
index 0000000000..80eff9606a
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountListenersIT.java
@@ -0,0 +1,212 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.api.accounts;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.extensions.events.AccountActivationListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.validators.AccountActivationValidationListener;
+import com.google.gerrit.server.validators.ValidationException;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests the wiring of a real plugin's account listeners
+ *
+ * <p>This test really puts focus on the wiring of the account listeners. Tests for the inner
+ * workings of account activation/deactivation can be found in {@link AccountIT}.
+ */
+@TestPlugin(
+ name = "account-listener-it-plugin",
+ sysModule = "com.google.gerrit.acceptance.api.accounts.AccountListenersIT$Module")
+public class AccountListenersIT extends LightweightPluginDaemonTest {
+ @Inject private AccountOperations accountOperations;
+
+ public static class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ DynamicSet.bind(binder(), AccountActivationValidationListener.class).to(Validator.class);
+ DynamicSet.bind(binder(), AccountActivationListener.class).to(Listener.class);
+ }
+ }
+
+ Validator validator;
+ Listener listener;
+
+ @Before
+ public void setUp() {
+ validator = plugin.getSysInjector().getInstance(Validator.class);
+
+ listener = plugin.getSysInjector().getInstance(Listener.class);
+ }
+
+ @Test
+ public void testActivation() throws RestApiException {
+ int id = accountOperations.newAccount().inactive().create().get();
+
+ gApi.accounts().id(id).setActive(true);
+
+ validator.assertActivationValidation(id);
+ listener.assertActivated(id);
+ assertNoMoreEvents();
+ assertThat(gApi.accounts().id(id).getActive()).isTrue();
+ }
+
+ @Test
+ public void testActivationProhibited() throws RestApiException {
+ int id = accountOperations.newAccount().inactive().create().get();
+
+ validator.failActivationValidations();
+
+ assertThrows(
+ ResourceConflictException.class,
+ () -> {
+ gApi.accounts().id(id).setActive(true);
+ });
+
+ validator.assertActivationValidation(id);
+ // No call to activation listener as validation failed
+ assertNoMoreEvents();
+ assertThat(gApi.accounts().id(id).getActive()).isFalse();
+ }
+
+ @Test
+ public void testDeactivation() throws RestApiException {
+ int id = accountOperations.newAccount().active().create().get();
+
+ gApi.accounts().id(id).setActive(false);
+
+ validator.assertDeactivationValidation(id);
+ listener.assertDeactivated(id);
+ assertNoMoreEvents();
+ assertThat(gApi.accounts().id(id).getActive()).isFalse();
+ }
+
+ @Test
+ public void testDeactivationProhibited() throws RestApiException {
+ int id = accountOperations.newAccount().active().create().get();
+
+ validator.failDeactivationValidations();
+
+ assertThrows(
+ ResourceConflictException.class,
+ () -> {
+ gApi.accounts().id(id).setActive(false);
+ });
+
+ validator.assertDeactivationValidation(id);
+ // No call to activation listener as validation failed
+ assertNoMoreEvents();
+ assertThat(gApi.accounts().id(id).getActive()).isTrue();
+ }
+
+ private void assertNoMoreEvents() {
+ validator.assertNoMoreEvents();
+ listener.assertNoMoreEvents();
+ }
+
+ @Singleton
+ public static class Validator implements AccountActivationValidationListener {
+ private Integer lastIdActivationValidation;
+ private Integer lastIdDeactivationValidation;
+ private boolean failActivationValidations;
+ private boolean failDeactivationValidations;
+
+ @Override
+ public void validateActivation(AccountState account) throws ValidationException {
+ assertThat(lastIdActivationValidation).isNull();
+ lastIdActivationValidation = account.account().id().get();
+ if (failActivationValidations) {
+ throw new ValidationException("testing validation failure");
+ }
+ }
+
+ @Override
+ public void validateDeactivation(AccountState account) throws ValidationException {
+ assertThat(lastIdDeactivationValidation).isNull();
+ lastIdDeactivationValidation = account.account().id().get();
+ if (failDeactivationValidations) {
+ throw new ValidationException("testing validation failure");
+ }
+ }
+
+ public void failActivationValidations() {
+ failActivationValidations = true;
+ }
+
+ public void failDeactivationValidations() {
+ failDeactivationValidations = true;
+ }
+
+ private void assertNoMoreEvents() {
+ assertThat(lastIdActivationValidation).isNull();
+ assertThat(lastIdDeactivationValidation).isNull();
+ }
+
+ private void assertActivationValidation(int id) {
+ assertThat(lastIdActivationValidation).isEqualTo(id);
+ lastIdActivationValidation = null;
+ }
+
+ private void assertDeactivationValidation(int id) {
+ assertThat(lastIdDeactivationValidation).isEqualTo(id);
+ lastIdDeactivationValidation = null;
+ }
+ }
+
+ @Singleton
+ public static class Listener implements AccountActivationListener {
+ private Integer lastIdActivated;
+ private Integer lastIdDeactivated;
+
+ @Override
+ public void onAccountActivated(int id) {
+ assertThat(lastIdActivated).isNull();
+ lastIdActivated = id;
+ }
+
+ @Override
+ public void onAccountDeactivated(int id) {
+ assertThat(lastIdDeactivated).isNull();
+ lastIdDeactivated = id;
+ }
+
+ private void assertNoMoreEvents() {
+ assertThat(lastIdActivated).isNull();
+ assertThat(lastIdDeactivated).isNull();
+ }
+
+ private void assertDeactivated(int id) {
+ assertThat(lastIdDeactivated).isEqualTo(id);
+ lastIdDeactivated = null;
+ }
+
+ private void assertActivated(int id) {
+ assertThat(lastIdActivated).isEqualTo(id);
+ lastIdActivated = null;
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
index 7fd6af945f..5bc0473022 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
@@ -14,16 +14,19 @@
package com.google.gerrit.acceptance.api.accounts;
+import static com.google.common.truth.OptionalSubject.optionals;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountException;
@@ -137,7 +140,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
@Test
public void authenticateWithEmail() throws Exception {
String email = "foo@example.com";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key mailtoExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_MAILTO, email);
accountsUpdate.insert(
"Create Test Account",
@@ -152,7 +155,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
@Test
public void authenticateWithUsername() throws Exception {
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -167,7 +170,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
@Test
public void authenticateWithExternalUser() throws Exception {
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
@@ -183,7 +186,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
public void authenticateWithUsernameAndUpdateEmail() throws Exception {
String username = "foo";
String email = "foo@example.com";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -204,7 +207,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
Optional<AccountState> accountState = accounts.get(accountId);
assertThat(accountState).isPresent();
- assertThat(accountState.get().getAccount().getPreferredEmail()).isEqualTo(newEmail);
+ assertThat(accountState.get().account().preferredEmail()).isEqualTo(newEmail);
}
@Test
@@ -236,7 +239,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
private void authenticateWithUsernameAndUpdateDisplayName(AccountManager am) throws Exception {
String username = "foo";
String email = "foo@example.com";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -254,7 +257,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
Optional<AccountState> accountState = accounts.get(accountId);
assertThat(accountState).isPresent();
- assertThat(accountState.get().getAccount().getFullName()).isEqualTo(newName);
+ assertThat(accountState.get().account().fullName()).isEqualTo(newName);
}
@Test
@@ -264,7 +267,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
assertNoSuchExternalIds(gerritExtIdKey);
// Create orphaned SCHEME_GERRIT external ID.
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId gerritExtId = ExternalId.create(gerritExtIdKey, accountId);
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
@@ -274,15 +277,15 @@ public class AccountManagerIT extends AbstractDaemonTest {
}
AuthRequest who = AuthRequest.forUser(username);
- exception.expect(AccountException.class);
- exception.expectMessage("Authentication error, account not found");
- accountManager.authenticate(who);
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.authenticate(who));
+ assertThat(thrown).hasMessageThat().contains("Authentication error, account not found");
}
@Test
public void cannotAuthenticateWithInactiveAccount() throws Exception {
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -290,16 +293,16 @@ public class AccountManagerIT extends AbstractDaemonTest {
u -> u.setActive(false).addExternalId(ExternalId.create(gerritExtIdKey, accountId)));
AuthRequest who = AuthRequest.forUser(username);
- exception.expect(AccountException.class);
- exception.expectMessage("Authentication error, account inactive");
- accountManager.authenticate(who);
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.authenticate(who));
+ assertThat(thrown).hasMessageThat().contains("Authentication error, account inactive");
}
@Test
public void cannotActivateAccountOnAuthenticationWhenAutoUpdateAccountActiveStatusIsDisabled()
throws Exception {
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -309,9 +312,9 @@ public class AccountManagerIT extends AbstractDaemonTest {
AuthRequest who = AuthRequest.forUser(username);
who.setActive(true);
who.setAuthProvidesAccountActiveStatus(true);
- exception.expect(AccountException.class);
- exception.expectMessage("Authentication error, account inactive");
- accountManager.authenticate(who);
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.authenticate(who));
+ assertThat(thrown).hasMessageThat().contains("Authentication error, account inactive");
}
@Test
@@ -319,7 +322,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
public void activateAccountOnAuthenticationWhenAutoUpdateAccountActiveStatusIsEnabled()
throws Exception {
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -333,14 +336,14 @@ public class AccountManagerIT extends AbstractDaemonTest {
assertAuthResultForExistingAccount(authResult, accountId, gerritExtIdKey);
Optional<AccountState> accountState = accounts.get(accountId);
assertThat(accountState).isPresent();
- assertThat(accountState.get().getAccount().isActive()).isTrue();
+ assertThat(accountState.get().account().isActive()).isTrue();
}
@Test
public void cannotDeactivateAccountOnAuthenticationWhenAutoUpdateAccountActiveStatusIsDisabled()
throws Exception {
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -354,7 +357,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
assertAuthResultForExistingAccount(authResult, accountId, gerritExtIdKey);
Optional<AccountState> accountState = accounts.get(accountId);
assertThat(accountState).isPresent();
- assertThat(accountState.get().getAccount().isActive()).isTrue();
+ assertThat(accountState.get().account().isActive()).isTrue();
}
@Test
@@ -362,7 +365,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
public void deactivateAccountOnAuthenticationWhenAutoUpdateAccountActiveStatusIsEnabled()
throws Exception {
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -372,16 +375,13 @@ public class AccountManagerIT extends AbstractDaemonTest {
AuthRequest who = AuthRequest.forUser(username);
who.setActive(false);
who.setAuthProvidesAccountActiveStatus(true);
- try {
- accountManager.authenticate(who);
- fail("Expected AccountException");
- } catch (AccountException e) {
- assertThat(e).hasMessageThat().isEqualTo("Authentication error, account inactive");
- }
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.authenticate(who));
+ assertThat(thrown).hasMessageThat().isEqualTo("Authentication error, account inactive");
Optional<AccountState> accountState = accounts.get(accountId);
assertThat(accountState).isPresent();
- assertThat(accountState.get().getAccount().isActive()).isFalse();
+ assertThat(accountState.get().account().isActive()).isFalse();
}
@Test
@@ -390,7 +390,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Create an account with an SCHEME_EXTERNAL external ID that occupies the email.
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
@@ -400,9 +400,11 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Try to authenticate with this email to create a new account with a SCHEME_MAILTO external ID.
// Expect that this fails because the email is already assigned to the other account.
AuthRequest who = AuthRequest.forEmail(email);
- exception.expect(AccountException.class);
- exception.expectMessage("Email 'foo@example.com' in use by another account");
- accountManager.authenticate(who);
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.authenticate(who));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Email 'foo@example.com' in use by another account");
}
@Test
@@ -411,7 +413,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Create an account with an SCHEME_EXTERNAL external ID that occupies the email.
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
@@ -422,9 +424,11 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Expect that this fails because the email is already assigned to the other account.
AuthRequest who = AuthRequest.forUser("bar");
who.setEmailAddress(email);
- exception.expect(AccountException.class);
- exception.expectMessage("Email 'foo@example.com' in use by another account");
- accountManager.authenticate(who);
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.authenticate(who));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Email 'foo@example.com' in use by another account");
}
@Test
@@ -434,7 +438,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Create an account with a SCHEME_GERRIT external ID and an email.
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -444,7 +448,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
.addExternalId(ExternalId.createWithEmail(gerritExtIdKey, accountId, email)));
// Create another account with an SCHEME_EXTERNAL external ID that occupies the new email.
- Account.Id accountId2 = new Account.Id(seq.nextAccountId());
+ Account.Id accountId2 = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, "bar");
accountsUpdate.insert(
"Create Test Account",
@@ -455,12 +459,11 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Expect that this fails because the new email is already assigned to the other account.
AuthRequest who = AuthRequest.forUser(username);
who.setEmailAddress(newEmail);
- try {
- accountManager.authenticate(who);
- fail("Expected AccountException");
- } catch (AccountException e) {
- assertThat(e).hasMessageThat().isEqualTo("Email 'bar@example.com' in use by another account");
- }
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.authenticate(who));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo("Email 'bar@example.com' in use by another account");
// Verify that the email in the external ID was not updated.
Optional<ExternalId> gerritExtId = externalIds.get(gerritExtIdKey);
@@ -470,7 +473,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Verify that the preferred email was not updated.
Optional<AccountState> accountState = accounts.get(accountId);
assertThat(accountState).isPresent();
- assertThat(accountState.get().getAccount().getPreferredEmail()).isEqualTo(email);
+ assertThat(accountState.get().account().preferredEmail()).isEqualTo(email);
}
@Test
@@ -480,7 +483,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Create an account with a SCHEME_GERRIT external ID
String username = "foo";
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
accountsUpdate.insert(
"Create Test Account",
accountId,
@@ -502,20 +505,20 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Verify that the account external ids with scheme 'mailto:' contains the email
AccountState account = accounts.get(authResult.getAccountId()).get();
- ImmutableSet<ExternalId> accountExternalIds = account.getExternalIds(ExternalId.SCHEME_MAILTO);
+ ImmutableSet<ExternalId> accountExternalIds = account.externalIds();
assertThat(accountExternalIds).isNotEmpty();
Set<String> emails = ExternalId.getEmails(accountExternalIds).collect(toSet());
assertThat(emails).contains(email);
// Verify the preferred email
- assertThat(account.getAccount().getPreferredEmail()).isEqualTo(email);
+ assertThat(account.account().preferredEmail()).isEqualTo(email);
}
@Test
public void linkNewExternalId() throws Exception {
// Create an account with a SCHEME_GERRIT external ID and no email
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username);
accountsUpdate.insert(
"Create Test Account",
@@ -539,7 +542,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
public void updateExternalIdOnLink() throws Exception {
// Create an account with a SCHEME_GERRIT external ID and no email
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
@@ -562,7 +565,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
public void cannotLinkExternalIdThatIsAlreadyUsed() throws Exception {
// Create an account with a SCHEME_EXTERNAL external ID
String username1 = "foo";
- Account.Id accountId1 = new Account.Id(seq.nextAccountId());
+ Account.Id accountId1 = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey1 = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username1);
accountsUpdate.insert(
"Create Test Account",
@@ -571,7 +574,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Create another account with a SCHEME_EXTERNAL external ID
String username2 = "bar";
- Account.Id accountId2 = new Account.Id(seq.nextAccountId());
+ Account.Id accountId2 = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey2 = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username2);
accountsUpdate.insert(
"Create Test Account",
@@ -581,9 +584,11 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Try to link external ID of the first account to the second account.
// Expect that this fails because the external ID is already assigned to the first account.
AuthRequest who = AuthRequest.forExternalUser(username1);
- exception.expect(AccountException.class);
- exception.expectMessage("Identity 'external:foo' in use by another account");
- accountManager.link(accountId2, who);
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.link(accountId2, who));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Identity 'external:foo' in use by another account");
}
@Test
@@ -592,7 +597,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Create an account with an SCHEME_EXTERNAL external ID that occupies the email.
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
@@ -601,7 +606,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Create another account with a SCHEME_GERRIT external ID and no email
String username2 = "foo";
- Account.Id accountId2 = new Account.Id(seq.nextAccountId());
+ Account.Id accountId2 = Account.id(seq.nextAccountId());
ExternalId.Key gerritExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_GERRIT, username2);
accountsUpdate.insert(
"Create Test Account",
@@ -611,9 +616,11 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Try to link the email to the second account (via a new MAILTO external ID) and expect that
// this fails because the email is already assigned to the first account.
AuthRequest who = AuthRequest.forEmail(email);
- exception.expect(AccountException.class);
- exception.expectMessage("Email 'foo@example.com' in use by another account");
- accountManager.link(accountId2, who);
+ AccountException thrown =
+ assertThrows(AccountException.class, () -> accountManager.link(accountId2, who));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Email 'foo@example.com' in use by another account");
}
@Test
@@ -622,7 +629,7 @@ public class AccountManagerIT extends AbstractDaemonTest {
// Create an account with an SCHEME_EXTERNAL external ID that occupies the email.
String username = "foo";
- Account.Id accountId = new Account.Id(seq.nextAccountId());
+ Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId.Key externalExtIdKey = ExternalId.Key.create(ExternalId.SCHEME_EXTERNAL, username);
accountsUpdate.insert(
"Create Test Account",
@@ -637,7 +644,10 @@ public class AccountManagerIT extends AbstractDaemonTest {
private void assertNoSuchExternalIds(ExternalId.Key... extIdKeys) throws Exception {
for (ExternalId.Key extIdKey : extIdKeys) {
- assertThat(externalIds.get(extIdKey)).named(extIdKey.get()).isEmpty();
+ assertWithMessage(extIdKey.get())
+ .about(optionals())
+ .that(externalIds.get(extIdKey))
+ .isEmpty();
}
}
@@ -658,13 +668,15 @@ public class AccountManagerIT extends AbstractDaemonTest {
@Nullable String expectedEmail)
throws Exception {
Optional<ExternalId> extId = externalIds.get(extIdKey);
- assertThat(extId).named(extIdKey.get()).isPresent();
+ assertWithMessage(extIdKey.get()).about(optionals()).that(extId).isPresent();
if (expectedAccountId != null) {
- assertThat(extId.get().accountId())
- .named("account ID of " + extIdKey.get())
+ assertWithMessage("account ID of " + extIdKey.get())
+ .that(extId.get().accountId())
.isEqualTo(expectedAccountId);
}
- assertThat(extId.get().email()).named("email of " + extIdKey.get()).isEqualTo(expectedEmail);
+ assertWithMessage("email of " + extIdKey.get())
+ .that(extId.get().email())
+ .isEqualTo(expectedEmail);
}
private void assertAuthResultForNewAccount(
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
index a4a5745e0a..5550d98cc4 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
@@ -16,19 +16,22 @@ package com.google.gerrit.acceptance.api.accounts;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.UseClockStep;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.RawInputUtil;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.SubmitInput;
@@ -44,21 +47,17 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.lib.Config;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
+@UseClockStep
public class AgreementsIT extends AbstractDaemonTest {
private ContributorAgreement caAutoVerify;
private ContributorAgreement caNoAutoVerify;
@@ -81,7 +80,7 @@ public class AgreementsIT extends AbstractDaemonTest {
AccountGroup.UUID g = groupOperations.newGroup().name(name).create();
GroupApi groupApi = gApi.groups().id(g.get());
groupApi.description("CLA test group");
- InternalGroup caGroup = group(new AccountGroup.UUID(groupApi.detail().id));
+ InternalGroup caGroup = group(AccountGroup.uuid(groupApi.detail().id));
GroupReference groupRef = new GroupReference(caGroup.getGroupUUID(), caGroup.getName());
PermissionRule rule = new PermissionRule(groupRef);
rule.setAction(PermissionRule.Action.ALLOW);
@@ -111,16 +110,6 @@ public class AgreementsIT extends AbstractDaemonTest {
return cfg;
}
- @BeforeClass
- public static void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @AfterClass
- public static void restoreTime() {
- TestTimeUtil.useSystemTime();
- }
-
@Before
public void setUp() throws Exception {
caAutoVerify = configureContributorAgreement(true);
@@ -145,17 +134,21 @@ public class AgreementsIT extends AbstractDaemonTest {
@Test
public void signNonExistingAgreement() throws Exception {
assume().that(isContributorAgreementsEnabled()).isTrue();
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("contributor agreement not found");
- gApi.accounts().self().signAgreement("does-not-exist");
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.accounts().self().signAgreement("does-not-exist"));
+ assertThat(thrown).hasMessageThat().contains("contributor agreement not found");
}
@Test
public void signAgreementNoAutoVerify() throws Exception {
assume().that(isContributorAgreementsEnabled()).isTrue();
- exception.expect(BadRequestException.class);
- exception.expectMessage("cannot enter a non-autoVerify agreement");
- gApi.accounts().self().signAgreement(caNoAutoVerify.getName());
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.accounts().self().signAgreement(caNoAutoVerify.getName()));
+ assertThat(thrown).hasMessageThat().contains("cannot enter a non-autoVerify agreement");
}
@Test
@@ -188,33 +181,40 @@ public class AgreementsIT extends AbstractDaemonTest {
public void signAgreementAsOtherUser() throws Exception {
assume().that(isContributorAgreementsEnabled()).isTrue();
assertThat(gApi.accounts().self().get().name).isNotEqualTo("admin");
- exception.expect(AuthException.class);
- exception.expectMessage("not allowed to enter contributor agreement");
- gApi.accounts().id("admin").signAgreement(caAutoVerify.getName());
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.accounts().id("admin").signAgreement(caAutoVerify.getName()));
+ assertThat(thrown).hasMessageThat().contains("not allowed to enter contributor agreement");
}
@Test
public void signAgreementAnonymous() throws Exception {
requestScopeOperations.setApiUserAnonymous();
- exception.expect(AuthException.class);
- exception.expectMessage("Authentication required");
- gApi.accounts().self().signAgreement(caAutoVerify.getName());
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.accounts().self().signAgreement(caAutoVerify.getName()));
+ assertThat(thrown).hasMessageThat().contains("Authentication required");
}
@Test
public void agreementsDisabledSign() throws Exception {
assume().that(isContributorAgreementsEnabled()).isFalse();
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("contributor agreements disabled");
- gApi.accounts().self().signAgreement(caAutoVerify.getName());
+ MethodNotAllowedException thrown =
+ assertThrows(
+ MethodNotAllowedException.class,
+ () -> gApi.accounts().self().signAgreement(caAutoVerify.getName()));
+ assertThat(thrown).hasMessageThat().contains("contributor agreements disabled");
}
@Test
public void agreementsDisabledList() throws Exception {
assume().that(isContributorAgreementsEnabled()).isFalse();
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("contributor agreements disabled");
- gApi.accounts().self().listAgreements();
+ MethodNotAllowedException thrown =
+ assertThrows(
+ MethodNotAllowedException.class, () -> gApi.accounts().self().listAgreements());
+ assertThat(thrown).hasMessageThat().contains("contributor agreements disabled");
}
@Test
@@ -233,9 +233,9 @@ public class AgreementsIT extends AbstractDaemonTest {
// Revert is not allowed when CLA is required but not signed
requestScopeOperations.setApiUser(user.id());
setUseContributorAgreements(InheritableBoolean.TRUE);
- exception.expect(AuthException.class);
- exception.expectMessage("Contributor Agreement");
- gApi.changes().id(change.changeId).revert();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(change.changeId).revert());
+ assertThat(thrown).hasMessageThat().contains("Contributor Agreement");
}
@Test
@@ -287,9 +287,10 @@ public class AgreementsIT extends AbstractDaemonTest {
CherryPickInput in = new CherryPickInput();
in.destination = dest.ref;
in.message = change.subject;
- exception.expect(AuthException.class);
- exception.expectMessage("Contributor Agreement");
- gApi.changes().id(change.changeId).current().cherryPick(in);
+ AuthException thrown =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(change.changeId).current().cherryPick(in));
+ assertThat(thrown).hasMessageThat().contains("Contributor Agreement");
}
@Test
@@ -302,12 +303,9 @@ public class AgreementsIT extends AbstractDaemonTest {
// Create a change is not allowed when CLA is required but not signed
setUseContributorAgreements(InheritableBoolean.TRUE);
- try {
- gApi.changes().create(newChangeInput());
- fail("Expected AuthException");
- } catch (AuthException e) {
- assertThat(e.getMessage()).contains("Contributor Agreement");
- }
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().create(newChangeInput()));
+ assertThat(thrown).hasMessageThat().contains("Contributor Agreement");
// Sign the agreement
gApi.accounts().self().signAgreement(caAutoVerify.getName());
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index c905d3fee0..6717fb7ec1 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -16,8 +16,11 @@ package com.google.gerrit.acceptance.api.accounts;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.AssertUtil.assertPrefs;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
@@ -26,23 +29,18 @@ import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DefaultBase;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.ReviewCategoryStrategy;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.TimeFormat;
import com.google.gerrit.extensions.client.MenuItem;
import com.google.gerrit.extensions.config.DownloadScheme;
-import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.inject.Inject;
-import com.google.inject.util.Providers;
import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
@NoHttpd
public class GeneralPreferencesIT extends AbstractDaemonTest {
- @Inject private DynamicMap<DownloadScheme> downloadSchemes;
+ @Inject private ExtensionRegistry extensionRegistry;
private TestAccount user42;
@@ -63,15 +61,13 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
new MenuItem("Edits", "#/q/has:edit", null),
new MenuItem("Watched Changes", "#/q/is:watched+is:open", null),
new MenuItem("Starred Changes", "#/q/is:starred", null),
- new MenuItem("Groups", "#/groups/self", null));
+ new MenuItem("Groups", "#/settings/#Groups", null));
assertThat(o.changeTable).isEmpty();
GeneralPreferencesInfo i = GeneralPreferencesInfo.defaults();
// change all default values
i.changesPerPage *= -1;
- i.showSiteHeader ^= true;
- i.useFlashClipboard ^= true;
i.dateFormat = DateFormat.US;
i.timeFormat = TimeFormat.HHMM_24;
i.emailStrategy = EmailStrategy.DISABLED;
@@ -84,7 +80,6 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
i.legacycidInChangeTable ^= true;
i.muteCommonPathPrefixes ^= true;
i.signedOffBy ^= true;
- i.reviewCategoryStrategy = ReviewCategoryStrategy.ABBREV;
i.diffView = DiffView.UNIFIED_DIFF;
i.my = new ArrayList<>();
i.my.add(new MenuItem("name", "url"));
@@ -155,9 +150,11 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
i.my = new ArrayList<>();
i.my.add(new MenuItem(null, "url"));
- exception.expect(BadRequestException.class);
- exception.expectMessage("name for menu item is required");
- gApi.accounts().id(user42.id().toString()).setPreferences(i);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.accounts().id(user42.id().toString()).setPreferences(i));
+ assertThat(thrown).hasMessageThat().contains("name for menu item is required");
}
@Test
@@ -166,9 +163,11 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
i.my = new ArrayList<>();
i.my.add(new MenuItem("name", null));
- exception.expect(BadRequestException.class);
- exception.expectMessage("URL for menu item is required");
- gApi.accounts().id(user42.id().toString()).setPreferences(i);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.accounts().id(user42.id().toString()).setPreferences(i));
+ assertThat(thrown).hasMessageThat().contains("URL for menu item is required");
}
@Test
@@ -186,18 +185,20 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
GeneralPreferencesInfo i = GeneralPreferencesInfo.defaults();
i.downloadScheme = "foo";
- exception.expect(BadRequestException.class);
- exception.expectMessage("Unsupported download scheme: " + i.downloadScheme);
- gApi.accounts().id(user42.id().toString()).setPreferences(i);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.accounts().id(user42.id().toString()).setPreferences(i));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Unsupported download scheme: " + i.downloadScheme);
}
@Test
public void setDownloadScheme() throws Exception {
String schemeName = "foo";
- RegistrationHandle registrationHandle =
- ((PrivateInternals_DynamicMapImpl<DownloadScheme>) downloadSchemes)
- .put("myPlugin", schemeName, Providers.of(new TestDownloadScheme()));
- try {
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(new TestDownloadScheme(), schemeName)) {
GeneralPreferencesInfo i = GeneralPreferencesInfo.defaults();
i.downloadScheme = schemeName;
@@ -206,8 +207,6 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
o = gApi.accounts().id(user42.id().toString()).getPreferences();
assertThat(o.downloadScheme).isEqualTo(schemeName);
- } finally {
- registrationHandle.remove();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
index 925c66ab7b..8aebc69845 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
@@ -15,10 +15,11 @@
package com.google.gerrit.acceptance.api.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.concurrent.TimeUnit.HOURS;
-import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
@@ -26,13 +27,15 @@ import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.AbandonUtil;
import com.google.gerrit.server.config.ChangeCleanupConfig;
@@ -46,8 +49,9 @@ import org.junit.Test;
public class AbandonIT extends AbstractDaemonTest {
@Inject private AbandonUtil abandonUtil;
- @Inject private RequestScopeOperations requestScopeOperations;
@Inject private ChangeCleanupConfig cleanupConfig;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
@Test
public void abandon() throws Exception {
@@ -59,9 +63,9 @@ public class AbandonIT extends AbstractDaemonTest {
assertThat(info.status).isEqualTo(ChangeStatus.ABANDONED);
assertThat(Iterables.getLast(info.messages).message.toLowerCase()).contains("abandoned");
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("change is abandoned");
- gApi.changes().id(changeId).abandon();
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.changes().id(changeId).abandon());
+ assertThat(thrown).hasMessageThat().contains("change is abandoned");
}
@Test
@@ -89,24 +93,29 @@ public class AbandonIT extends AbstractDaemonTest {
String project2Name = name("Project2");
gApi.projects().create(project1Name);
gApi.projects().create(project2Name);
- TestRepository<InMemoryRepository> project1 = cloneProject(new Project.NameKey(project1Name));
- TestRepository<InMemoryRepository> project2 = cloneProject(new Project.NameKey(project2Name));
+ TestRepository<InMemoryRepository> project1 = cloneProject(Project.nameKey(project1Name));
+ TestRepository<InMemoryRepository> project2 = cloneProject(Project.nameKey(project2Name));
CurrentUser user = atrScope.get().getUser();
PushOneCommit.Result a = createChange(project1, "master", "x", "x", "x", "");
PushOneCommit.Result b = createChange(project2, "master", "x", "x", "x", "");
List<ChangeData> list = ImmutableList.of(a.getChange(), b.getChange());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- String.format("Project name \"%s\" doesn't match \"%s\"", project2Name, project1Name));
- batchAbandon.batchAbandon(batchUpdateFactory, new Project.NameKey(project1Name), user, list);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () ->
+ batchAbandon.batchAbandon(
+ batchUpdateFactory, Project.nameKey(project1Name), user, list));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format("Project name \"%s\" doesn't match \"%s\"", project2Name, project1Name));
}
@Test
+ @UseClockStep
@GerritConfig(name = "changeCleanup.abandonAfter", value = "1w")
public void abandonInactiveOpenChanges() throws Exception {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
-
// create 2 changes which will be abandoned ...
int id1 = createChange().getChange().getId().get();
int id2 = createChange().getChange().getId().get();
@@ -157,9 +166,9 @@ public class AbandonIT extends AbstractDaemonTest {
String changeId = r.getChangeId();
assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("abandon not permitted");
- gApi.changes().id(changeId).abandon();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).abandon());
+ assertThat(thrown).hasMessageThat().contains("abandon not permitted");
}
@Test
@@ -167,7 +176,11 @@ public class AbandonIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
- grant(project, "refs/heads/master", Permission.ABANDON, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.ABANDON).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).abandon();
assertThat(info(changeId).status).isEqualTo(ChangeStatus.ABANDONED);
@@ -188,9 +201,9 @@ public class AbandonIT extends AbstractDaemonTest {
assertThat(info.status).isEqualTo(ChangeStatus.NEW);
assertThat(Iterables.getLast(info.messages).message.toLowerCase()).contains("restored");
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("change is new");
- gApi.changes().id(changeId).restore();
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.changes().id(changeId).restore());
+ assertThat(thrown).hasMessageThat().contains("change is new");
}
@Test
@@ -201,9 +214,9 @@ public class AbandonIT extends AbstractDaemonTest {
gApi.changes().id(changeId).abandon();
requestScopeOperations.setApiUser(user.id());
assertThat(info(changeId).status).isEqualTo(ChangeStatus.ABANDONED);
- exception.expect(AuthException.class);
- exception.expectMessage("restore not permitted");
- gApi.changes().id(changeId).restore();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).restore());
+ assertThat(thrown).hasMessageThat().contains("restore not permitted");
}
private List<Integer> toChangeNumbers(List<ChangeInfo> changes) {
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index fe97688b07..59e0a6857a 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -15,12 +15,21 @@
package com.google.gerrit.acceptance.api.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.acceptance.PushOneCommit.FILE_CONTENT;
import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.blockLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.labelPermissionKey;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
import static com.google.gerrit.extensions.client.ListChangesOption.ALL_REVISIONS;
import static com.google.gerrit.extensions.client.ListChangesOption.CHANGE_ACTIONS;
import static com.google.gerrit.extensions.client.ListChangesOption.CHECK;
@@ -38,20 +47,23 @@ import static com.google.gerrit.extensions.client.ListChangesOption.TRACKING_IDS
import static com.google.gerrit.extensions.client.ReviewerState.CC;
import static com.google.gerrit.extensions.client.ReviewerState.REMOVED;
import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
import static com.google.gerrit.server.StarredChangesUtil.DEFAULT_LABEL;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.CHANGE_OWNER;
import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.project.testing.Util.category;
-import static com.google.gerrit.server.project.testing.Util.value;
+import static com.google.gerrit.server.project.testing.TestLabels.label;
+import static com.google.gerrit.server.project.testing.TestLabels.value;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.truth.CacheStatsSubject.assertThat;
+import static com.google.gerrit.truth.CacheStatsSubject.cloneStats;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheStats;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -60,20 +72,33 @@ import com.google.common.collect.Lists;
import com.google.common.truth.ThrowableSubject;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.ChangeIndexedCounter;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.UseTimezone;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.FooterConstants;
+import com.google.gerrit.common.RawInputUtil;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
@@ -87,7 +112,6 @@ import com.google.gerrit.extensions.api.changes.NotifyInfo;
import com.google.gerrit.extensions.api.changes.RebaseInput;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.api.changes.RelatedChangeAndCommitInfo;
-import com.google.gerrit.extensions.api.changes.RevertInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
import com.google.gerrit.extensions.api.changes.ReviewResult;
@@ -98,7 +122,6 @@ import com.google.gerrit.extensions.api.groups.GroupApi;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.ConfigInput;
-import com.google.gerrit.extensions.api.projects.ProjectApi;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.client.ChangeStatus;
@@ -119,12 +142,8 @@ import com.google.gerrit.extensions.common.GitPerson;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.common.MergeInput;
import com.google.gerrit.extensions.common.MergePatchSetInput;
-import com.google.gerrit.extensions.common.PureRevertInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.common.TrackingIdInfo;
-import com.google.gerrit.extensions.events.ChangeIndexedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -135,22 +154,21 @@ import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.query.PostFilterPredicate;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.git.ChangeMessageModifier;
+import com.google.gerrit.server.change.testing.TestChangeETagComputation;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.patch.DiffSummary;
+import com.google.gerrit.server.patch.DiffSummaryKey;
+import com.google.gerrit.server.patch.IntraLineDiff;
+import com.google.gerrit.server.patch.IntraLineDiffKey;
+import com.google.gerrit.server.patch.PatchList;
+import com.google.gerrit.server.patch.PatchListKey;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder.ChangeOperatorFactory;
import com.google.gerrit.server.restapi.change.PostReview;
@@ -159,9 +177,9 @@ import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.FakeEmailSender.Message;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
+import com.google.inject.name.Named;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -184,49 +202,31 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushResult;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
@NoHttpd
+@UseTimezone(timezone = "US/Eastern")
public class ChangeIT extends AbstractDaemonTest {
- private String systemTimeZone;
@Inject private AccountOperations accountOperations;
@Inject private ChangeIndexCollection changeIndexCollection;
- @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
- @Inject private DynamicSet<ChangeMessageModifier> changeMessageModifiers;
@Inject private GroupOperations groupOperations;
@Inject private IndexConfig indexConfig;
@Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
- private ChangeIndexedCounter changeIndexedCounter;
- private RegistrationHandle changeIndexedCounterHandle;
+ @Inject
+ @Named("diff")
+ private Cache<PatchListKey, PatchList> fileCache;
- @Before
- public void setTimeForTesting() {
- systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
- }
-
- @After
- public void resetTime() {
- TestTimeUtil.useSystemTime();
- System.setProperty("user.timezone", systemTimeZone);
- }
+ @Inject
+ @Named("diff_intraline")
+ private Cache<IntraLineDiffKey, IntraLineDiff> intraCache;
- @Before
- public void addChangeIndexedCounter() {
- changeIndexedCounter = new ChangeIndexedCounter();
- changeIndexedCounterHandle = changeIndexedListeners.add("gerrit", changeIndexedCounter);
- }
-
- @After
- public void removeChangeIndexedCounter() {
- if (changeIndexedCounterHandle != null) {
- changeIndexedCounterHandle.remove();
- }
- }
+ @Inject
+ @Named("diff_summary")
+ private Cache<DiffSummaryKey, DiffSummary> diffSummaryCache;
@Test
public void get() throws Exception {
@@ -252,6 +252,48 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void diffStatShouldComputeInsertionsAndDeletions() throws Exception {
+ String fileName = "a_new_file.txt";
+ String fileContent = "First line\nSecond line\n";
+ PushOneCommit.Result result = createChange("Add a file", fileName, fileContent);
+ String triplet = project.get() + "~master~" + result.getChangeId();
+ ChangeInfo change = gApi.changes().id(triplet).get();
+ assertThat(change.insertions).isNotNull();
+ assertThat(change.deletions).isNotNull();
+ }
+
+ @Test
+ public void diffStatShouldSkipInsertionsAndDeletions() throws Exception {
+ String fileName = "a_new_file.txt";
+ String fileContent = "First line\nSecond line\n";
+ PushOneCommit.Result result = createChange("Add a file", fileName, fileContent);
+ String triplet = project.get() + "~master~" + result.getChangeId();
+ ChangeInfo change =
+ gApi.changes().id(triplet).get(ImmutableList.of(ListChangesOption.SKIP_DIFFSTAT));
+ assertThat(change.insertions).isNull();
+ assertThat(change.deletions).isNull();
+ }
+
+ @Test
+ public void skipDiffstatOptionAvoidsAllDiffComputations() throws Exception {
+ String fileName = "a_new_file.txt";
+ String fileContent = "First line\nSecond line\n";
+ PushOneCommit.Result result = createChange("Add a file", fileName, fileContent);
+ String triplet = project.get() + "~master~" + result.getChangeId();
+ CacheStats startPatch = cloneStats(fileCache.stats());
+ CacheStats startIntra = cloneStats(intraCache.stats());
+ CacheStats startSummary = cloneStats(diffSummaryCache.stats());
+ gApi.changes().id(triplet).get(ImmutableList.of(ListChangesOption.SKIP_DIFFSTAT));
+
+ assertThat(fileCache.stats()).since(startPatch).hasMissCount(0);
+ assertThat(fileCache.stats()).since(startPatch).hasHitCount(0);
+ assertThat(intraCache.stats()).since(startIntra).hasMissCount(0);
+ assertThat(intraCache.stats()).since(startIntra).hasHitCount(0);
+ assertThat(diffSummaryCache.stats()).since(startSummary).hasMissCount(0);
+ assertThat(diffSummaryCache.stats()).since(startSummary).hasHitCount(0);
+ }
+
+ @Test
public void skipMergeable() throws Exception {
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
@@ -277,9 +319,9 @@ public class ChangeIT extends AbstractDaemonTest {
String changeId = rwip.getChangeId();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("toggle work in progress state not permitted");
- gApi.changes().id(changeId).setWorkInProgress();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).setWorkInProgress());
+ assertThat(thrown).hasMessageThat().contains("toggle work in progress state not permitted");
}
@Test
@@ -300,7 +342,11 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
com.google.gerrit.acceptance.TestAccount user2 = accountCreator.user2();
- grant(project, "refs/*", Permission.OWNER, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user2.id());
gApi.changes().id(changeId).setWorkInProgress();
assertThat(gApi.changes().id(changeId).get().workInProgress).isTrue();
@@ -323,9 +369,9 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).setWorkInProgress();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("toggle work in progress state not permitted");
- gApi.changes().id(changeId).setReadyForReview();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).setReadyForReview());
+ assertThat(thrown).hasMessageThat().contains("toggle work in progress state not permitted");
}
@Test
@@ -348,7 +394,11 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).setWorkInProgress();
com.google.gerrit.acceptance.TestAccount user2 = accountCreator.user2();
- grant(project, "refs/*", Permission.OWNER, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user2.id());
gApi.changes().id(changeId).setReadyForReview();
assertThat(gApi.changes().id(changeId).get().workInProgress).isNull();
@@ -367,7 +417,7 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
- public void pendingReviewersInNoteDb() throws Exception {
+ public void pendingReviewers() throws Exception {
ConfigInput conf = new ConfigInput();
conf.enableReviewerByEmail = InheritableBoolean.TRUE;
gApi.projects().name(project.get()).config(conf);
@@ -415,14 +465,13 @@ public class ChangeIT extends AbstractDaemonTest {
.reviewer("byemail2@example.com")
.reviewer("byemail3@example.com", CC, false)
.reviewer("byemail4@example.com", CC, false);
- ReviewResult result = gApi.changes().id(changeId).revision("current").review(in);
+ ReviewResult result = gApi.changes().id(changeId).current().review(in);
assertThat(result.reviewers).isNotEmpty();
ChangeInfo info = gApi.changes().id(changeId).get();
Function<Collection<AccountInfo>, Collection<String>> toEmails =
ais -> ais.stream().map(ai -> ai.email).collect(toSet());
assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
- .containsExactly(
- admin.email(), email1, email2, "byemail1@example.com", "byemail2@example.com");
+ .containsExactly(email1, email2, "byemail1@example.com", "byemail2@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
.containsExactly(email3, email4, "byemail3@example.com", "byemail4@example.com");
assertThat(info.pendingReviewers.get(REMOVED)).isNull();
@@ -434,7 +483,7 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).reviewer("byemail3@example.com").remove();
info = gApi.changes().id(changeId).get();
assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
- .containsExactly(admin.email(), email2, "byemail2@example.com");
+ .containsExactly(email2, "byemail2@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
.containsExactly(email4, "byemail4@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(REMOVED)))
@@ -442,10 +491,10 @@ public class ChangeIT extends AbstractDaemonTest {
// "Undo" a removal.
in = ReviewInput.noScore().reviewer(email1);
- gApi.changes().id(changeId).revision("current").review(in);
+ gApi.changes().id(changeId).current().review(in);
info = gApi.changes().id(changeId).get();
assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
- .containsExactly(admin.email(), email1, email2, "byemail2@example.com");
+ .containsExactly(email1, email2, "byemail2@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
.containsExactly(email4, "byemail4@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(REMOVED)))
@@ -456,7 +505,7 @@ public class ChangeIT extends AbstractDaemonTest {
info = gApi.changes().id(changeId).get();
assertThat(info.pendingReviewers).isEmpty();
assertThat(toEmails.apply(info.reviewers.get(REVIEWER)))
- .containsExactly(admin.email(), email1, email2, "byemail2@example.com");
+ .containsExactly(email1, email2, "byemail2@example.com");
assertThat(toEmails.apply(info.reviewers.get(CC)))
.containsExactly(email4, "byemail4@example.com");
assertThat(info.reviewers.get(REMOVED)).isNull();
@@ -507,12 +556,14 @@ public class ChangeIT extends AbstractDaemonTest {
String refactor = "Needs some refactoring";
String ptal = "PTAL";
- grant(
- project,
- "refs/heads/master",
- Permission.TOGGLE_WORK_IN_PROGRESS_STATE,
- false,
- REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allow(Permission.TOGGLE_WORK_IN_PROGRESS_STATE)
+ .ref("refs/heads/master")
+ .group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).setWorkInProgress(refactor);
@@ -538,7 +589,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(r.getChange().change().isWorkInProgress()).isTrue();
ReviewInput in = ReviewInput.noScore().setWorkInProgress(false);
- ReviewResult result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
+ ReviewResult result = gApi.changes().id(r.getChangeId()).current().review(in);
assertThat(result.ready).isTrue();
ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
@@ -551,7 +602,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(r.getChange().change().isWorkInProgress()).isFalse();
ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
- ReviewResult result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
+ ReviewResult result = gApi.changes().id(r.getChangeId()).current().review(in);
assertThat(result.ready).isNull();
ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
@@ -568,7 +619,7 @@ public class ChangeIT extends AbstractDaemonTest {
.reviewer(user.email())
.label("Code-Review", 1)
.setWorkInProgress(true);
- gApi.changes().id(r.getChangeId()).revision("current").review(in);
+ gApi.changes().id(r.getChangeId()).current().review(in);
ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
assertThat(info.workInProgress).isTrue();
@@ -583,7 +634,7 @@ public class ChangeIT extends AbstractDaemonTest {
ReviewInput in = ReviewInput.noScore();
in.ready = true;
in.workInProgress = true;
- ReviewResult result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
+ ReviewResult result = gApi.changes().id(r.getChangeId()).current().review(in);
assertThat(result.error).isEqualTo(PostReview.ERROR_WIP_READY_MUTUALLY_EXCLUSIVE);
}
@@ -622,21 +673,24 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("toggle work in progress state not permitted");
- gApi.changes().id(r.getChangeId()).current().review(in);
+ AuthException thrown =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(r.getChangeId()).current().review(in));
+ assertThat(thrown).hasMessageThat().contains("toggle work in progress state not permitted");
}
@Test
public void reviewWithWorkInProgressByNonOwnerWithPermission() throws Exception {
PushOneCommit.Result r = createChange();
ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
- grant(
- project,
- "refs/heads/master",
- Permission.TOGGLE_WORK_IN_PROGRESS_STATE,
- false,
- REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allow(Permission.TOGGLE_WORK_IN_PROGRESS_STATE)
+ .ref("refs/heads/master")
+ .group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).current().review(in);
ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
@@ -648,9 +702,10 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
ReviewInput in = ReviewInput.noScore().setReady(true);
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("toggle work in progress state not permitted");
- gApi.changes().id(r.getChangeId()).current().review(in);
+ AuthException thrown =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(r.getChangeId()).current().review(in));
+ assertThat(thrown).hasMessageThat().contains("toggle work in progress state not permitted");
}
@Test
@@ -674,105 +729,9 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r2 = push2.to("refs/for/other");
assertThat(r2.getChangeId()).isEqualTo(changeId);
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Multiple changes found for " + changeId);
- gApi.changes().id(changeId).get();
- }
-
- @Test
- public void revert() throws Exception {
- PushOneCommit.Result r = createChange();
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
- ChangeInfo revertChange = gApi.changes().id(r.getChangeId()).revert().get();
-
- // expected messages on source change:
- // 1. Uploaded patch set 1.
- // 2. Patch Set 1: Code-Review+2
- // 3. Change has been successfully merged by Administrator
- // 4. Patch Set 1: Reverted
- List<ChangeMessageInfo> sourceMessages =
- new ArrayList<>(gApi.changes().id(r.getChangeId()).get().messages);
- assertThat(sourceMessages).hasSize(4);
- String expectedMessage =
- String.format("Created a revert of this change as %s", revertChange.changeId);
- assertThat(sourceMessages.get(3).message).isEqualTo(expectedMessage);
-
- assertThat(revertChange.messages).hasSize(1);
- assertThat(revertChange.messages.iterator().next().message).isEqualTo("Uploaded patch set 1.");
- assertThat(revertChange.revertOf).isEqualTo(gApi.changes().id(r.getChangeId()).get()._number);
- }
-
- @Test
- public void revertNotifications() throws Exception {
- PushOneCommit.Result r = createChange();
- gApi.changes().id(r.getChangeId()).addReviewer(user.email());
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
-
- sender.clear();
- ChangeInfo revertChange = gApi.changes().id(r.getChangeId()).revert().get();
-
- List<Message> messages = sender.getMessages();
- assertThat(messages).hasSize(2);
- assertThat(sender.getMessages(revertChange.changeId, "newchange")).hasSize(1);
- assertThat(sender.getMessages(r.getChangeId(), "revert")).hasSize(1);
- }
-
- @Test
- public void suppressRevertNotifications() throws Exception {
- PushOneCommit.Result r = createChange();
- gApi.changes().id(r.getChangeId()).addReviewer(user.email());
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
-
- RevertInput revertInput = new RevertInput();
- revertInput.notify = NotifyHandling.NONE;
-
- sender.clear();
- gApi.changes().id(r.getChangeId()).revert(revertInput).get();
- assertThat(sender.getMessages()).isEmpty();
- }
-
- @Test
- public void revertPreservesReviewersAndCcs() throws Exception {
- PushOneCommit.Result r = createChange();
-
- ReviewInput in = ReviewInput.approve();
- in.reviewer(user.email());
- in.reviewer(accountCreator.user2().email(), ReviewerState.CC, true);
- // Add user as reviewer that will create the revert
- in.reviewer(accountCreator.admin2().email());
-
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
-
- // expect both the original reviewers and CCs to be preserved
- // original owner should be added as reviewer, user requesting the revert (new owner) removed
- requestScopeOperations.setApiUser(accountCreator.admin2().id());
- Map<ReviewerState, Collection<AccountInfo>> result =
- gApi.changes().id(r.getChangeId()).revert().get().reviewers;
- assertThat(result).containsKey(ReviewerState.REVIEWER);
-
- List<Integer> reviewers =
- result.get(ReviewerState.REVIEWER).stream().map(a -> a._accountId).collect(toList());
- assertThat(result).containsKey(ReviewerState.CC);
- List<Integer> ccs =
- result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
- assertThat(ccs).containsExactly(accountCreator.user2().id().get());
- assertThat(reviewers).containsExactly(user.id().get(), admin.id().get());
- }
-
- @Test
- @TestProjectInput(createEmptyCommit = false)
- public void revertInitialCommit() throws Exception {
- PushOneCommit.Result r = createChange();
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
-
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Cannot revert initial commit");
- gApi.changes().id(r.getChangeId()).revert();
+ ResourceNotFoundException thrown =
+ assertThrows(ResourceNotFoundException.class, () -> gApi.changes().id(changeId).get());
+ assertThat(thrown).hasMessageThat().contains("Multiple changes found for " + changeId);
}
@FunctionalInterface
@@ -827,19 +786,10 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(cr.all.get(0).value).isEqualTo(1);
// Rebasing the second change again should fail
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Change is already up to date");
- gApi.changes().id(changeId).current().rebase();
- }
-
- @Test
- public void rebaseOnNonExistingChange() throws Exception {
- String changeId = createChange().getChangeId();
- RebaseInput in = new RebaseInput();
- in.base = "999999";
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("Base change not found: " + in.base);
- gApi.changes().id(changeId).rebase(in);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.changes().id(changeId).current().rebase());
+ assertThat(thrown).hasMessageThat().contains("Change is already up to date");
}
@Test
@@ -902,6 +852,17 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void rebaseOnNonExistingChange() throws Exception {
+ String changeId = createChange().getChangeId();
+ RebaseInput in = new RebaseInput();
+ in.base = "999999";
+ UnprocessableEntityException exception =
+ assertThrows(
+ UnprocessableEntityException.class, () -> gApi.changes().id(changeId).rebase(in));
+ assertThat(exception).hasMessageThat().isEqualTo("Base change not found: " + in.base);
+ }
+
+ @Test
public void rebaseFromRelationChainToClosedChange() throws Exception {
PushOneCommit.Result r1 = createChange();
testRepo.reset("HEAD~1");
@@ -942,9 +903,9 @@ public class ChangeIT extends AbstractDaemonTest {
// Rebase the second
String changeId = r2.getChangeId();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("rebase not permitted");
- gApi.changes().id(changeId).rebase();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).rebase());
+ assertThat(thrown).hasMessageThat().contains("rebase not permitted");
}
@Test
@@ -959,7 +920,11 @@ public class ChangeIT extends AbstractDaemonTest {
revision.review(ReviewInput.approve());
revision.submit();
- grant(project, "refs/heads/master", Permission.REBASE, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.REBASE).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
// Rebase the second
String changeId = r2.getChangeId();
@@ -979,15 +944,19 @@ public class ChangeIT extends AbstractDaemonTest {
revision.review(ReviewInput.approve());
revision.submit();
- grant(project, "refs/heads/master", Permission.REBASE, false, REGISTERED_USERS);
- block("refs/for/*", Permission.PUSH, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.REBASE).ref("refs/heads/master").group(REGISTERED_USERS))
+ .add(block(Permission.PUSH).ref("refs/for/*").group(REGISTERED_USERS))
+ .update();
// Rebase the second
String changeId = r2.getChangeId();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("rebase not permitted");
- gApi.changes().id(changeId).rebase();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).rebase());
+ assertThat(thrown).hasMessageThat().contains("rebase not permitted");
}
@Test
@@ -1002,13 +971,17 @@ public class ChangeIT extends AbstractDaemonTest {
revision.review(ReviewInput.approve());
revision.submit();
- block("refs/for/*", Permission.PUSH, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.PUSH).ref("refs/for/*").group(REGISTERED_USERS))
+ .update();
// Rebase the second
String changeId = r2.getChangeId();
- exception.expect(AuthException.class);
- exception.expectMessage("rebase not permitted");
- gApi.changes().id(changeId).rebase();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).rebase());
+ assertThat(thrown).hasMessageThat().contains("rebase not permitted");
}
@Test
@@ -1024,14 +997,18 @@ public class ChangeIT extends AbstractDaemonTest {
String changeId = changeResult.getChangeId();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("delete not permitted");
- gApi.changes().id(changeId).delete();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).delete());
+ assertThat(thrown).hasMessageThat().contains("delete not permitted");
}
@Test
public void deleteNewChangeAsUserWithDeleteChangesPermissionForGroup() throws Exception {
- allow("refs/*", Permission.DELETE_CHANGES, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE_CHANGES).ref("refs/*").group(REGISTERED_USERS))
+ .update();
deleteChangeAsUser(admin, user);
}
@@ -1040,32 +1017,40 @@ public class ChangeIT extends AbstractDaemonTest {
GroupApi groupApi = gApi.groups().create(name("delete-change"));
groupApi.addMembers("user");
+ Project.NameKey nameKey = Project.nameKey(name("delete-change"));
ProjectInput in = new ProjectInput();
- in.name = name("delete-change");
+ in.name = nameKey.get();
in.owners = Lists.newArrayListWithCapacity(1);
in.owners.add(groupApi.name());
in.createEmptyCommit = true;
- ProjectApi api = gApi.projects().create(in);
+ gApi.projects().create(in);
- Project.NameKey nameKey = new Project.NameKey(api.get().name);
-
- try (ProjectConfigUpdate u = updateProject(nameKey)) {
- Util.allow(u.getConfig(), Permission.DELETE_CHANGES, PROJECT_OWNERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(nameKey)
+ .forUpdate()
+ .add(allow(Permission.DELETE_CHANGES).ref("refs/*").group(PROJECT_OWNERS))
+ .update();
deleteChangeAsUser(nameKey, admin, user);
}
@Test
public void deleteChangeAsUserWithDeleteOwnChangesPermissionForGroup() throws Exception {
- allow("refs/*", Permission.DELETE_OWN_CHANGES, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE_OWN_CHANGES).ref("refs/*").group(REGISTERED_USERS))
+ .update();
deleteChangeAsUser(user, user);
}
@Test
public void deleteChangeAsUserWithDeleteOwnChangesPermissionForOwners() throws Exception {
- allow("refs/*", Permission.DELETE_OWN_CHANGES, CHANGE_OWNER);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE_OWN_CHANGES).ref("refs/*").group(CHANGE_OWNER))
+ .update();
deleteChangeAsUser(user, user);
}
@@ -1099,12 +1084,16 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(query(changeId)).isEmpty();
- String ref = new Change.Id(id).toRefPrefix() + "1";
+ String ref = Change.id(id).toRefPrefix() + "1";
eventRecorder.assertRefUpdatedEvents(projectName.get(), ref, null, commit, commit, null);
eventRecorder.assertChangeDeletedEvents(changeId, deleteAs.email());
} finally {
- removePermission(project, "refs/*", Permission.DELETE_OWN_CHANGES);
- removePermission(project, "refs/*", Permission.DELETE_CHANGES);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(permissionKey(Permission.DELETE_OWN_CHANGES).ref("refs/*"))
+ .remove(permissionKey(Permission.DELETE_CHANGES).ref("refs/*"))
+ .update();
}
}
@@ -1115,18 +1104,26 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void deleteNewChangeOfAnotherUserWithDeleteOwnChangesPermission() throws Exception {
- allow("refs/*", Permission.DELETE_OWN_CHANGES, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE_OWN_CHANGES).ref("refs/*").group(REGISTERED_USERS))
+ .update();
try {
PushOneCommit.Result changeResult = createChange();
String changeId = changeResult.getChangeId();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("delete not permitted");
- gApi.changes().id(changeId).delete();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).delete());
+ assertThat(thrown).hasMessageThat().contains("delete not permitted");
} finally {
- removePermission(project, "refs/*", Permission.DELETE_OWN_CHANGES);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(permissionKey(Permission.DELETE_OWN_CHANGES).ref("refs/*"))
+ .update();
}
}
@@ -1151,9 +1148,9 @@ public class ChangeIT extends AbstractDaemonTest {
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).abandon();
- exception.expect(AuthException.class);
- exception.expectMessage("delete not permitted");
- gApi.changes().id(changeId).delete();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).delete());
+ assertThat(thrown).hasMessageThat().contains("delete not permitted");
}
@Test
@@ -1177,15 +1174,19 @@ public class ChangeIT extends AbstractDaemonTest {
merge(changeResult);
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("delete not permitted");
- gApi.changes().id(changeId).delete();
+ MethodNotAllowedException thrown =
+ assertThrows(MethodNotAllowedException.class, () -> gApi.changes().id(changeId).delete());
+ assertThat(thrown).hasMessageThat().contains("delete not permitted");
}
@Test
@TestProjectInput(cloneAs = "user")
public void deleteMergedChangeWithDeleteOwnChangesPermission() throws Exception {
- allow("refs/*", Permission.DELETE_OWN_CHANGES, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE_OWN_CHANGES).ref("refs/*").group(REGISTERED_USERS))
+ .update();
try {
PushOneCommit.Result changeResult =
@@ -1195,11 +1196,15 @@ public class ChangeIT extends AbstractDaemonTest {
merge(changeResult);
requestScopeOperations.setApiUser(user.id());
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("delete not permitted");
- gApi.changes().id(changeId).delete();
+ MethodNotAllowedException thrown =
+ assertThrows(MethodNotAllowedException.class, () -> gApi.changes().id(changeId).delete());
+ assertThat(thrown).hasMessageThat().contains("delete not permitted");
} finally {
- removePermission(project, "refs/*", Permission.DELETE_OWN_CHANGES);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(permissionKey(Permission.DELETE_OWN_CHANGES).ref("refs/*"))
+ .update();
}
}
@@ -1212,10 +1217,11 @@ public class ChangeIT extends AbstractDaemonTest {
merge(changeResult);
setChangeStatus(id, Change.Status.NEW);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- String.format("Cannot delete change %s: patch set 1 is already merged", id));
- gApi.changes().id(changeId).delete();
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.changes().id(changeId).delete());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("Cannot delete change %s: patch set 1 is already merged", id));
}
@Test
@@ -1229,10 +1235,10 @@ public class ChangeIT extends AbstractDaemonTest {
Optional<ChangeData> result =
idx.get(id, IndexedChangeQuery.createOptions(indexConfig, 0, 1, ImmutableSet.of()));
- assertThat(result.isPresent()).isTrue();
+ assertThat(result).isPresent();
gApi.changes().id(changeId).delete();
result = idx.get(id, IndexedChangeQuery.createOptions(indexConfig, 0, 1, ImmutableSet.of()));
- assertThat(result.isPresent()).isFalse();
+ assertThat(result).isEmpty();
}
@Test
@@ -1264,18 +1270,64 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void deleteChangeRemovesItsChangeEdit() throws Exception {
+ PushOneCommit.Result result = createChange();
+
+ requestScopeOperations.setApiUser(user.id());
+ String changeId = result.getChangeId();
+ gApi.changes().id(changeId).edit().create();
+ gApi.changes()
+ .id(changeId)
+ .edit()
+ .modifyFile(FILE_NAME, RawInputUtil.create("foo".getBytes(UTF_8)));
+
+ requestScopeOperations.setApiUser(admin.id());
+ try (Repository repo = repoManager.openRepository(project)) {
+ String expected =
+ RefNames.refsUsers(user.id()) + "/edit-" + result.getChange().getId() + "/1";
+ assertThat(repo.getRefDatabase().getRefsByPrefix(expected)).isNotEmpty();
+ gApi.changes().id(changeId).delete();
+ assertThat(repo.getRefDatabase().getRefsByPrefix(expected)).isEmpty();
+ }
+ }
+
+ @Test
+ public void deleteChangeDoesntRemoveOtherChangeEdits() throws Exception {
+ PushOneCommit.Result result = createChange();
+ PushOneCommit.Result irrelevantChangeResult = createChange();
+ requestScopeOperations.setApiUser(admin.id());
+ String changeId = result.getChangeId();
+ String irrelevantChangeId = irrelevantChangeResult.getChangeId();
+
+ gApi.changes().id(irrelevantChangeId).edit().create();
+ gApi.changes()
+ .id(irrelevantChangeId)
+ .edit()
+ .modifyFile(FILE_NAME, RawInputUtil.create("foo".getBytes(UTF_8)));
+
+ gApi.changes().id(changeId).delete();
+
+ assertThat(gApi.changes().id(irrelevantChangeId).edit().get()).isPresent();
+ }
+
+ @Test
public void rebaseUpToDateChange() throws Exception {
PushOneCommit.Result r = createChange();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Change is already up to date");
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).rebase();
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).rebase());
+ assertThat(thrown).hasMessageThat().contains("Change is already up to date");
}
@Test
public void rebaseConflict() throws Exception {
- PushOneCommit.Result r = createChange();
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+ PushOneCommit.Result r1 = createChange();
+ gApi.changes()
+ .id(r1.getChangeId())
+ .revision(r1.getCommit().name())
+ .review(ReviewInput.approve());
+ gApi.changes().id(r1.getChangeId()).revision(r1.getCommit().name()).submit();
PushOneCommit push =
pushFactory.create(
@@ -1285,11 +1337,11 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.FILE_NAME,
"other content",
"If09d8782c1e59dd0b33de2b1ec3595d69cc10ad5");
- r = push.to("refs/for/master");
- r.assertOkStatus();
-
- exception.expect(ResourceConflictException.class);
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).rebase();
+ PushOneCommit.Result r2 = push.to("refs/for/master");
+ r2.assertOkStatus();
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r2.getChangeId()).revision(r2.getCommit().name()).rebase());
}
@Test
@@ -1303,23 +1355,23 @@ public class ChangeIT extends AbstractDaemonTest {
ri.base = "";
gApi.changes().id(r3.getChangeId()).revision(r3.getCommit().name()).rebase(ri);
PatchSet ps3 = r3.getPatchSet();
- assertThat(ps3.getId().get()).isEqualTo(2);
+ assertThat(ps3.id().get()).isEqualTo(2);
// rebase r2 onto r3 (referenced by ref)
- ri.base = ps3.getId().toRefName();
+ ri.base = ps3.id().toRefName();
gApi.changes().id(r2.getChangeId()).revision(r2.getCommit().name()).rebase(ri);
PatchSet ps2 = r2.getPatchSet();
- assertThat(ps2.getId().get()).isEqualTo(2);
+ assertThat(ps2.id().get()).isEqualTo(2);
// rebase r1 onto r2 (referenced by commit)
- ri.base = ps2.getRevision().get();
+ ri.base = ps2.commitId().name();
gApi.changes().id(r1.getChangeId()).revision(r1.getCommit().name()).rebase(ri);
PatchSet ps1 = r1.getPatchSet();
- assertThat(ps1.getId().get()).isEqualTo(2);
+ assertThat(ps1.id().get()).isEqualTo(2);
// rebase r1 onto r3 (referenced by change number)
ri.base = String.valueOf(r3.getChange().getId().get());
- gApi.changes().id(r1.getChangeId()).revision(ps1.getRevision().get()).rebase(ri);
+ gApi.changes().id(r1.getChangeId()).revision(ps1.commitId().name()).rebase(ri);
assertThat(r1.getPatchSetId().get()).isEqualTo(3);
}
@@ -1334,9 +1386,11 @@ public class ChangeIT extends AbstractDaemonTest {
"base change "
+ r2.getChangeId()
+ " is a descendant of the current change - recursion not allowed";
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(expectedMessage);
- gApi.changes().id(r1.getChangeId()).revision(r1.getCommit().name()).rebase(ri);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r1.getChangeId()).revision(r1.getCommit().name()).rebase(ri));
+ assertThat(thrown).hasMessageThat().contains(expectedMessage);
}
@Test
@@ -1348,9 +1402,11 @@ public class ChangeIT extends AbstractDaemonTest {
ChangeInfo info = info(changeId);
assertThat(info.status).isEqualTo(ChangeStatus.ABANDONED);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("change is abandoned");
- gApi.changes().id(changeId).revision(r.getCommit().name()).rebase();
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).revision(r.getCommit().name()).rebase());
+ assertThat(thrown).hasMessageThat().contains("change is abandoned");
}
@Test
@@ -1370,9 +1426,11 @@ public class ChangeIT extends AbstractDaemonTest {
RebaseInput ri = new RebaseInput();
ri.base = r.getCommit().name();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("base change is abandoned: " + changeId);
- gApi.changes().id(r2.getChangeId()).revision(r2.getCommit().name()).rebase(ri);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r2.getChangeId()).revision(r2.getCommit().name()).rebase(ri));
+ assertThat(thrown).hasMessageThat().contains("base change is abandoned: " + changeId);
}
@Test
@@ -1382,9 +1440,11 @@ public class ChangeIT extends AbstractDaemonTest {
String commit = r.getCommit().name();
RebaseInput ri = new RebaseInput();
ri.base = commit;
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("cannot rebase change onto itself");
- gApi.changes().id(changeId).revision(commit).rebase(ri);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).revision(commit).rebase(ri));
+ assertThat(thrown).hasMessageThat().contains("cannot rebase change onto itself");
}
@Test
@@ -1466,11 +1526,12 @@ public class ChangeIT extends AbstractDaemonTest {
public void pushCommitOfOtherUserThatCannotSeeChange() throws Exception {
// create hidden project that is only visible to administrators
Project.NameKey p = projectOperations.newProject().create();
- try (ProjectConfigUpdate u = updateProject(p)) {
- Util.allow(u.getConfig(), Permission.READ, adminGroupUuid(), "refs/*");
- Util.block(u.getConfig(), Permission.READ, REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(adminGroupUuid()))
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// admin pushes commit of user
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
@@ -1486,12 +1547,8 @@ public class ChangeIT extends AbstractDaemonTest {
// check the user cannot see the change
requestScopeOperations.setApiUser(user.id());
- try {
- gApi.changes().id(result.getChangeId()).get();
- fail("Expected ResourceNotFoundException");
- } catch (ResourceNotFoundException e) {
- // Expected.
- }
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.changes().id(result.getChangeId()).get());
// check that the author/committer was NOT added as reviewer (he can't see
// the change)
@@ -1539,11 +1596,12 @@ public class ChangeIT extends AbstractDaemonTest {
public void pushCommitWithFooterOfOtherUserThatCannotSeeChange() throws Exception {
// create hidden project that is only visible to administrators
Project.NameKey p = projectOperations.newProject().create();
- try (ProjectConfigUpdate u = updateProject(p)) {
- Util.allow(u.getConfig(), Permission.READ, adminGroupUuid(), "refs/*");
- Util.block(u.getConfig(), Permission.READ, REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(adminGroupUuid()))
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// admin pushes commit that references 'user' in a footer
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
@@ -1563,12 +1621,8 @@ public class ChangeIT extends AbstractDaemonTest {
// check that 'user' cannot see the change
requestScopeOperations.setApiUser(user.id());
- try {
- gApi.changes().id(result.getChangeId()).get();
- fail("Expected ResourceNotFoundException");
- } catch (ResourceNotFoundException e) {
- // Expected.
- }
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.changes().id(result.getChangeId()).get());
// check that 'user' was NOT added as cc ('user' can't see the change)
requestScopeOperations.setApiUser(admin.id());
@@ -1582,11 +1636,12 @@ public class ChangeIT extends AbstractDaemonTest {
public void addReviewerThatCannotSeeChange() throws Exception {
// create hidden project that is only visible to administrators
Project.NameKey p = projectOperations.newProject().create();
- try (ProjectConfigUpdate u = updateProject(p)) {
- Util.allow(u.getConfig(), Permission.READ, adminGroupUuid(), "refs/*");
- Util.block(u.getConfig(), Permission.READ, REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(adminGroupUuid()))
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// create change
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
@@ -1596,12 +1651,8 @@ public class ChangeIT extends AbstractDaemonTest {
// check the user cannot see the change
requestScopeOperations.setApiUser(user.id());
- try {
- gApi.changes().id(result.getChangeId()).get();
- fail("Expected ResourceNotFoundException");
- } catch (ResourceNotFoundException e) {
- // Expected.
- }
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.changes().id(result.getChangeId()).get());
// try to add user as reviewer
requestScopeOperations.setApiUser(admin.id());
@@ -1684,16 +1735,36 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ @UseClockStep
public void addReviewer() throws Exception {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
+ testAddReviewerViaPostReview(
+ (changeId, reviewer) -> {
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = reviewer;
+ gApi.changes().id(changeId).addReviewer(in);
+ });
+ }
+
+ @Test
+ @UseClockStep
+ public void addReviewerViaPostReview() throws Exception {
+ testAddReviewerViaPostReview(
+ (changeId, reviewer) -> {
+ AddReviewerInput addReviewerInput = new AddReviewerInput();
+ addReviewerInput.reviewer = reviewer;
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.reviewers = ImmutableList.of(addReviewerInput);
+ gApi.changes().id(changeId).current().review(reviewInput);
+ });
+ }
+
+ private void testAddReviewerViaPostReview(AddReviewerCaller addReviewer) throws Exception {
PushOneCommit.Result r = createChange();
ChangeResource rsrc = parseResource(r);
String oldETag = rsrc.getETag();
Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
- AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email();
- gApi.changes().id(r.getChangeId()).addReviewer(in);
+ addReviewer.call(r.getChangeId(), user.email());
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
@@ -1705,15 +1776,15 @@ public class ChangeIT extends AbstractDaemonTest {
assertMailReplyTo(m, admin.email());
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- // When NoteDb is enabled adding a reviewer records that user as reviewer
- // in NoteDb. When NoteDb is disabled adding a reviewer results in a dummy 0
- // approval on the change which is treated as CC when the ChangeInfo is
- // created.
+ // Adding a reviewer records that user as reviewer.
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
+ // Nobody was added as CC.
+ assertThat(c.reviewers.get(CC)).isNull();
+
// Ensure ETag and lastUpdatedOn are updated.
rsrc = parseResource(r);
assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
@@ -1727,6 +1798,19 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void postingMessageOnOwnChangeDoesntAddCallerAsReviewer() throws Exception {
+ PushOneCommit.Result r = createChange();
+
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.message = "Foo Bar";
+ gApi.changes().id(r.getChangeId()).current().review(reviewInput);
+
+ ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
+ assertThat(c.reviewers.get(REVIEWER)).isNull();
+ assertThat(c.reviewers.get(CC)).isNull();
+ }
+
+ @Test
public void listReviewers() throws Exception {
PushOneCommit.Result r = createChange();
AddReviewerInput in = new AddReviewerInput();
@@ -1772,20 +1856,20 @@ public class ChangeIT extends AbstractDaemonTest {
// In this case, the child ReviewerInput has a notify=OWNER_REVIEWERS
// that should be ignored.
r = createWorkInProgressChange();
- gApi.changes().id(r.getChangeId()).revision("current").review(batchIn);
+ gApi.changes().id(r.getChangeId()).current().review(batchIn);
assertThat(sender.getMessages()).isEmpty();
// Top-level notify property can force notifications when adding reviewer
// via PostReview.
r = createWorkInProgressChange();
batchIn.notify = NotifyHandling.OWNER_REVIEWERS;
- gApi.changes().id(r.getChangeId()).revision("current").review(batchIn);
+ gApi.changes().id(r.getChangeId()).current().review(batchIn);
assertThat(sender.getMessages()).hasSize(1);
}
@Test
+ @UseClockStep
public void addReviewerThatIsNotPerfectMatch() throws Exception {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
PushOneCommit.Result r = createChange();
ChangeResource rsrc = parseResource(r);
String oldETag = rsrc.getETag();
@@ -1820,10 +1904,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertMailReplyTo(m, email);
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- // When NoteDb is enabled adding a reviewer records that user as reviewer
- // in NoteDb. When NoteDb is disabled adding a reviewer results in a dummy 0
- // approval on the change which is treated as CC when the ChangeInfo is
- // created.
+ // Adding a reviewer records that user as reviewer.
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
@@ -1836,8 +1917,8 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ @UseClockStep
public void addGroupAsReviewersWhenANotPerfectMatchedUserExists() throws Exception {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
PushOneCommit.Result r = createChange();
ChangeResource rsrc = parseResource(r);
String oldETag = rsrc.getETag();
@@ -1884,10 +1965,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertMailReplyTo(m, myGroupUserEmail);
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- // When NoteDb is enabled adding a reviewer records that user as reviewer
- // in NoteDb. When NoteDb is disabled adding a reviewer results in a dummy 0
- // approval on the change which is treated as CC when the ChangeInfo is
- // created.
+ // Adding a reviewer records that user as reviewer.
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
@@ -1900,8 +1978,8 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ @UseClockStep
public void addSelfAsReviewer() throws Exception {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
PushOneCommit.Result r = createChange();
ChangeResource rsrc = parseResource(r);
String oldETag = rsrc.getETag();
@@ -1915,10 +1993,7 @@ public class ChangeIT extends AbstractDaemonTest {
// There should be no email notification when adding self
assertThat(sender.getMessages()).isEmpty();
- // When NoteDb is enabled adding a reviewer records that user as reviewer
- // in NoteDb. When NoteDb is disabled adding a reviewer results in a dummy 0
- // approval on the change which is treated as CC when the ChangeInfo is
- // created.
+ // Adding a reviewer records that user as reviewer.
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
@@ -1974,7 +2049,7 @@ public class ChangeIT extends AbstractDaemonTest {
.containsExactly(user.id().get());
// Further test: remove the vote, then comment again. The user should be
- // implicitly re-added to the ReviewerSet, as a CC if we're using NoteDb.
+ // implicitly re-added to the ReviewerSet, as a CC.
requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).remove();
c = gApi.changes().id(r.getChangeId()).get();
@@ -2027,6 +2102,45 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void pluginCanContributeToETagComputation() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String oldETag = parseResource(r).getETag();
+
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(TestChangeETagComputation.withETag("foo"))) {
+ assertThat(parseResource(r).getETag()).isNotEqualTo(oldETag);
+ }
+
+ assertThat(parseResource(r).getETag()).isEqualTo(oldETag);
+ }
+
+ @Test
+ public void returningNullFromETagComputationDoesNotBreakGerrit() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String oldETag = parseResource(r).getETag();
+
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(TestChangeETagComputation.withETag(null))) {
+ assertThat(parseResource(r).getETag()).isEqualTo(oldETag);
+ }
+ }
+
+ @Test
+ public void throwingExceptionFromETagComputationDoesNotBreakGerrit() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String oldETag = parseResource(r).getETag();
+
+ try (Registration registration =
+ extensionRegistry
+ .newRegistration()
+ .add(
+ TestChangeETagComputation.withException(
+ new StorageException("exception during test")))) {
+ assertThat(parseResource(r).getETag()).isEqualTo(oldETag);
+ }
+ }
+
+ @Test
public void emailNotificationForFileLevelComment() throws Exception {
String changeId = createChange().getChangeId();
@@ -2067,8 +2181,8 @@ public class ChangeIT extends AbstractDaemonTest {
comment.message = "comment 1";
review.comments = ImmutableMap.of(comment.path, Lists.newArrayList(comment));
- exception.expect(BadRequestException.class);
- gApi.changes().id(changeId).current().review(review);
+ assertThrows(
+ BadRequestException.class, () -> gApi.changes().id(changeId).current().review(review));
}
@Test
@@ -2093,21 +2207,21 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void removeReviewerNoVotes() throws Exception {
+ LabelType verified =
+ label("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
try (ProjectConfigUpdate u = updateProject(project)) {
- LabelType verified =
- category("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
u.getConfig().getLabelSections().put(verified.getName(), verified);
- AccountGroup.UUID registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
- String heads = RefNames.REFS_HEADS + "*";
- Util.allow(
- u.getConfig(),
- Permission.forLabel(Util.verified().getName()),
- -1,
- 1,
- registeredUsers,
- heads);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel(verified.getName())
+ .ref(RefNames.REFS_HEADS + "*")
+ .group(REGISTERED_USERS)
+ .range(-1, 1))
+ .update();
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
@@ -2135,8 +2249,9 @@ public class ChangeIT extends AbstractDaemonTest {
// Remove again, and then try to remove once more to verify 404 is
// returned.
gApi.changes().id(changeId).reviewer(user.id().toString()).remove();
- exception.expect(ResourceNotFoundException.class);
- gApi.changes().id(changeId).reviewer(user.id().toString()).remove();
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(changeId).reviewer(user.id().toString()).remove());
}
@Test
@@ -2197,9 +2312,11 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).revision(r.getCommit().name()).review(ReviewInput.approve());
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).remove();
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).remove());
+ assertThat(thrown).hasMessageThat().contains("remove reviewer not permitted");
}
@Test
@@ -2215,9 +2332,11 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).revision(r.getCommit().name()).submit();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer("self").remove();
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(r.getChangeId()).reviewer("self").remove());
+ assertThat(thrown).hasMessageThat().contains("remove reviewer not permitted");
}
@Test
@@ -2249,9 +2368,11 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).abandon();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).remove();
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).remove());
+ assertThat(thrown).hasMessageThat().contains("remove reviewer not permitted");
}
@Test
@@ -2359,24 +2480,32 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("delete vote not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).deleteVote("Code-Review");
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () ->
+ gApi.changes()
+ .id(r.getChangeId())
+ .reviewer(admin.id().toString())
+ .deleteVote("Code-Review"));
+ assertThat(thrown).hasMessageThat().contains("delete vote not permitted");
}
@Test
public void nonVotingReviewerStaysAfterSubmit() throws Exception {
LabelType verified =
- category("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
+ label("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
+ String heads = "refs/heads/*";
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getLabelSections().put(verified.getName(), verified);
- String heads = "refs/heads/*";
- AccountGroup.UUID owners = systemGroupBackend.getGroup(CHANGE_OWNER).getUUID();
- AccountGroup.UUID registered = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
- Util.allow(u.getConfig(), Permission.forLabel(verified.getName()), -1, 1, owners, heads);
- Util.allow(u.getConfig(), Permission.forLabel("Code-Review"), -2, +2, registered, heads);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(verified.getName()).ref(heads).group(CHANGE_OWNER).range(-1, 1))
+ .add(allowLabel("Code-Review").ref(heads).group(REGISTERED_USERS).range(-2, +2))
+ .update();
// Set Code-Review+2 and Verified+1 as admin (change owner)
PushOneCommit.Result r = createChange();
@@ -2472,8 +2601,13 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void queryChangesNoLimit() throws Exception {
- allowGlobalCapabilities(
- SystemGroupBackend.REGISTERED_USERS, 0, 2, GlobalCapability.QUERY_LIMIT);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(
+ allowCapability(GlobalCapability.QUERY_LIMIT)
+ .group(SystemGroupBackend.REGISTERED_USERS)
+ .range(0, 2))
+ .update();
for (int i = 0; i < 3; i++) {
createChange();
}
@@ -2611,16 +2745,21 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("");
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("edit topic name not permitted");
- gApi.changes().id(r.getChangeId()).topic("mytopic");
+ AuthException thrown =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(r.getChangeId()).topic("mytopic"));
+ assertThat(thrown).hasMessageThat().contains("edit topic name not permitted");
}
@Test
public void editTopicWithPermissionAllowed() throws Exception {
PushOneCommit.Result r = createChange();
assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("");
- grant(project, "refs/heads/master", Permission.EDIT_TOPIC_NAME, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.EDIT_TOPIC_NAME).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).topic("mytopic");
assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("mytopic");
@@ -2666,16 +2805,22 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("submit not permitted");
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit());
+ assertThat(thrown).hasMessageThat().contains("submit not permitted");
}
@Test
public void submitAllowedWithPermission() throws Exception {
PushOneCommit.Result r = createChange();
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- grant(project, "refs/heads/master", Permission.SUBMIT, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
assertThat(gApi.changes().id(r.getChangeId()).info().status).isEqualTo(ChangeStatus.MERGED);
@@ -2691,22 +2836,24 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void commitFooters() throws Exception {
LabelType verified =
- category("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
+ label("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
LabelType custom1 =
- category("Custom1", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
+ label("Custom1", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
LabelType custom2 =
- category("Custom2", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
+ label("Custom2", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getLabelSections().put(verified.getName(), verified);
u.getConfig().getLabelSections().put(custom1.getName(), custom1);
u.getConfig().getLabelSections().put(custom2.getName(), custom2);
- String heads = "refs/heads/*";
- AccountGroup.UUID anon = systemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID();
- Util.allow(u.getConfig(), Permission.forLabel("Verified"), -1, 1, anon, heads);
- Util.allow(u.getConfig(), Permission.forLabel("Custom1"), -1, 1, anon, heads);
- Util.allow(u.getConfig(), Permission.forLabel("Custom2"), -1, 1, anon, heads);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(verified.getName()).ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, 1))
+ .add(allowLabel(custom1.getName()).ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, 1))
+ .add(allowLabel(custom2.getName()).ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, 1))
+ .update();
PushOneCommit.Result r1 = createChange();
r1.assertOkStatus();
@@ -2756,18 +2903,16 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void customCommitFooters() throws Exception {
PushOneCommit.Result change = createChange();
- RegistrationHandle handle =
- changeMessageModifiers.add(
- "gerrit",
- (newCommitMessage, original, mergeTip, destination) -> {
- assertThat(original.getName()).isNotEqualTo(mergeTip.getName());
- return newCommitMessage + "Custom: " + destination.get();
- });
ChangeInfo actual;
- try {
+ try (Registration registration =
+ extensionRegistry
+ .newRegistration()
+ .add(
+ (newCommitMessage, original, mergeTip, destination) -> {
+ assertThat(original.getName()).isNotEqualTo(mergeTip.getName());
+ return newCommitMessage + "Custom: " + destination.branch();
+ })) {
actual = gApi.changes().id(change.getChangeId()).get(ALL_REVISIONS, COMMIT_FOOTERS);
- } finally {
- handle.remove();
}
List<String> footers =
new ArrayList<>(
@@ -2829,10 +2974,11 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(approval._accountId).isEqualTo(user.id().get());
assertThat(approval.value).isEqualTo(0);
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.blockLabel(u.getConfig(), "Code-Review", REGISTERED_USERS, "refs/heads/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-1, 1))
+ .update();
c = gApi.changes().id(triplet).get(DETAILED_LABELS);
codeReview = c.labels.get("Code-Review");
@@ -2878,13 +3024,13 @@ public class ChangeIT extends AbstractDaemonTest {
info = gApi.changes().id(info._number).get();
assertThat(info.changeId).isEqualTo(r.getChangeId());
-
- exception.expect(AuthException.class);
- gApi.changes().id(triplet).current().review(ReviewInput.approve());
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(triplet).current().review(ReviewInput.approve()));
}
@Test
- public void noteDbCommitsOnPatchSetCreation() throws Exception {
+ public void commitsOnPatchSetCreation() throws Exception {
PushOneCommit.Result r = createChange();
pushFactory
.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "4711", r.getChangeId())
@@ -2894,7 +3040,7 @@ public class ChangeIT extends AbstractDaemonTest {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
RevCommit commitPatchSetCreation =
- rw.parseCommit(repo.exactRef(changeMetaRef(new Change.Id(c._number))).getObjectId());
+ rw.parseCommit(repo.exactRef(changeMetaRef(Change.id(c._number))).getObjectId());
assertThat(commitPatchSetCreation.getShortMessage()).isEqualTo("Create patch set 2");
PersonIdent expectedAuthor =
@@ -2937,8 +3083,7 @@ public class ChangeIT extends AbstractDaemonTest {
in.project = project.get();
in.newBranch = true;
- exception.expect(ResourceConflictException.class);
- gApi.changes().create(in).get();
+ assertThrows(ResourceConflictException.class, () -> gApi.changes().create(in).get());
}
@Test
@@ -2951,7 +3096,11 @@ public class ChangeIT extends AbstractDaemonTest {
TestRepository<InMemoryRepository> userTestRepo = cloneProject(p, user);
// Block default permission
- block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(block(Permission.ADD_PATCH_SET).ref("refs/for/*").group(REGISTERED_USERS))
+ .update();
// Create change as admin
PushOneCommit push = pushFactory.create(admin.newIdent(), adminTestRepo);
@@ -2959,12 +3108,12 @@ public class ChangeIT extends AbstractDaemonTest {
r1.assertOkStatus();
// Fetch change
- GitUtil.fetch(userTestRepo, r1.getPatchSet().getRefName() + ":ps");
+ GitUtil.fetch(userTestRepo, r1.getPatchSet().refName() + ":ps");
userTestRepo.reset("ps");
// Amend change as user
PushOneCommit.Result r2 = amendChange(r1.getChangeId(), "refs/for/master", user, userTestRepo);
- r2.assertErrorStatus("cannot add patch set to " + r1.getChange().getId().id + ".");
+ r2.assertErrorStatus("cannot add patch set to " + r1.getChange().getId().get() + ".");
}
@Test
@@ -2979,7 +3128,7 @@ public class ChangeIT extends AbstractDaemonTest {
r1.assertOkStatus();
// Fetch change
- GitUtil.fetch(userTestRepo, r1.getPatchSet().getRefName() + ":ps");
+ GitUtil.fetch(userTestRepo, r1.getPatchSet().refName() + ":ps");
userTestRepo.reset("ps");
// Amend change as user
@@ -2995,7 +3144,11 @@ public class ChangeIT extends AbstractDaemonTest {
TestRepository<?> adminTestRepo = cloneProject(project, admin);
// Block default permission
- block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(block(Permission.ADD_PATCH_SET).ref("refs/for/*").group(REGISTERED_USERS))
+ .update();
// Create change as admin
PushOneCommit push = pushFactory.create(admin.newIdent(), adminTestRepo);
@@ -3003,7 +3156,7 @@ public class ChangeIT extends AbstractDaemonTest {
r1.assertOkStatus();
// Fetch change
- GitUtil.fetch(adminTestRepo, r1.getPatchSet().getRefName() + ":ps");
+ GitUtil.fetch(adminTestRepo, r1.getPatchSet().refName() + ":ps");
adminTestRepo.reset("ps");
// Amend change as admin
@@ -3014,20 +3167,19 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void createMergePatchSet() throws Exception {
- PushOneCommit.Result start = pushTo("refs/heads/master");
- start.assertOkStatus();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
+ createBranch("dev");
+
// create a change for master
- PushOneCommit.Result r = createChange();
- r.assertOkStatus();
- String changeId = r.getChangeId();
+ String changeId = createChange().getChangeId();
- testRepo.reset(start.getCommit());
+ testRepo.reset(initialHead);
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
currentMaster.assertOkStatus();
String parent = currentMaster.getCommit().getName();
// push a commit into dev branch
- createBranch("dev");
+ testRepo.reset(initialHead);
PushOneCommit.Result changeA =
pushFactory
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
@@ -3048,22 +3200,57 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void createMergePatchSet_Conflict() throws Exception {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
+ createBranch("dev");
+
+ // create a change for master
+ String changeId = createChange().getChangeId();
+
+ String fileName = "shared.txt";
+ testRepo.reset(initialHead);
+ PushOneCommit.Result currentMaster =
+ pushFactory
+ .create(admin.newIdent(), testRepo, "change 1", fileName, "content 1")
+ .to("refs/heads/master");
+ currentMaster.assertOkStatus();
+
+ // push a commit into dev branch
+ testRepo.reset(initialHead);
+ PushOneCommit.Result changeA =
+ pushFactory
+ .create(user.newIdent(), testRepo, "change 2", fileName, "content 2")
+ .to("refs/heads/dev");
+ changeA.assertOkStatus();
+ MergeInput mergeInput = new MergeInput();
+ mergeInput.source = "dev";
+ MergePatchSetInput in = new MergePatchSetInput();
+ in.merge = mergeInput;
+ in.subject = "update change by merge ps2";
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).createMergePatchSet(in));
+ assertThat(thrown).hasMessageThat().isEqualTo("merge conflict(s):\n" + fileName);
+ }
+
+ @Test
public void createMergePatchSetInheritParent() throws Exception {
- PushOneCommit.Result start = pushTo("refs/heads/master");
- start.assertOkStatus();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
+ createBranch("dev");
+
// create a change for master
PushOneCommit.Result r = createChange();
- r.assertOkStatus();
String changeId = r.getChangeId();
String parent = r.getCommit().getParent(0).getName();
// advance master branch
- testRepo.reset(start.getCommit());
+ testRepo.reset(initialHead);
PushOneCommit.Result currentMaster = pushTo("refs/heads/master");
currentMaster.assertOkStatus();
// push a commit into dev branch
- createBranch("dev");
+ testRepo.reset(initialHead);
PushOneCommit.Result changeA =
pushFactory
.create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
@@ -3089,7 +3276,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void createMergePatchSetCannotBaseOnInvisibleChange() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
createBranch("foo");
createBranch("bar");
@@ -3106,14 +3293,19 @@ public class ChangeIT extends AbstractDaemonTest {
testRepo.reset(initialHead);
String changeId = createChange().getChangeId();
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("Read not permitted for " + baseChange);
- gApi.changes().id(changeId).createMergePatchSet(createMergePatchSetInput(baseChange));
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () ->
+ gApi.changes()
+ .id(changeId)
+ .createMergePatchSet(createMergePatchSetInput(baseChange)));
+ assertThat(thrown).hasMessageThat().contains("Read not permitted for " + baseChange);
}
@Test
public void createMergePatchSetBaseOnChange() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
createBranch("foo");
createBranch("bar");
@@ -3140,6 +3332,46 @@ public class ChangeIT extends AbstractDaemonTest {
.isEqualTo(expectedParent);
}
+ @Test
+ public void createMergePatchSetWithUnupportedMergeStrategy() throws Exception {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
+ createBranch("dev");
+
+ // create a change for master
+ String changeId = createChange().getChangeId();
+
+ String fileName = "shared.txt";
+ String sourceSubject = "source change";
+ String sourceContent = "source content";
+ String targetSubject = "target change";
+ String targetContent = "target content";
+ testRepo.reset(initialHead);
+ PushOneCommit.Result currentMaster =
+ pushFactory
+ .create(admin.newIdent(), testRepo, targetSubject, fileName, targetContent)
+ .to("refs/heads/master");
+ currentMaster.assertOkStatus();
+
+ // push a commit into dev branch
+ testRepo.reset(initialHead);
+ PushOneCommit.Result changeA =
+ pushFactory
+ .create(user.newIdent(), testRepo, sourceSubject, fileName, sourceContent)
+ .to("refs/heads/dev");
+ changeA.assertOkStatus();
+ MergeInput mergeInput = new MergeInput();
+ mergeInput.source = "dev";
+ mergeInput.strategy = "unsupported-strategy";
+ MergePatchSetInput in = new MergePatchSetInput();
+ in.merge = mergeInput;
+ in.subject = "update change by merge ps2";
+
+ BadRequestException ex =
+ assertThrows(
+ BadRequestException.class, () -> gApi.changes().id(changeId).createMergePatchSet(in));
+ assertThat(ex).hasMessageThat().isEqualTo("invalid merge strategy: " + mergeInput.strategy);
+ }
+
private MergePatchSetInput createMergePatchSetInput(String baseChange) {
MergeInput mergeInput = new MergeInput();
mergeInput.source = "foo";
@@ -3161,15 +3393,18 @@ public class ChangeIT extends AbstractDaemonTest {
// add new label and assert that it's returned for existing changes
AccountGroup.UUID registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
- LabelType verified = Util.verified();
+ LabelType verified = TestLabels.verified();
String heads = RefNames.REFS_HEADS + "*";
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getLabelSections().put(verified.getName(), verified);
- Util.allow(
- u.getConfig(), Permission.forLabel(verified.getName()), -1, 1, registeredUsers, heads);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(verified.getName()).ref(heads).group(registeredUsers).range(-1, 1))
+ .update();
change = gApi.changes().id(r.getChangeId()).get();
assertThat(change.labels.keySet()).containsExactly("Code-Review", "Verified");
@@ -3187,9 +3422,16 @@ public class ChangeIT extends AbstractDaemonTest {
// remove label and assert that it's no longer returned for existing
// changes, even if there is an approval for it
u.getConfig().getLabelSections().remove(verified.getName());
- Util.remove(u.getConfig(), Permission.forLabel(verified.getName()), registeredUsers, heads);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(
+ permissionKey(Permission.forLabel(verified.getName()))
+ .ref(heads)
+ .group(registeredUsers))
+ .update();
change = gApi.changes().id(r.getChangeId()).get();
assertThat(change.labels.keySet()).containsExactly("Code-Review");
@@ -3216,17 +3458,20 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(change.permittedLabels.keySet()).containsExactly("Code-Review");
assertPermitted(change, "Code-Review", 2);
- LabelType verified = Util.verified();
+ LabelType verified = TestLabels.verified();
AccountGroup.UUID registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
String heads = RefNames.REFS_HEADS + "*";
// add new label and assert that it's returned for existing changes
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getLabelSections().put(verified.getName(), verified);
- Util.allow(
- u.getConfig(), Permission.forLabel(verified.getName()), -1, 1, registeredUsers, heads);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(verified.getName()).ref(heads).group(registeredUsers).range(-1, 1))
+ .update();
change = gApi.changes().id(r.getChangeId()).get();
assertThat(change.labels.keySet()).containsExactly("Code-Review", "Verified");
@@ -3268,9 +3513,13 @@ public class ChangeIT extends AbstractDaemonTest {
// changes, even if there is an approval for it
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getLabelSections().remove(verified.getName());
- Util.remove(u.getConfig(), Permission.forLabel(verified.getName()), registeredUsers, heads);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(permissionKey(verified.getName()).ref(heads).group(registeredUsers))
+ .update();
change = gApi.changes().id(r.getChangeId()).get();
assertThat(change.labels.keySet()).containsExactly("Code-Review");
@@ -3279,9 +3528,45 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void notifyConfigForDirectoryTriggersEmail() throws Exception {
+ // Configure notifications on project level.
+ RevCommit oldHead = projectOperations.project(project).getHead("master");
+ GitUtil.fetch(testRepo, RefNames.REFS_CONFIG + ":config");
+ testRepo.reset("config");
+ PushOneCommit push =
+ pushFactory.create(
+ admin.newIdent(),
+ testRepo,
+ "Configure Notifications",
+ "project.config",
+ "[notify \"my=notify-config\"]\n"
+ + " email = foo@test.com\n"
+ + " filter = dir:\\\"foo/bar/baz\\\"");
+ push.to(RefNames.REFS_CONFIG);
+ testRepo.reset(oldHead);
+
+ // Push a change that matches the filter.
+ sender.clear();
+ push =
+ pushFactory.create(
+ admin.newIdent(), testRepo, "Test change", "foo/bar/baz/test.txt", "some content");
+ PushOneCommit.Result r = push.to("refs/for/master");
+ assertThat(sender.getMessages()).hasSize(1);
+ assertThat(sender.getMessages().get(0).rcpt()).containsExactly(Address.parse("foo@test.com"));
+
+ // Comment on the change.
+ sender.clear();
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.message = "some message";
+ gApi.changes().id(r.getChangeId()).current().review(reviewInput);
+ assertThat(sender.getMessages()).hasSize(1);
+ assertThat(sender.getMessages().get(0).rcpt()).containsExactly(Address.parse("foo@test.com"));
+ }
+
+ @Test
public void checkLabelsForMergedChangeWithNonAuthorCodeReview() throws Exception {
// Configure Non-Author-Code-Review
- RevCommit oldHead = getRemoteHead();
+ RevCommit oldHead = projectOperations.project(project).getHead("master");
GitUtil.fetch(testRepo, RefNames.REFS_CONFIG + ":config");
testRepo.reset("config");
PushOneCommit push2 =
@@ -3306,20 +3591,18 @@ public class ChangeIT extends AbstractDaemonTest {
push2.to(RefNames.REFS_CONFIG);
testRepo.reset(oldHead);
- AccountGroup.UUID registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
String heads = RefNames.REFS_HEADS + "*";
// Allow user to approve
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(
- u.getConfig(),
- Permission.forLabel(Util.codeReview().getName()),
- -2,
- 2,
- registeredUsers,
- heads);
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel(TestLabels.codeReview().getName())
+ .ref(heads)
+ .group(REGISTERED_USERS)
+ .range(-2, 2))
+ .update();
PushOneCommit.Result r = createChange();
@@ -3371,16 +3654,15 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(approval.permittedVotingRange.min).isEqualTo(-1);
assertThat(approval.permittedVotingRange.max).isEqualTo(1);
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(
- u.getConfig(),
- Permission.forLabel("Code-Review"),
- minPermittedValue,
- maxPermittedValue,
- REGISTERED_USERS,
- heads);
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel("Code-Review")
+ .ref(heads)
+ .group(REGISTERED_USERS)
+ .range(minPermittedValue, maxPermittedValue))
+ .update();
c = gApi.changes().id(triplet).get(DETAILED_LABELS);
codeReview = c.labels.get("Code-Review");
@@ -3394,10 +3676,11 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void maxPermittedValueBlocked() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.blockLabel(u.getConfig(), "Code-Review", REGISTERED_USERS, "refs/heads/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-1, 1))
+ .update();
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
@@ -3434,9 +3717,9 @@ public class ChangeIT extends AbstractDaemonTest {
ReviewInput input = new ReviewInput().label("Code-Review", 3);
gApi.changes().id(changeId).current().review(input);
- Map<String, Short> votes =
- gApi.changes().id(changeId).current().reviewer(admin.email()).votes();
- assertThat(votes).isEmpty();
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(changeId).current().reviewer(admin.email()));
}
@Test
@@ -3445,9 +3728,10 @@ public class ChangeIT extends AbstractDaemonTest {
String changeId = createChange().getChangeId();
ReviewInput in = new ReviewInput().label("Code-Style", 1);
- exception.expect(BadRequestException.class);
- exception.expectMessage("label \"Code-Style\" is not a configured label");
- gApi.changes().id(changeId).current().review(in);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> gApi.changes().id(changeId).current().review(in));
+ assertThat(thrown).hasMessageThat().contains("label \"Code-Style\" is not a configured label");
}
@Test
@@ -3456,9 +3740,10 @@ public class ChangeIT extends AbstractDaemonTest {
String changeId = createChange().getChangeId();
ReviewInput in = new ReviewInput().label("Code-Review", 3);
- exception.expect(BadRequestException.class);
- exception.expectMessage("label \"Code-Review\": 3 is not a valid value");
- gApi.changes().id(changeId).current().review(in);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> gApi.changes().id(changeId).current().review(in));
+ assertThat(thrown).hasMessageThat().contains("label \"Code-Review\": 3 is not a valid value");
}
@Test
@@ -3474,7 +3759,7 @@ public class ChangeIT extends AbstractDaemonTest {
+ "U > 0,"
+ "R = label('All-Comments-Resolved', need(_)). \n\n");
- String oldHead = getRemoteHead().name();
+ String oldHead = projectOperations.project(project).getHead("master").name();
PushOneCommit.Result result1 =
pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
testRepo.reset(oldHead);
@@ -3486,56 +3771,14 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(result1.getChangeId()).current().submit();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Failed to submit 1 change due to the following problems");
- exception.expectMessage("needs All-Comments-Resolved");
- gApi.changes().id(result2.getChangeId()).current().submit();
- }
-
- @Test
- public void pureRevertFactBlocksSubmissionOfNonReverts() throws Exception {
- addPureRevertSubmitRule();
-
- // Create a change that is not a revert of another change
- PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
- approve(r1.getChangeId());
-
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Failed to submit 1 change due to the following problems");
- exception.expectMessage("needs Is-Pure-Revert");
- gApi.changes().id(r1.getChangeId()).current().submit();
- }
-
- @Test
- public void pureRevertFactBlocksSubmissionOfNonPureReverts() throws Exception {
- PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
- merge(r1);
-
- addPureRevertSubmitRule();
-
- // Create a revert and push a content change
- String revertId = gApi.changes().id(r1.getChangeId()).revert().get().changeId;
- amendChange(revertId);
- approve(revertId);
-
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Failed to submit 1 change due to the following problems");
- exception.expectMessage("needs Is-Pure-Revert");
- gApi.changes().id(revertId).current().submit();
- }
-
- @Test
- public void pureRevertFactAllowsSubmissionOfPureReverts() throws Exception {
- // Create a change that we can later revert
- PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
- merge(r1);
-
- addPureRevertSubmitRule();
-
- // Create a revert and submit it
- String revertId = gApi.changes().id(r1.getChangeId()).revert().get().changeId;
- approve(revertId);
- gApi.changes().id(revertId).current().submit();
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(result2.getChangeId()).current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Failed to submit 1 change due to the following problems");
+ assertThat(thrown).hasMessageThat().contains("needs All-Comments-Resolved");
}
@Test
@@ -3595,13 +3838,13 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
- public void changeCommitMessageWithNoChangeIdFails() throws Exception {
+ public void changeCommitMessageWithNoChangeIdRetainsChangeID() throws Exception {
PushOneCommit.Result r = createChange();
assertThat(getCommitMessage(r.getChangeId()))
.isEqualTo("test commit\n\nChange-Id: " + r.getChangeId() + "\n");
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("missing Change-Id footer");
gApi.changes().id(r.getChangeId()).setMessage("modified commit\n");
+ assertThat(getCommitMessage(r.getChangeId()))
+ .isEqualTo("modified commit\n\nChange-Id: " + r.getChangeId() + "\n");
}
@Test
@@ -3609,11 +3852,14 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
assertThat(getCommitMessage(r.getChangeId()))
.isEqualTo("test commit\n\nChange-Id: " + r.getChangeId() + "\n");
- exception.expect(BadRequestException.class);
- exception.expectMessage("NUL character");
- gApi.changes()
- .id(r.getChangeId())
- .setMessage("test\0commit\n\nChange-Id: " + r.getChangeId() + "\n");
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.changes()
+ .id(r.getChangeId())
+ .setMessage("test\0commit\n\nChange-Id: " + r.getChangeId() + "\n"));
+ assertThat(thrown).hasMessageThat().contains("NUL character");
}
@Test
@@ -3622,11 +3868,15 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
assertThat(getCommitMessage(r.getChangeId()))
.isEqualTo("test commit\n\nChange-Id: " + r.getChangeId() + "\n");
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("wrong Change-Id footer");
- gApi.changes()
- .id(r.getChangeId())
- .setMessage("modified commit\n\nChange-Id: " + otherChange.getChangeId() + "\n");
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () ->
+ gApi.changes()
+ .id(r.getChangeId())
+ .setMessage(
+ "modified commit\n\nChange-Id: " + otherChange.getChangeId() + "\n"));
+ assertThat(thrown).hasMessageThat().contains("wrong Change-Id footer");
}
@Test
@@ -3635,15 +3885,20 @@ public class ChangeIT extends AbstractDaemonTest {
Project.NameKey p = projectOperations.newProject().create();
TestRepository<InMemoryRepository> userTestRepo = cloneProject(p, user);
// Block default permission
- block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(block(Permission.ADD_PATCH_SET).ref("refs/for/*").group(REGISTERED_USERS))
+ .update();
// Create change as user
PushOneCommit push = pushFactory.create(user.newIdent(), userTestRepo);
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
// Try to change the commit message
- exception.expect(AuthException.class);
- exception.expectMessage("modifying commit message not permitted");
- gApi.changes().id(r.getChangeId()).setMessage("foo");
+ AuthException thrown =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(r.getChangeId()).setMessage("foo"));
+ assertThat(thrown).hasMessageThat().contains("modifying commit message not permitted");
}
@Test
@@ -3651,9 +3906,11 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
assertThat(getCommitMessage(r.getChangeId()))
.isEqualTo("test commit\n\nChange-Id: " + r.getChangeId() + "\n");
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("new and existing commit message are the same");
- gApi.changes().id(r.getChangeId()).setMessage(getCommitMessage(r.getChangeId()));
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r.getChangeId()).setMessage(getCommitMessage(r.getChangeId())));
+ assertThat(thrown).hasMessageThat().contains("new and existing commit message are the same");
}
@Test
@@ -3693,114 +3950,13 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
- public void pureRevertReturnsTrueForPureRevert() throws Exception {
- PushOneCommit.Result r = createChange();
- merge(r);
- String revertId = gApi.changes().id(r.getChangeId()).revert().get().id;
- // Without query parameter
- assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isTrue();
- // With query parameter
- assertThat(
- gApi.changes()
- .id(revertId)
- .pureRevert(getRemoteHead().toObjectId().name())
- .isPureRevert)
- .isTrue();
- }
-
- @Test
- public void pureRevertReturnsFalseOnContentChange() throws Exception {
- PushOneCommit.Result r1 = createChange();
- merge(r1);
- // Create a revert and expect pureRevert to be true
- String revertId = gApi.changes().id(r1.getChangeId()).revert().get().changeId;
- assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isTrue();
-
- // Create a new PS and expect pureRevert to be false
- PushOneCommit.Result result = amendChange(revertId);
- result.assertOkStatus();
- assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isFalse();
- }
-
- @Test
- public void pureRevertParameterTakesPrecedence() throws Exception {
- PushOneCommit.Result r1 = createChange("commit message", "a.txt", "content1");
- merge(r1);
- String oldHead = getRemoteHead().toObjectId().name();
-
- PushOneCommit.Result r2 = createChange("commit message", "a.txt", "content2");
- merge(r2);
-
- String revertId = gApi.changes().id(r2.getChangeId()).revert().get().changeId;
- assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isTrue();
- assertThat(gApi.changes().id(revertId).pureRevert(oldHead).isPureRevert).isFalse();
- }
-
- @Test
- public void pureRevertReturnsFalseOnInvalidInput() throws Exception {
- PushOneCommit.Result r1 = createChange();
- merge(r1);
-
- exception.expect(BadRequestException.class);
- exception.expectMessage("invalid object ID");
- gApi.changes().id(createChange().getChangeId()).pureRevert("invalid id");
- }
-
- @Test
- public void pureRevertReturnsTrueWithCleanRebase() throws Exception {
- PushOneCommit.Result r1 = createChange("commit message", "a.txt", "content1");
- merge(r1);
-
- PushOneCommit.Result r2 = createChange("commit message", "b.txt", "content2");
- merge(r2);
-
- String revertId = gApi.changes().id(r1.getChangeId()).revert().get().changeId;
- // Rebase revert onto HEAD
- gApi.changes().id(revertId).rebase();
- // Check that pureRevert is true which implies that the commit can be rebased onto the original
- // commit.
- assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isTrue();
- }
-
- @Test
- public void pureRevertReturnsFalseWithRebaseConflict() throws Exception {
- // Create an initial commit to serve as claimed original
- PushOneCommit.Result r1 = createChange("commit message", "a.txt", "content1");
- merge(r1);
- String claimedOriginal = getRemoteHead().toObjectId().name();
-
- // Change contents of the file to provoke a conflict
- merge(createChange("commit message", "a.txt", "content2"));
-
- // Create a commit that we can revert
- PushOneCommit.Result r2 = createChange("commit message", "a.txt", "content3");
- merge(r2);
-
- // Create a revert of r2
- String revertR3Id = gApi.changes().id(r2.getChangeId()).revert().id();
- // Assert that the change is a pure revert of it's 'revertOf'
- assertThat(gApi.changes().id(revertR3Id).pureRevert().isPureRevert).isTrue();
- // Assert that the change is not a pure revert of claimedOriginal because pureRevert is trying
- // to rebase this on claimed original, which fails.
- PureRevertInfo pureRevert = gApi.changes().id(revertR3Id).pureRevert(claimedOriginal);
- assertThat(pureRevert.isPureRevert).isFalse();
- }
-
- @Test
- public void pureRevertThrowsExceptionWhenChangeIsNotARevertAndNoIdProvided() throws Exception {
- exception.expect(BadRequestException.class);
- exception.expectMessage("revertOf not set");
- gApi.changes().id(createChange().getChangeId()).pureRevert();
- }
-
- @Test
public void putTopicExceedLimitFails() throws Exception {
String changeId = createChange().getChangeId();
String topic = Stream.generate(() -> "t").limit(2049).collect(joining());
- exception.expect(BadRequestException.class);
- exception.expectMessage("topic length exceeds the limit");
- gApi.changes().id(changeId).topic(topic);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.changes().id(changeId).topic(topic));
+ assertThat(thrown).hasMessageThat().contains("topic length exceeds the limit");
}
@Test
@@ -3817,13 +3973,13 @@ public class ChangeIT extends AbstractDaemonTest {
private void submittableAfterLosingPermissions(String label) throws Exception {
String codeReviewLabel = "Code-Review";
- AccountGroup.UUID registered = SystemGroupBackend.REGISTERED_USERS;
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(u.getConfig(), Permission.forLabel(label), -1, +1, registered, "refs/heads/*");
- Util.allow(
- u.getConfig(), Permission.forLabel(codeReviewLabel), -2, +2, registered, "refs/heads/*");
- u.save();
- }
+ AccountGroup.UUID registered = REGISTERED_USERS;
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(label).ref("refs/heads/*").group(registered).range(-1, +1))
+ .add(allowLabel(codeReviewLabel).ref("refs/heads/*").group(registered).range(-2, +2))
+ .update();
requestScopeOperations.setApiUser(user.id());
PushOneCommit.Result r = createChange();
@@ -3846,15 +4002,13 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(changeId).get().submittable).isTrue();
requestScopeOperations.setApiUser(admin.id());
- // Remove user's permission for 'Label'.
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.remove(u.getConfig(), Permission.forLabel(label), registered, "refs/heads/*");
- // Update user's permitted range for 'Code-Review' to be -1...+1.
- Util.remove(u.getConfig(), Permission.forLabel(codeReviewLabel), registered, "refs/heads/*");
- Util.allow(
- u.getConfig(), Permission.forLabel(codeReviewLabel), -1, +1, registered, "refs/heads/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(labelPermissionKey(label).ref("refs/heads/*").group(registered))
+ .remove(labelPermissionKey(codeReviewLabel).ref("refs/heads/*").group(registered))
+ .add(allowLabel(codeReviewLabel).ref("refs/heads/*").group(registered).range(-1, +1))
+ .update();
// Verify user's new permitted range.
requestScopeOperations.setApiUser(user.id());
@@ -3938,7 +4092,7 @@ public class ChangeIT extends AbstractDaemonTest {
if (r == null) {
return ImmutableList.of();
}
- return Iterables.transform(r, a -> new Account.Id(a._accountId));
+ return Iterables.transform(r, a -> Account.id(a._accountId));
}
private ChangeResource parseResource(PushOneCommit.Result r) throws Exception {
@@ -3953,7 +4107,7 @@ public class ChangeIT extends AbstractDaemonTest {
.filter(e -> e.getValue().stream().anyMatch(a -> a._accountId == accountId.get()))
.map(Map.Entry::getKey)
.collect(toSet());
- assertThat(states.size()).named(states.toString()).isAtMost(1);
+ assertWithMessage(states.toString()).that(states.size()).isAtMost(1);
return states.stream().findFirst();
}
@@ -3979,10 +4133,7 @@ public class ChangeIT extends AbstractDaemonTest {
public boolean updateChange(ChangeContext ctx) throws Exception {
Change change = ctx.getChange();
- // Change status in database.
- change.setStatus(newStatus);
-
- // Change status in NoteDb.
+ // Change status.
PatchSet.Id currentPatchSetId = change.currentPatchSetId();
ctx.getUpdate(currentPatchSetId).setStatus(newStatus);
@@ -3990,19 +4141,6 @@ public class ChangeIT extends AbstractDaemonTest {
}
}
- private void addPureRevertSubmitRule() throws Exception {
- modifySubmitRules(
- "submit_rule(submit(R)) :- \n"
- + "gerrit:pure_revert(1), \n"
- + "!,"
- + "gerrit:uploader(U), \n"
- + "R = label('Is-Pure-Revert', ok(U)).\n"
- + "submit_rule(submit(R)) :- \n"
- + "gerrit:pure_revert(U), \n"
- + "U \\= 1,"
- + "R = label('Is-Pure-Revert', need(_)). \n\n");
- }
-
private void modifySubmitRules(String newContent) throws Exception {
try (Repository repo = repoManager.openRepository(project);
TestRepository<Repository> testRepo = new TestRepository<>(repo)) {
@@ -4042,21 +4180,25 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void starUnstar() throws Exception {
- PushOneCommit.Result r = createChange();
- String triplet = project.get() + "~master~" + r.getChangeId();
- changeIndexedCounter.clear();
-
- gApi.accounts().self().starChange(triplet);
- ChangeInfo change = info(triplet);
- assertThat(change.starred).isTrue();
- assertThat(change.stars).contains(DEFAULT_LABEL);
- changeIndexedCounter.assertReindexOf(change);
-
- gApi.accounts().self().unstarChange(triplet);
- change = info(triplet);
- assertThat(change.starred).isNull();
- assertThat(change.stars).isNull();
- changeIndexedCounter.assertReindexOf(change);
+ ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedCounter)) {
+ PushOneCommit.Result r = createChange();
+ String triplet = project.get() + "~master~" + r.getChangeId();
+ changeIndexedCounter.clear();
+
+ gApi.accounts().self().starChange(triplet);
+ ChangeInfo change = info(triplet);
+ assertThat(change.starred).isTrue();
+ assertThat(change.stars).contains(DEFAULT_LABEL);
+ changeIndexedCounter.assertReindexOf(change);
+
+ gApi.accounts().self().unstarChange(triplet);
+ change = info(triplet);
+ assertThat(change.starred).isNull();
+ assertThat(change.stars).isNull();
+ changeIndexedCounter.assertReindexOf(change);
+ }
}
@Test
@@ -4116,9 +4258,9 @@ public class ChangeIT extends AbstractDaemonTest {
public void cannotIgnoreOwnChange() throws Exception {
String changeId = createChange().getChangeId();
- exception.expect(BadRequestException.class);
- exception.expectMessage("cannot ignore own change");
- gApi.changes().id(changeId).ignore(true);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.changes().id(changeId).ignore(true));
+ assertThat(thrown).hasMessageThat().contains("cannot ignore own change");
}
@Test
@@ -4129,14 +4271,17 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.accounts().self().starChange(changeId);
assertThat(gApi.changes().id(changeId).get().starred).isTrue();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- "The labels "
- + StarredChangesUtil.DEFAULT_LABEL
- + " and "
- + StarredChangesUtil.IGNORE_LABEL
- + " are mutually exclusive. Only one of them can be set.");
- gApi.changes().id(changeId).ignore(true);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.changes().id(changeId).ignore(true));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "The labels "
+ + StarredChangesUtil.DEFAULT_LABEL
+ + " and "
+ + StarredChangesUtil.IGNORE_LABEL
+ + " are mutually exclusive. Only one of them can be set.");
}
@Test
@@ -4147,14 +4292,17 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).ignore(true);
assertThat(gApi.changes().id(changeId).ignored()).isTrue();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- "The labels "
- + StarredChangesUtil.DEFAULT_LABEL
- + " and "
- + StarredChangesUtil.IGNORE_LABEL
- + " are mutually exclusive. Only one of them can be set.");
- gApi.accounts().self().starChange(changeId);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.accounts().self().starChange(changeId));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "The labels "
+ + StarredChangesUtil.DEFAULT_LABEL
+ + " and "
+ + StarredChangesUtil.IGNORE_LABEL
+ + " are mutually exclusive. Only one of them can be set.");
}
@Test
@@ -4192,21 +4340,28 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).markAsReviewed(true);
assertThat(gApi.changes().id(changeId).get().reviewed).isTrue();
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- "The labels "
- + StarredChangesUtil.REVIEWED_LABEL
- + "/"
- + 1
- + " and "
- + StarredChangesUtil.UNREVIEWED_LABEL
- + "/"
- + 1
- + " are mutually exclusive. Only one of them can be set.");
- gApi.accounts()
- .self()
- .setStars(
- changeId, new StarsInput(ImmutableSet.of(StarredChangesUtil.UNREVIEWED_LABEL + "/1")));
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.accounts()
+ .self()
+ .setStars(
+ changeId,
+ new StarsInput(
+ ImmutableSet.of(StarredChangesUtil.UNREVIEWED_LABEL + "/1"))));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "The labels "
+ + StarredChangesUtil.REVIEWED_LABEL
+ + "/"
+ + 1
+ + " and "
+ + StarredChangesUtil.UNREVIEWED_LABEL
+ + "/"
+ + 1
+ + " are mutually exclusive. Only one of them can be set.");
}
@Test
@@ -4217,21 +4372,27 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).markAsReviewed(false);
assertThat(gApi.changes().id(changeId).get().reviewed).isNull();
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- "The labels "
- + StarredChangesUtil.REVIEWED_LABEL
- + "/"
- + 1
- + " and "
- + StarredChangesUtil.UNREVIEWED_LABEL
- + "/"
- + 1
- + " are mutually exclusive. Only one of them can be set.");
- gApi.accounts()
- .self()
- .setStars(
- changeId, new StarsInput(ImmutableSet.of(StarredChangesUtil.REVIEWED_LABEL + "/1")));
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.accounts()
+ .self()
+ .setStars(
+ changeId,
+ new StarsInput(ImmutableSet.of(StarredChangesUtil.REVIEWED_LABEL + "/1"))));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "The labels "
+ + StarredChangesUtil.REVIEWED_LABEL
+ + "/"
+ + 1
+ + " and "
+ + StarredChangesUtil.UNREVIEWED_LABEL
+ + "/"
+ + 1
+ + " are mutually exclusive. Only one of them can be set.");
}
@Test
@@ -4260,9 +4421,14 @@ public class ChangeIT extends AbstractDaemonTest {
// label cannot contain whitespace
String invalidLabel = "invalid label";
- exception.expect(BadRequestException.class);
- exception.expectMessage("invalid labels: " + invalidLabel);
- gApi.accounts().self().setStars(changeId, new StarsInput(ImmutableSet.of(invalidLabel)));
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.accounts()
+ .self()
+ .setStars(changeId, new StarsInput(ImmutableSet.of(invalidLabel))));
+ assertThat(thrown).hasMessageThat().contains("invalid labels: " + invalidLabel);
}
@Test
@@ -4279,7 +4445,8 @@ public class ChangeIT extends AbstractDaemonTest {
ListChangesOption.MESSAGES,
ListChangesOption.SUBMITTABLE,
ListChangesOption.WEB_LINKS,
- ListChangesOption.SKIP_MERGEABLE);
+ ListChangesOption.SKIP_MERGEABLE,
+ ListChangesOption.SKIP_DIFFSTAT);
PushOneCommit.Result change = createChange();
int number = gApi.changes().id(change.getChangeId()).get()._number;
@@ -4295,7 +4462,7 @@ public class ChangeIT extends AbstractDaemonTest {
}
private BranchApi createBranch(String branch) throws Exception {
- return createBranch(new Branch.NameKey(project, branch));
+ return createBranch(BranchNameKey.create(project, branch));
}
private ThrowableSubject assertThatQueryException(String query) throws Exception {
@@ -4306,4 +4473,9 @@ public class ChangeIT extends AbstractDaemonTest {
}
throw new AssertionError("expected BadRequestException");
}
+
+ @FunctionalInterface
+ private interface AddReviewerCaller {
+ void call(String changeId, String reviewer) throws RestApiException;
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java
index 4551f900c7..de73c00f82 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java
@@ -15,15 +15,16 @@
package com.google.gerrit.acceptance.api.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import org.junit.Before;
import org.junit.Test;
@@ -54,16 +55,22 @@ public class ChangeIdIT extends AbstractDaemonTest {
@Test
public void wrongProjectInProjectChangeNumberReturnsNotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Not found: unknown~" + changeInfo._number);
- gApi.changes().id("unknown", changeInfo._number);
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id("unknown", changeInfo._number));
+ assertThat(thrown).hasMessageThat().contains("Not found: unknown~" + changeInfo._number);
}
@Test
public void wrongIdInProjectChangeNumberReturnsNotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Not found: " + project.get() + "~" + Integer.MAX_VALUE);
- gApi.changes().id(project.get(), Integer.MAX_VALUE);
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(project.get(), Integer.MAX_VALUE));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Not found: " + project.get() + "~" + Integer.MAX_VALUE);
}
@Test
@@ -74,8 +81,7 @@ public class ChangeIdIT extends AbstractDaemonTest {
@Test
public void wrongChangeNumberReturnsNotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.changes().id(Integer.MAX_VALUE);
+ assertThrows(ResourceNotFoundException.class, () -> gApi.changes().id(Integer.MAX_VALUE));
}
@Test
@@ -86,25 +92,36 @@ public class ChangeIdIT extends AbstractDaemonTest {
@Test
public void wrongProjectInTripletChangeIdReturnsNotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Not found: unknown~" + changeInfo.branch + "~" + changeInfo.changeId);
- gApi.changes().id("unknown", changeInfo.branch, changeInfo.changeId);
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id("unknown", changeInfo.branch, changeInfo.changeId));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Not found: unknown~" + changeInfo.branch + "~" + changeInfo.changeId);
}
@Test
public void wrongBranchInTripletChangeIdReturnsNotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Not found: " + project.get() + "~unknown~" + changeInfo.changeId);
- gApi.changes().id(project.get(), "unknown", changeInfo.changeId);
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(project.get(), "unknown", changeInfo.changeId));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Not found: " + project.get() + "~unknown~" + changeInfo.changeId);
}
@Test
public void wrongIdInTripletChangeIdReturnsNotFound() throws Exception {
String unknownId = "I1234567890";
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage(
- "Not found: " + project.get() + "~" + changeInfo.branch + "~" + unknownId);
- gApi.changes().id(project.get(), changeInfo.branch, unknownId);
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(project.get(), changeInfo.branch, unknownId));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Not found: " + project.get() + "~" + changeInfo.branch + "~" + unknownId);
}
@Test
@@ -119,7 +136,6 @@ public class ChangeIdIT extends AbstractDaemonTest {
@Test
public void wrongChangeIdReturnsNotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.changes().id("I1234567890");
+ assertThrows(ResourceNotFoundException.class, () -> gApi.changes().id("I1234567890"));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
index 48c46d2976..a704f0cd09 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
@@ -26,15 +26,14 @@ import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.SubmitRequirementInfo;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.rules.SubmitRule;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
@@ -109,15 +108,15 @@ public class ChangeSubmitRequirementIT extends AbstractDaemonTest {
}
@Override
- public Collection<SubmitRecord> evaluate(ChangeData changeData, SubmitRuleOptions options) {
+ public Optional<SubmitRecord> evaluate(ChangeData changeData) {
if (block.get()) {
SubmitRecord record = new SubmitRecord();
record.labels = new ArrayList<>();
record.status = SubmitRecord.Status.NOT_READY;
record.requirements = ImmutableList.of(req);
- return ImmutableList.of(record);
+ return Optional.of(record);
}
- return ImmutableList.of();
+ return Optional.empty();
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java b/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
index ae88afdab2..42d62bd12f 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
@@ -15,25 +15,24 @@
package com.google.gerrit.acceptance.api.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
-import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class DisablePrivateChangesIT extends AbstractDaemonTest {
-
@Test
@GerritConfig(name = "change.disablePrivateChanges", value = "true")
public void createPrivateChangeWithDisablePrivateChangesTrue() throws Exception {
ChangeInput input = new ChangeInput(project.get(), "master", "empty change");
input.isPrivate = true;
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("private changes are disabled");
- gApi.changes().create(input);
+ MethodNotAllowedException thrown =
+ assertThrows(MethodNotAllowedException.class, () -> gApi.changes().create(input));
+ assertThat(thrown).hasMessageThat().contains("private changes are disabled");
}
@Test
@@ -59,20 +58,6 @@ public class DisablePrivateChangesIT extends AbstractDaemonTest {
}
@Test
- @GerritConfig(name = "change.allowDrafts", value = "true")
- @GerritConfig(name = "change.disablePrivateChanges", value = "true")
- public void pushDraftsWithDisablePrivateChangesTrue() throws Exception {
- RevCommit initialHead = getRemoteHead();
- PushOneCommit.Result result =
- pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
- result.assertErrorStatus();
-
- testRepo.reset(initialHead);
- result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
- result.assertErrorStatus();
- }
-
- @Test
@GerritConfig(name = "change.disablePrivateChanges", value = "true")
public void pushWithDisablePrivateChangesTrue() throws Exception {
PushOneCommit.Result result =
@@ -82,34 +67,15 @@ public class DisablePrivateChangesIT extends AbstractDaemonTest {
}
@Test
- @GerritConfig(name = "change.allowDrafts", value = "true")
- public void pushPrivatesWithDisablePrivateChangesFalse() throws Exception {
- PushOneCommit.Result result =
- pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%private");
- assertThat(result.getChange().change().isPrivate()).isTrue();
- }
-
- @Test
- @GerritConfig(name = "change.allowDrafts", value = "true")
- public void pushDraftsWithDisablePrivateChangesFalse() throws Exception {
- RevCommit initialHead = getRemoteHead();
- PushOneCommit.Result result =
- pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
- assertThat(result.getChange().change().isPrivate()).isTrue();
-
- testRepo.reset(initialHead);
- result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
- assertThat(result.getChange().change().isPrivate()).isTrue();
- }
-
- @Test
@GerritConfig(name = "change.disablePrivateChanges", value = "true")
public void setPrivateWithDisablePrivateChangesTrue() throws Exception {
PushOneCommit.Result result = createChange();
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("private changes are disabled");
- gApi.changes().id(result.getChangeId()).setPrivate(true, "set private");
+ MethodNotAllowedException thrown =
+ assertThrows(
+ MethodNotAllowedException.class,
+ () -> gApi.changes().id(result.getChangeId()).setPrivate(true, "set private"));
+ assertThat(thrown).hasMessageThat().contains("private changes are disabled");
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java b/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
index a08d417455..7354ca412d 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
@@ -15,7 +15,9 @@
package com.google.gerrit.acceptance.api.change;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
+import static com.google.gerrit.entities.Patch.MERGE_LIST;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.HEAD;
@@ -152,18 +154,26 @@ public class MergeListIT extends AbstractDaemonTest {
public void editMergeList() throws Exception {
gApi.changes().id(changeId).edit().create();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Invalid path: " + MERGE_LIST);
- gApi.changes().id(changeId).edit().modifyFile(MERGE_LIST, RawInputUtil.create("new content"));
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () ->
+ gApi.changes()
+ .id(changeId)
+ .edit()
+ .modifyFile(MERGE_LIST, RawInputUtil.create("new content")));
+ assertThat(thrown).hasMessageThat().contains("Invalid path: " + MERGE_LIST);
}
@Test
public void deleteMergeList() throws Exception {
gApi.changes().id(changeId).edit().create();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("no changes were made");
- gApi.changes().id(changeId).edit().deleteFile(MERGE_LIST);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).edit().deleteFile(MERGE_LIST));
+ assertThat(thrown).hasMessageThat().contains("no changes were made");
}
private String getMergeListContent(RevCommit... commits) {
@@ -171,7 +181,7 @@ public class MergeListIT extends AbstractDaemonTest {
for (RevCommit c : commits) {
mergeList
.append("* ")
- .append(c.abbreviate(8).name())
+ .append(abbreviateName(c, 8))
.append(" ")
.append(c.getShortMessage())
.append("\n");
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
new file mode 100644
index 0000000000..7156c8d1b8
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
@@ -0,0 +1,290 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.api.change;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.api.changes.DraftInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
+import com.google.gerrit.extensions.client.Side;
+import com.google.gerrit.extensions.common.ChangeMessageInfo;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.validators.CommentForValidation;
+import com.google.gerrit.extensions.validators.CommentForValidation.CommentType;
+import com.google.gerrit.extensions.validators.CommentValidator;
+import com.google.gerrit.server.restapi.change.PostReview;
+import com.google.gerrit.server.update.CommentsRejectedException;
+import com.google.gerrit.testing.TestCommentHelper;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import java.sql.Timestamp;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+
+/** Tests for comment validation in {@link PostReview}. */
+public class PostReviewIT extends AbstractDaemonTest {
+ @Inject private CommentValidator mockCommentValidator;
+ @Inject private TestCommentHelper testCommentHelper;
+
+ private static final String COMMENT_TEXT = "The comment text";
+
+ @Captor private ArgumentCaptor<ImmutableList<CommentForValidation>> capture;
+
+ @Override
+ public Module createModule() {
+ return new FactoryModule() {
+ @Override
+ public void configure() {
+ CommentValidator mockCommentValidator = mock(CommentValidator.class);
+ bind(CommentValidator.class)
+ .annotatedWith(Exports.named(mockCommentValidator.getClass()))
+ .toInstance(mockCommentValidator);
+ bind(CommentValidator.class).toInstance(mockCommentValidator);
+ }
+ };
+ }
+
+ @Before
+ public void resetMock() {
+ initMocks(this);
+ clearInvocations(mockCommentValidator);
+ }
+
+ @Test
+ public void validateCommentsInInput_commentOK() throws Exception {
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(
+ CommentForValidation.create(
+ CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of());
+
+ PushOneCommit.Result r = createChange();
+
+ ReviewInput input = new ReviewInput();
+ CommentInput comment = newComment(r.getChange().currentFilePaths().get(0));
+ comment.updated = new Timestamp(0);
+ input.comments = ImmutableMap.of(comment.path, ImmutableList.of(comment));
+
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+ gApi.changes().id(r.getChangeId()).current().review(input);
+
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).hasSize(1);
+ }
+
+ @Test
+ public void validateCommentsInInput_commentRejected() throws Exception {
+ CommentForValidation commentForValidation =
+ CommentForValidation.create(CommentType.FILE_COMMENT, COMMENT_TEXT);
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(CommentForValidation.create(CommentType.FILE_COMMENT, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!")));
+
+ PushOneCommit.Result r = createChange();
+
+ ReviewInput input = new ReviewInput();
+ CommentInput comment = newComment(r.getChange().currentFilePaths().get(0));
+ comment.updated = new Timestamp(0);
+ input.comments = ImmutableMap.of(comment.path, ImmutableList.of(comment));
+
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+ BadRequestException badRequestException =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(r.getChangeId()).current().review(input));
+ assertThat(badRequestException.getCause()).isInstanceOf(CommentsRejectedException.class);
+ assertThat(
+ Iterables.getOnlyElement(
+ ((CommentsRejectedException) badRequestException.getCause())
+ .getCommentValidationFailures())
+ .getComment()
+ .getText())
+ .isEqualTo(COMMENT_TEXT);
+ assertThat(badRequestException.getCause()).hasMessageThat().contains("Oh no!");
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+ }
+
+ @Test
+ public void validateCommentsInInput_commentCleanedUp() throws Exception {
+ PushOneCommit.Result r = createChange();
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+
+ // posting a comment which is empty after trim is a no-op, as the empty comment is dropped
+ // during comment cleanup
+ ReviewInput input = new ReviewInput();
+ CommentInput comment =
+ TestCommentHelper.populate(
+ new CommentInput(), r.getChange().currentFilePaths().get(0), " ");
+ comment.updated = new Timestamp(0);
+ input.comments = ImmutableMap.of(comment.path, ImmutableList.of(comment));
+ gApi.changes().id(r.getChangeId()).current().review(input);
+
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+ }
+
+ @Test
+ public void validateDrafts_draftOK() throws Exception {
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(
+ CommentForValidation.create(
+ CommentForValidation.CommentType.INLINE_COMMENT, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of());
+
+ PushOneCommit.Result r = createChange();
+
+ DraftInput draft =
+ testCommentHelper.newDraft(
+ r.getChange().currentFilePaths().get(0), Side.REVISION, 1, COMMENT_TEXT);
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().getName()).createDraft(draft).get();
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+
+ ReviewInput input = new ReviewInput();
+ input.drafts = DraftHandling.PUBLISH;
+
+ gApi.changes().id(r.getChangeId()).current().review(input);
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).hasSize(1);
+ }
+
+ @Test
+ public void validateDrafts_draftRejected() throws Exception {
+ CommentForValidation commentForValidation =
+ CommentForValidation.create(CommentType.INLINE_COMMENT, COMMENT_TEXT);
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(
+ CommentForValidation.create(
+ CommentForValidation.CommentType.INLINE_COMMENT, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!")));
+ PushOneCommit.Result r = createChange();
+
+ DraftInput draft =
+ testCommentHelper.newDraft(
+ r.getChange().currentFilePaths().get(0), Side.REVISION, 1, COMMENT_TEXT);
+ testCommentHelper.addDraft(r.getChangeId(), r.getCommit().getName(), draft);
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+
+ ReviewInput input = new ReviewInput();
+ input.drafts = DraftHandling.PUBLISH;
+ BadRequestException badRequestException =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(r.getChangeId()).current().review(input));
+ assertThat(badRequestException.getCause()).isInstanceOf(CommentsRejectedException.class);
+ assertThat(
+ Iterables.getOnlyElement(
+ ((CommentsRejectedException) badRequestException.getCause())
+ .getCommentValidationFailures())
+ .getComment()
+ .getText())
+ .isEqualTo(draft.message);
+ assertThat(badRequestException.getCause()).hasMessageThat().contains("Oh no!");
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+ }
+
+ @Test
+ public void validateDrafts_inlineVsFileComments_allOK() throws Exception {
+ PushOneCommit.Result r = createChange();
+ DraftInput draftInline =
+ testCommentHelper.newDraft(
+ r.getChange().currentFilePaths().get(0), Side.REVISION, 1, COMMENT_TEXT);
+ testCommentHelper.addDraft(r.getChangeId(), r.getCommit().getName(), draftInline);
+ DraftInput draftFile = testCommentHelper.newDraft(COMMENT_TEXT);
+ testCommentHelper.addDraft(r.getChangeId(), r.getCommit().getName(), draftFile);
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty();
+
+ when(mockCommentValidator.validateComments(capture.capture())).thenReturn(ImmutableList.of());
+
+ ReviewInput input = new ReviewInput();
+ input.drafts = DraftHandling.PUBLISH;
+ gApi.changes().id(r.getChangeId()).current().review(input);
+ assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).hasSize(2);
+
+ assertThat(capture.getAllValues()).hasSize(1);
+ assertThat(capture.getValue())
+ .containsExactly(
+ CommentForValidation.create(
+ CommentForValidation.CommentType.INLINE_COMMENT, draftInline.message),
+ CommentForValidation.create(
+ CommentForValidation.CommentType.FILE_COMMENT, draftFile.message));
+ }
+
+ @Test
+ public void validateCommentsInChangeMessage_messageOK() throws Exception {
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(
+ CommentForValidation.create(CommentType.CHANGE_MESSAGE, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of());
+ PushOneCommit.Result r = createChange();
+
+ ReviewInput input = new ReviewInput().message(COMMENT_TEXT);
+ int numMessages = gApi.changes().id(r.getChangeId()).get().messages.size();
+ gApi.changes().id(r.getChangeId()).current().review(input);
+ assertThat(gApi.changes().id(r.getChangeId()).get().messages).hasSize(numMessages + 1);
+ ChangeMessageInfo message =
+ Iterables.getLast(gApi.changes().id(r.getChangeId()).get().messages);
+ assertThat(message.message).contains(COMMENT_TEXT);
+ }
+
+ @Test
+ public void validateCommentsInChangeMessage_messageRejected() throws Exception {
+ CommentForValidation commentForValidation =
+ CommentForValidation.create(CommentType.CHANGE_MESSAGE, COMMENT_TEXT);
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(
+ CommentForValidation.create(CommentType.CHANGE_MESSAGE, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!")));
+ PushOneCommit.Result r = createChange();
+
+ ReviewInput input = new ReviewInput().message(COMMENT_TEXT);
+ assertThat(gApi.changes().id(r.getChangeId()).get().messages)
+ .hasSize(1); // From the initial commit.
+ BadRequestException badRequestException =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(r.getChangeId()).current().review(input));
+ assertThat(badRequestException.getCause()).isInstanceOf(CommentsRejectedException.class);
+ assertThat(
+ Iterables.getOnlyElement(
+ ((CommentsRejectedException) badRequestException.getCause())
+ .getCommentValidationFailures())
+ .getComment()
+ .getText())
+ .isEqualTo(COMMENT_TEXT);
+ assertThat(badRequestException.getCause()).hasMessageThat().contains("Oh no!");
+ assertThat(gApi.changes().id(r.getChangeId()).get().messages)
+ .hasSize(1); // Unchanged from before.
+ ChangeMessageInfo message =
+ Iterables.getLast(gApi.changes().id(r.getChangeId()).get().messages);
+ assertThat(message.message).doesNotContain(COMMENT_TEXT);
+ }
+
+ private static CommentInput newComment(String path) {
+ return TestCommentHelper.populate(new CommentInput(), path, PostReviewIT.COMMENT_TEXT);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
index bfcb1a8358..f043c9b3c9 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
@@ -15,19 +15,22 @@
package com.google.gerrit.acceptance.api.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.update.BatchUpdate;
@@ -41,6 +44,7 @@ import org.junit.Test;
public class PrivateChangeIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Test
@@ -89,9 +93,9 @@ public class PrivateChangeIT extends AbstractDaemonTest {
String changeId = result.getChangeId();
assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
- exception.expect(BadRequestException.class);
- exception.expectMessage("cannot set merged change to private");
- gApi.changes().id(changeId).setPrivate(true);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.changes().id(changeId).setPrivate(true));
+ assertThat(thrown).hasMessageThat().contains("cannot set merged change to private");
}
@Test
@@ -102,9 +106,9 @@ public class PrivateChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).abandon();
assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
- exception.expect(BadRequestException.class);
- exception.expectMessage("cannot set abandoned change to private");
- gApi.changes().id(changeId).setPrivate(true);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.changes().id(changeId).setPrivate(true));
+ assertThat(thrown).hasMessageThat().contains("cannot set abandoned change to private");
}
@Test
@@ -126,9 +130,11 @@ public class PrivateChangeIT extends AbstractDaemonTest {
public void cannotSetOtherUsersChangePrivate() throws Exception {
PushOneCommit.Result result = createChange();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("not allowed to mark private");
- gApi.changes().id(result.getChangeId()).setPrivate(true, null);
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(result.getChangeId()).setPrivate(true, null));
+ assertThat(thrown).hasMessageThat().contains("not allowed to mark private");
}
@Test
@@ -153,9 +159,10 @@ public class PrivateChangeIT extends AbstractDaemonTest {
gApi.changes().id(result.getChangeId()).reviewer(admin.id().toString()).remove();
// This change should not be visible for admin anymore.
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Not found: " + result.getChangeId());
- gApi.changes().id(result.getChangeId());
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.changes().id(result.getChangeId()));
+ assertThat(thrown).hasMessageThat().contains("Not found: " + result.getChangeId());
}
@Test
@@ -163,7 +170,11 @@ public class PrivateChangeIT extends AbstractDaemonTest {
PushOneCommit.Result result = createChange();
gApi.changes().id(result.getChangeId()).setPrivate(true, null);
- allow("refs/*", Permission.VIEW_PRIVATE_CHANGES, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.VIEW_PRIVATE_CHANGES).ref("refs/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
}
@@ -173,7 +184,7 @@ public class PrivateChangeIT extends AbstractDaemonTest {
PushOneCommit.Result result = createChange();
String changeId = result.getChangeId();
merge(result);
- markMergedChangePrivate(new Change.Id(gApi.changes().id(changeId).get()._number));
+ markMergedChangePrivate(Change.id(gApi.changes().id(changeId).get()._number));
gApi.changes().id(changeId).setPrivate(false, null);
assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
@@ -191,9 +202,9 @@ public class PrivateChangeIT extends AbstractDaemonTest {
merge(result);
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("not allowed to mark private");
- gApi.changes().id(changeId).setPrivate(true, null);
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.changes().id(changeId).setPrivate(true, null));
+ assertThat(thrown).hasMessageThat().contains("not allowed to mark private");
}
@Test
@@ -218,7 +229,7 @@ public class PrivateChangeIT extends AbstractDaemonTest {
String changeId = result.getChangeId();
gApi.changes().id(changeId).addReviewer(admin.id().toString());
merge(result);
- markMergedChangePrivate(new Change.Id(gApi.changes().id(changeId).get()._number));
+ markMergedChangePrivate(Change.id(gApi.changes().id(changeId).get()._number));
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).setPrivate(false, null);
diff --git a/javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java b/javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java
index 843527acfd..78354d653b 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/QueryChangesIT.java
@@ -48,7 +48,7 @@ public class QueryChangesIT extends AbstractDaemonTest {
queryChanges.addQuery("is:wip repo:" + project.get());
List<List<ChangeInfo>> result =
- (List<List<ChangeInfo>>) queryChanges.apply(TopLevelResource.INSTANCE);
+ (List<List<ChangeInfo>>) queryChanges.apply(TopLevelResource.INSTANCE).value();
assertThat(result).hasSize(2);
assertThat(result.get(0)).hasSize(2);
assertThat(result.get(1)).hasSize(1);
@@ -75,7 +75,7 @@ public class QueryChangesIT extends AbstractDaemonTest {
queryChanges.addQuery(queryWithMoreChanges);
queryChanges.addQuery(queryWithNoMoreChanges);
List<List<ChangeInfo>> result =
- (List<List<ChangeInfo>>) queryChanges.apply(TopLevelResource.INSTANCE);
+ (List<List<ChangeInfo>>) queryChanges.apply(TopLevelResource.INSTANCE).value();
assertThat(result).hasSize(2);
assertThat(result.get(0)).hasSize(1);
assertThat(result.get(1)).hasSize(3);
@@ -88,7 +88,7 @@ public class QueryChangesIT extends AbstractDaemonTest {
queryChanges2.addQuery(queryWithNoMoreChanges);
queryChanges2.addQuery(queryWithMoreChanges);
List<List<ChangeInfo>> result2 =
- (List<List<ChangeInfo>>) queryChanges2.apply(TopLevelResource.INSTANCE);
+ (List<List<ChangeInfo>>) queryChanges2.apply(TopLevelResource.INSTANCE).value();
assertThat(result2).hasSize(2);
assertThat(result2.get(0)).hasSize(3);
assertThat(result2.get(1)).hasSize(1);
diff --git a/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
new file mode 100644
index 0000000000..0607a3ce36
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/change/RevertIT.java
@@ -0,0 +1,454 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.api.change;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static java.util.stream.Collectors.toList;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
+import com.google.gerrit.extensions.api.changes.RevertInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.client.ProjectState;
+import com.google.gerrit.extensions.client.ReviewerState;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.ChangeMessageInfo;
+import com.google.gerrit.extensions.common.PureRevertInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.permissions.PermissionDeniedException;
+import com.google.gerrit.testing.FakeEmailSender.Message;
+import com.google.inject.Inject;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Test;
+
+public class RevertIT extends AbstractDaemonTest {
+
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
+ @Test
+ public void pureRevertFactBlocksSubmissionOfNonReverts() throws Exception {
+ addPureRevertSubmitRule();
+
+ // Create a change that is not a revert of another change
+ PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
+ approve(r1.getChangeId());
+
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r1.getChangeId()).current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Failed to submit 1 change due to the following problems");
+ assertThat(thrown).hasMessageThat().contains("needs Is-Pure-Revert");
+ }
+
+ @Test
+ public void pureRevertFactBlocksSubmissionOfNonPureReverts() throws Exception {
+ PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
+ merge(r1);
+
+ addPureRevertSubmitRule();
+
+ // Create a revert and push a content change
+ String revertId = gApi.changes().id(r1.getChangeId()).revert().get().changeId;
+ amendChange(revertId);
+ approve(revertId);
+
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.changes().id(revertId).current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Failed to submit 1 change due to the following problems");
+ assertThat(thrown).hasMessageThat().contains("needs Is-Pure-Revert");
+ }
+
+ @Test
+ public void pureRevertFactAllowsSubmissionOfPureReverts() throws Exception {
+ // Create a change that we can later revert
+ PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
+ merge(r1);
+
+ addPureRevertSubmitRule();
+
+ // Create a revert and submit it
+ String revertId = gApi.changes().id(r1.getChangeId()).revert().get().changeId;
+ approve(revertId);
+ gApi.changes().id(revertId).current().submit();
+ }
+
+ @Test
+ public void pureRevertReturnsTrueForPureRevert() throws Exception {
+ PushOneCommit.Result r = createChange();
+ merge(r);
+ String revertId = gApi.changes().id(r.getChangeId()).revert().get().id;
+ // Without query parameter
+ assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isTrue();
+ // With query parameter
+ assertThat(
+ gApi.changes()
+ .id(revertId)
+ .pureRevert(
+ projectOperations.project(project).getHead("master").toObjectId().name())
+ .isPureRevert)
+ .isTrue();
+ }
+
+ @Test
+ public void pureRevertReturnsFalseOnContentChange() throws Exception {
+ PushOneCommit.Result r1 = createChange();
+ merge(r1);
+ // Create a revert and expect pureRevert to be true
+ String revertId = gApi.changes().id(r1.getChangeId()).revert().get().changeId;
+ assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isTrue();
+
+ // Create a new PS and expect pureRevert to be false
+ PushOneCommit.Result result = amendChange(revertId);
+ result.assertOkStatus();
+ assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isFalse();
+ }
+
+ @Test
+ public void pureRevertParameterTakesPrecedence() throws Exception {
+ PushOneCommit.Result r1 = createChange("commit message", "a.txt", "content1");
+ merge(r1);
+ String oldHead = projectOperations.project(project).getHead("master").toObjectId().name();
+
+ PushOneCommit.Result r2 = createChange("commit message", "a.txt", "content2");
+ merge(r2);
+
+ String revertId = gApi.changes().id(r2.getChangeId()).revert().get().changeId;
+ assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isTrue();
+ assertThat(gApi.changes().id(revertId).pureRevert(oldHead).isPureRevert).isFalse();
+ }
+
+ @Test
+ public void pureRevertReturnsFalseOnInvalidInput() throws Exception {
+ PushOneCommit.Result r1 = createChange();
+ merge(r1);
+
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(createChange().getChangeId()).pureRevert("invalid id"));
+ assertThat(thrown).hasMessageThat().contains("invalid object ID");
+ }
+
+ @Test
+ public void pureRevertReturnsTrueWithCleanRebase() throws Exception {
+ PushOneCommit.Result r1 = createChange("commit message", "a.txt", "content1");
+ merge(r1);
+
+ PushOneCommit.Result r2 = createChange("commit message", "b.txt", "content2");
+ merge(r2);
+
+ String revertId = gApi.changes().id(r1.getChangeId()).revert().get().changeId;
+ // Rebase revert onto HEAD
+ gApi.changes().id(revertId).rebase();
+ // Check that pureRevert is true which implies that the commit can be rebased onto the original
+ // commit.
+ assertThat(gApi.changes().id(revertId).pureRevert().isPureRevert).isTrue();
+ }
+
+ @Test
+ public void pureRevertReturnsFalseWithRebaseConflict() throws Exception {
+ // Create an initial commit to serve as claimed original
+ PushOneCommit.Result r1 = createChange("commit message", "a.txt", "content1");
+ merge(r1);
+ String claimedOriginal =
+ projectOperations.project(project).getHead("master").toObjectId().name();
+
+ // Change contents of the file to provoke a conflict
+ merge(createChange("commit message", "a.txt", "content2"));
+
+ // Create a commit that we can revert
+ PushOneCommit.Result r2 = createChange("commit message", "a.txt", "content3");
+ merge(r2);
+
+ // Create a revert of r2
+ String revertR3Id = gApi.changes().id(r2.getChangeId()).revert().id();
+ // Assert that the change is a pure revert of it's 'revertOf'
+ assertThat(gApi.changes().id(revertR3Id).pureRevert().isPureRevert).isTrue();
+ // Assert that the change is not a pure revert of claimedOriginal because pureRevert is trying
+ // to rebase this on claimed original, which fails.
+ PureRevertInfo pureRevert = gApi.changes().id(revertR3Id).pureRevert(claimedOriginal);
+ assertThat(pureRevert.isPureRevert).isFalse();
+ }
+
+ @Test
+ public void pureRevertThrowsExceptionWhenChangeIsNotARevertAndNoIdProvided() throws Exception {
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(createChange().getChangeId()).pureRevert());
+ assertThat(thrown).hasMessageThat().contains("revertOf not set");
+ }
+
+ @Test
+ public void revert() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+ ChangeInfo revertChange = gApi.changes().id(r.getChangeId()).revert().get();
+
+ // expected messages on source change:
+ // 1. Uploaded patch set 1.
+ // 2. Patch Set 1: Code-Review+2
+ // 3. Change has been successfully merged by Administrator
+ // 4. Patch Set 1: Reverted
+ List<ChangeMessageInfo> sourceMessages =
+ new ArrayList<>(gApi.changes().id(r.getChangeId()).get().messages);
+ assertThat(sourceMessages).hasSize(4);
+ String expectedMessage =
+ String.format("Created a revert of this change as %s", revertChange.changeId);
+ assertThat(sourceMessages.get(3).message).isEqualTo(expectedMessage);
+
+ assertThat(revertChange.messages).hasSize(1);
+ assertThat(revertChange.messages.iterator().next().message).isEqualTo("Uploaded patch set 1.");
+ assertThat(revertChange.revertOf).isEqualTo(gApi.changes().id(r.getChangeId()).get()._number);
+ }
+
+ @Test
+ public void revertWithDefaultTopic() throws Exception {
+ PushOneCommit.Result result = createChange();
+ gApi.changes().id(result.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result.getChangeId()).topic("topic");
+ gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
+ RevertInput revertInput = new RevertInput();
+ assertThat(gApi.changes().id(result.getChangeId()).revert(revertInput).topic())
+ .isEqualTo("topic");
+ }
+
+ @Test
+ public void revertWithSetTopic() throws Exception {
+ PushOneCommit.Result result = createChange();
+ gApi.changes().id(result.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result.getChangeId()).topic("topic");
+ gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
+ RevertInput revertInput = new RevertInput();
+ revertInput.topic = "reverted-not-default";
+ assertThat(gApi.changes().id(result.getChangeId()).revert(revertInput).topic())
+ .isEqualTo(revertInput.topic);
+ }
+
+ @Test
+ public void revertWithSetMessage() throws Exception {
+ PushOneCommit.Result result = createChange();
+ gApi.changes().id(result.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
+ RevertInput revertInput = new RevertInput();
+ revertInput.message = "Message from input";
+ assertThat(gApi.changes().id(result.getChangeId()).revert(revertInput).get().subject)
+ .isEqualTo(revertInput.message);
+ }
+
+ @Test
+ public void revertNotifications() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).addReviewer(user.email());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+
+ sender.clear();
+ ChangeInfo revertChange = gApi.changes().id(r.getChangeId()).revert().get();
+
+ List<Message> messages = sender.getMessages();
+ assertThat(messages).hasSize(2);
+ assertThat(sender.getMessages(revertChange.changeId, "newchange")).hasSize(1);
+ assertThat(sender.getMessages(r.getChangeId(), "revert")).hasSize(1);
+ }
+
+ @Test
+ public void suppressRevertNotifications() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).addReviewer(user.email());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+
+ RevertInput revertInput = new RevertInput();
+ revertInput.notify = NotifyHandling.NONE;
+
+ sender.clear();
+ gApi.changes().id(r.getChangeId()).revert(revertInput).get();
+ assertThat(sender.getMessages()).isEmpty();
+ }
+
+ @Test
+ public void revertPreservesReviewersAndCcs() throws Exception {
+ PushOneCommit.Result r = createChange();
+
+ ReviewInput in = ReviewInput.approve();
+ in.reviewer(user.email());
+ in.reviewer(accountCreator.user2().email(), ReviewerState.CC, true);
+ // Add user as reviewer that will create the revert
+ in.reviewer(accountCreator.admin2().email());
+
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+
+ // expect both the original reviewers and CCs to be preserved
+ // original owner should be added as reviewer, user requesting the revert (new owner) removed
+ requestScopeOperations.setApiUser(accountCreator.admin2().id());
+ Map<ReviewerState, Collection<AccountInfo>> result =
+ gApi.changes().id(r.getChangeId()).revert().get().reviewers;
+ assertThat(result).containsKey(ReviewerState.REVIEWER);
+
+ List<Integer> reviewers =
+ result.get(ReviewerState.REVIEWER).stream().map(a -> a._accountId).collect(toList());
+ assertThat(result).containsKey(ReviewerState.CC);
+ List<Integer> ccs =
+ result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
+ assertThat(ccs).containsExactly(accountCreator.user2().id().get());
+ assertThat(reviewers).containsExactly(user.id().get(), admin.id().get());
+ }
+
+ @Test
+ @TestProjectInput(createEmptyCommit = false)
+ public void revertInitialCommit() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.changes().id(r.getChangeId()).revert());
+ assertThat(thrown).hasMessageThat().contains("Cannot revert initial commit");
+ }
+
+ @Test
+ public void cantRevertNonMergedCommit() throws Exception {
+ PushOneCommit.Result result = createChange();
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(result.getChangeId()).revert());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("change is " + ChangeUtil.status(result.getChange().change()));
+ }
+
+ @Test
+ public void cantCreateRevertWithoutProjectWritePermission() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+ projectCache.checkedGet(project).getProject().setState(ProjectState.READ_ONLY);
+
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.changes().id(r.getChangeId()).revert());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("project state " + ProjectState.READ_ONLY + " does not permit write");
+ }
+
+ @Test
+ public void cantCreateRevertWithoutCreateChangePermission() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.PUSH).ref("refs/for/*").group(REGISTERED_USERS))
+ .update();
+
+ PermissionDeniedException thrown =
+ assertThrows(
+ PermissionDeniedException.class, () -> gApi.changes().id(r.getChangeId()).revert());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("not permitted: create change on refs/heads/master");
+ }
+
+ @Test
+ public void cantCreateRevertWithoutReadPermission() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
+
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
+
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.changes().id(r.getChangeId()).revert());
+ assertThat(thrown).hasMessageThat().contains("Not found: " + r.getChangeId());
+ }
+
+ @Override
+ protected PushOneCommit.Result createChange() throws Exception {
+ return createChange("refs/for/master");
+ }
+
+ @Override
+ protected PushOneCommit.Result createChange(String ref) throws Exception {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ PushOneCommit.Result result = push.to(ref);
+ result.assertOkStatus();
+ return result;
+ }
+
+ private void addPureRevertSubmitRule() throws Exception {
+ modifySubmitRules(
+ "submit_rule(submit(R)) :- \n"
+ + "gerrit:pure_revert(1), \n"
+ + "!,"
+ + "gerrit:uploader(U), \n"
+ + "R = label('Is-Pure-Revert', ok(U)).\n"
+ + "submit_rule(submit(R)) :- \n"
+ + "gerrit:pure_revert(U), \n"
+ + "U \\= 1,"
+ + "R = label('Is-Pure-Revert', need(_)). \n\n");
+ }
+
+ private void modifySubmitRules(String newContent) throws Exception {
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> testRepo = new TestRepository<>(repo)) {
+ testRepo
+ .branch(RefNames.REFS_CONFIG)
+ .commit()
+ .author(admin.newIdent())
+ .committer(admin.newIdent())
+ .add("rules.pl", newContent)
+ .message("Modify rules.pl")
+ .create();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
index d53b305db3..7e692518d8 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
@@ -15,6 +15,8 @@
package com.google.gerrit.acceptance.api.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.extensions.client.ChangeKind.MERGE_FIRST_PARENT_UPDATE;
import static com.google.gerrit.extensions.client.ChangeKind.NO_CHANGE;
import static com.google.gerrit.extensions.client.ChangeKind.NO_CODE_CHANGE;
@@ -24,19 +26,21 @@ import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_COMM
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.project.testing.Util.category;
-import static com.google.gerrit.server.project.testing.Util.value;
+import static com.google.gerrit.server.project.testing.TestLabels.label;
+import static com.google.gerrit.server.project.testing.TestLabels.value;
import static org.eclipse.jgit.lib.Constants.HEAD;
+import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.RevisionApi;
@@ -44,12 +48,14 @@ import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.CommitInfo;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.change.ChangeKindCacheImpl;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.inject.Inject;
+import com.google.inject.name.Named;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.ObjectId;
@@ -60,15 +66,20 @@ import org.junit.Test;
@NoHttpd
public class StickyApprovalsIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject
+ @Named("change_kind")
+ private Cache<ChangeKindCacheImpl.Key, ChangeKind> changeKindCache;
+
@Before
public void setup() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
// Overwrite "Code-Review" label that is inherited from All-Projects.
// This way changes to the "Code Review" label don't affect other tests.
LabelType codeReview =
- category(
+ label(
"Code-Review",
value(2, "Looks good to me, approved"),
value(1, "Looks good to me, but someone else must approve"),
@@ -79,28 +90,26 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
u.getConfig().getLabelSections().put(codeReview.getName(), codeReview);
LabelType verified =
- category("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
+ label("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
verified.setCopyAllScoresIfNoChange(false);
u.getConfig().getLabelSections().put(verified.getName(), verified);
- AccountGroup.UUID registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
- String heads = RefNames.REFS_HEADS + "*";
- Util.allow(
- u.getConfig(),
- Permission.forLabel(Util.codeReview().getName()),
- -2,
- 2,
- registeredUsers,
- heads);
- Util.allow(
- u.getConfig(),
- Permission.forLabel(Util.verified().getName()),
- -1,
- 1,
- registeredUsers,
- heads);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel(TestLabels.codeReview().getName())
+ .ref(RefNames.REFS_HEADS + "*")
+ .group(REGISTERED_USERS)
+ .range(-2, 2))
+ .add(
+ allowLabel(TestLabels.verified().getName())
+ .ref(RefNames.REFS_HEADS + "*")
+ .group(REGISTERED_USERS)
+ .range(-1, 1))
+ .update();
}
@Test
@@ -110,6 +119,28 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
}
@Test
+ public void stickyOnAnyScore() throws Exception {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ u.getConfig().getLabelSections().get("Code-Review").setCopyAnyScore(true);
+ u.save();
+ }
+
+ for (ChangeKind changeKind :
+ EnumSet.of(REWORK, TRIVIAL_REBASE, NO_CODE_CHANGE, MERGE_FIRST_PARENT_UPDATE, NO_CHANGE)) {
+ testRepo.reset(projectOperations.project(project).getHead("master"));
+
+ String changeId = createChange(changeKind);
+ vote(admin, changeId, 2, 1);
+ vote(user, changeId, 1, -1);
+
+ updateChange(changeId, changeKind);
+ ChangeInfo c = detailedChange(changeId);
+ assertVotes(c, admin, 2, 0, changeKind);
+ assertVotes(c, user, 1, 0, changeKind);
+ }
+ }
+
+ @Test
public void stickyOnMinScore() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getLabelSections().get("Code-Review").setCopyMinScore(true);
@@ -118,7 +149,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
for (ChangeKind changeKind :
EnumSet.of(REWORK, TRIVIAL_REBASE, NO_CODE_CHANGE, MERGE_FIRST_PARENT_UPDATE, NO_CHANGE)) {
- testRepo.reset(getRemoteHead());
+ testRepo.reset(projectOperations.project(project).getHead("master"));
String changeId = createChange(changeKind);
vote(admin, changeId, -1, 1);
@@ -140,7 +171,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
for (ChangeKind changeKind :
EnumSet.of(REWORK, TRIVIAL_REBASE, NO_CODE_CHANGE, MERGE_FIRST_PARENT_UPDATE, NO_CHANGE)) {
- testRepo.reset(getRemoteHead());
+ testRepo.reset(projectOperations.project(project).getHead("master"));
String changeId = createChange(changeKind);
vote(admin, changeId, 2, 1);
@@ -177,7 +208,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
assertNotSticky(EnumSet.of(REWORK, NO_CODE_CHANGE, MERGE_FIRST_PARENT_UPDATE));
// check that votes are sticky when trivial rebase is done by cherry-pick
- testRepo.reset(getRemoteHead());
+ testRepo.reset(projectOperations.project(project).getHead("master"));
changeId = createChange().getChangeId();
vote(admin, changeId, 2, 1);
vote(user, changeId, -2, -1);
@@ -188,7 +219,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
assertVotes(c, user, -2, 0);
// check that votes are not sticky when rework is done by cherry-pick
- testRepo.reset(getRemoteHead());
+ testRepo.reset(projectOperations.project(project).getHead("master"));
changeId = createChange().getChangeId();
vote(admin, changeId, 2, 1);
vote(user, changeId, -2, -1);
@@ -277,7 +308,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
for (ChangeKind changeKind :
EnumSet.of(REWORK, TRIVIAL_REBASE, NO_CODE_CHANGE, MERGE_FIRST_PARENT_UPDATE, NO_CHANGE)) {
- testRepo.reset(getRemoteHead());
+ testRepo.reset(projectOperations.project(project).getHead("master"));
String changeId = createChange(changeKind);
vote(admin, changeId, 2, 1);
@@ -320,6 +351,42 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
}
@Test
+ public void stickyAcrossMultiplePatchSetsDoNotRegressPerformance() throws Exception {
+ // The purpose of this test is to make sure that we compute change kind only against the parent
+ // patch set. Change kind is a heavy operation. In prior version of Gerrit, we computed the
+ // change kind against all prior patch sets. This is a regression that made Gerrit do expensive
+ // work in O(num-patch-sets). This test ensures that we aren't regressing.
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ u.getConfig().getLabelSections().get("Code-Review").setCopyMaxScore(true);
+ u.getConfig().getLabelSections().get("Verified").setCopyAllScoresIfNoCodeChange(true);
+ u.save();
+ }
+
+ String changeId = createChange(REWORK);
+ vote(admin, changeId, 2, 1);
+ updateChange(changeId, NO_CODE_CHANGE);
+ updateChange(changeId, NO_CODE_CHANGE);
+ updateChange(changeId, NO_CODE_CHANGE);
+
+ Map<Integer, ObjectId> revisions = new HashMap<>();
+ gApi.changes()
+ .id(changeId)
+ .get()
+ .revisions
+ .forEach(
+ (revId, revisionInfo) ->
+ revisions.put(revisionInfo._number, ObjectId.fromString(revId)));
+ assertThat(revisions.size()).isEqualTo(4);
+ assertChangeKindCacheContains(revisions.get(3), revisions.get(4));
+ assertChangeKindCacheContains(revisions.get(2), revisions.get(3));
+ assertChangeKindCacheContains(revisions.get(1), revisions.get(2));
+
+ assertChangeKindCacheDoesNotContain(revisions.get(1), revisions.get(4));
+ assertChangeKindCacheDoesNotContain(revisions.get(2), revisions.get(4));
+ assertChangeKindCacheDoesNotContain(revisions.get(1), revisions.get(3));
+ }
+
+ @Test
public void copyMinMaxAcrossMultiplePatchSets() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getLabelSections().get("Code-Review").setCopyMaxScore(true);
@@ -379,13 +446,25 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
assertVotes(detailedChange(changeId), admin, label, 0, REWORK);
}
+ private void assertChangeKindCacheContains(ObjectId prior, ObjectId next) {
+ ChangeKind kind =
+ changeKindCache.getIfPresent(ChangeKindCacheImpl.Key.create(prior, next, "recursive"));
+ assertThat(kind).isNotNull();
+ }
+
+ private void assertChangeKindCacheDoesNotContain(ObjectId prior, ObjectId next) {
+ ChangeKind kind =
+ changeKindCache.getIfPresent(ChangeKindCacheImpl.Key.create(prior, next, "recursive"));
+ assertThat(kind).isNull();
+ }
+
private ChangeInfo detailedChange(String changeId) throws Exception {
return gApi.changes().id(changeId).get(DETAILED_LABELS, CURRENT_REVISION, CURRENT_COMMIT);
}
private void assertNotSticky(Set<ChangeKind> changeKinds) throws Exception {
for (ChangeKind changeKind : changeKinds) {
- testRepo.reset(getRemoteHead());
+ testRepo.reset(projectOperations.project(project).getHead("master"));
String changeId = createChange(changeKind);
vote(admin, changeId, +2, 1);
@@ -430,7 +509,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
noChange(changeId);
return;
default:
- fail("unexpected change kind: " + changeKind);
+ assertWithMessage("unexpected change kind: " + changeKind).fail();
}
}
@@ -476,7 +555,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
private void trivialRebase(String changeId) throws Exception {
requestScopeOperations.setApiUser(admin.id());
- testRepo.reset(getRemoteHead());
+ testRepo.reset(projectOperations.project(project).getHead("master"));
PushOneCommit push =
pushFactory.create(
admin.newIdent(),
@@ -557,10 +636,10 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
case NO_CHANGE:
case MERGE_FIRST_PARENT_UPDATE:
default:
- fail("unexpected change kind: " + changeKind);
+ assertWithMessage("unexpected change kind: " + changeKind).fail();
}
- testRepo.reset(getRemoteHead());
+ testRepo.reset(projectOperations.project(project).getHead("master"));
PushOneCommit.Result r =
pushFactory
.create(
@@ -581,7 +660,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
CherryPickInput in = new CherryPickInput();
in.destination = "master";
in.message = String.format("%s\n\nChange-Id: %s", subject, changeId);
- ChangeInfo c = gApi.changes().id(changeId).revision("current").cherryPick(in).get();
+ ChangeInfo c = gApi.changes().id(changeId).current().cherryPick(in).get();
return c.changeId;
}
@@ -634,6 +713,6 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
if (changeKind != null) {
name += "; changeKind = " + changeKind.name();
}
- assertThat(vote).named(name).isEqualTo(expectedVote);
+ assertWithMessage(name).that(vote).isEqualTo(expectedVote);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
index 6c0e9ab51b..dab2d00de8 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
@@ -21,11 +21,14 @@ import static com.google.gerrit.extensions.client.SubmitType.MERGE_ALWAYS;
import static com.google.gerrit.extensions.client.SubmitType.MERGE_IF_NECESSARY;
import static com.google.gerrit.extensions.client.SubmitType.REBASE_ALWAYS;
import static com.google.gerrit.extensions.client.SubmitType.REBASE_IF_NECESSARY;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.client.SubmitType;
@@ -33,8 +36,6 @@ import com.google.gerrit.extensions.common.TestSubmitRuleInfo;
import com.google.gerrit.extensions.common.TestSubmitRuleInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import com.google.gerrit.testing.ConfigSuite;
@@ -237,22 +238,21 @@ public class SubmitTypeRuleIT extends AbstractDaemonTest {
gApi.changes().id(r1.getChangeId()).current().review(ReviewInput.approve());
gApi.changes().id(r2.getChangeId()).current().review(ReviewInput.approve());
- try {
- gApi.changes().id(r2.getChangeId()).current().submit();
- fail("Expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- "Failed to submit 2 changes due to the following problems:\n"
- + "Change "
- + r1.getChange().getId()
- + ": Change has submit type "
- + "CHERRY_PICK, but previously chose submit type MERGE_IF_NECESSARY "
- + "from change "
- + r2.getChange().getId()
- + " in the same batch");
- }
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r2.getChangeId()).current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ "Failed to submit 2 changes due to the following problems:\n"
+ + "Change "
+ + r1.getChange().getId()
+ + ": Change has submit type "
+ + "CHERRY_PICK, but previously chose submit type MERGE_IF_NECESSARY "
+ + "from change "
+ + r2.getChange().getId()
+ + " in the same batch");
}
@Test
@@ -262,8 +262,8 @@ public class SubmitTypeRuleIT extends AbstractDaemonTest {
TestSubmitRuleInput in = new TestSubmitRuleInput();
in.rule = "invalid prolog rule";
// We have no rules.pl by default. The fact that the default rules are showing up here is a bug.
- List<TestSubmitRuleInfo> response = gApi.changes().id(changeId).current().testSubmitRule(in);
- assertThat(response).containsExactly(invalidPrologRuleInfo());
+ TestSubmitRuleInfo response = gApi.changes().id(changeId).current().testSubmitRule(in);
+ assertThat(response).isEqualTo(invalidPrologRuleInfo());
}
@Test
@@ -274,8 +274,8 @@ public class SubmitTypeRuleIT extends AbstractDaemonTest {
TestSubmitRuleInput in = new TestSubmitRuleInput();
in.rule = "invalid prolog rule";
- List<TestSubmitRuleInfo> response = gApi.changes().id(changeId).current().testSubmitRule(in);
- assertThat(response).containsExactly(invalidPrologRuleInfo());
+ TestSubmitRuleInfo response = gApi.changes().id(changeId).current().testSubmitRule(in);
+ assertThat(response).isEqualTo(invalidPrologRuleInfo());
}
private static TestSubmitRuleInfo invalidPrologRuleInfo() {
diff --git a/javatests/com/google/gerrit/acceptance/api/config/DiffPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/config/DiffPreferencesIT.java
index fd08838e24..70aa557438 100644
--- a/javatests/com/google/gerrit/acceptance/api/config/DiffPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/config/DiffPreferencesIT.java
@@ -14,7 +14,7 @@
package com.google.gerrit.acceptance.api.config;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.AssertUtil.assertPrefs;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -37,7 +37,7 @@ public class DiffPreferencesIT extends AbstractDaemonTest {
DiffPreferencesInfo update = new DiffPreferencesInfo();
update.lineLength = newLineLength;
DiffPreferencesInfo result = gApi.config().server().setDefaultDiffPreferences(update);
- assertThat(result.lineLength).named("lineLength").isEqualTo(newLineLength);
+ assertWithMessage("lineLength").that(result.lineLength).isEqualTo(newLineLength);
result = gApi.config().server().getDefaultDiffPreferences();
DiffPreferencesInfo expected = DiffPreferencesInfo.defaults();
diff --git a/javatests/com/google/gerrit/acceptance/api/config/EditPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/config/EditPreferencesIT.java
index e89aa3dec2..02f1ec355c 100644
--- a/javatests/com/google/gerrit/acceptance/api/config/EditPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/config/EditPreferencesIT.java
@@ -14,7 +14,7 @@
package com.google.gerrit.acceptance.api.config;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.AssertUtil.assertPrefs;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -37,7 +37,7 @@ public class EditPreferencesIT extends AbstractDaemonTest {
EditPreferencesInfo update = new EditPreferencesInfo();
update.lineLength = newLineLength;
EditPreferencesInfo result = gApi.config().server().setDefaultEditPreferences(update);
- assertThat(result.lineLength).named("lineLength").isEqualTo(newLineLength);
+ assertWithMessage("lineLength").that(result.lineLength).isEqualTo(newLineLength);
result = gApi.config().server().getDefaultEditPreferences();
EditPreferencesInfo expected = EditPreferencesInfo.defaults();
diff --git a/javatests/com/google/gerrit/acceptance/api/config/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/config/GeneralPreferencesIT.java
index c6069824c6..221e171959 100644
--- a/javatests/com/google/gerrit/acceptance/api/config/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/config/GeneralPreferencesIT.java
@@ -14,7 +14,7 @@
package com.google.gerrit.acceptance.api.config;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.AssertUtil.assertPrefs;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -36,7 +36,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
GeneralPreferencesInfo update = new GeneralPreferencesInfo();
update.signedOffBy = newSignedOffBy;
GeneralPreferencesInfo result = gApi.config().server().setDefaultPreferences(update);
- assertThat(result.signedOffBy).named("signedOffBy").isEqualTo(newSignedOffBy);
+ assertWithMessage("signedOffBy").that(result.signedOffBy).isEqualTo(newSignedOffBy);
result = gApi.config().server().getDefaultPreferences();
GeneralPreferencesInfo expected = GeneralPreferencesInfo.defaults();
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
index d7d311ba39..41ae3701a5 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
@@ -20,11 +20,11 @@ import static com.google.gerrit.truth.ListSubject.assertThat;
import static com.google.gerrit.truth.OptionalSubject.assertThat;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
@@ -57,7 +57,7 @@ public class GroupIndexerIT {
@Test
public void indexingUpdatesTheIndex() throws Exception {
AccountGroup.UUID groupUuid = createGroup("users");
- AccountGroup.UUID subgroupUuid = new AccountGroup.UUID("contributors");
+ AccountGroup.UUID subgroupUuid = AccountGroup.uuid("contributors");
updateGroupWithoutCacheOrIndex(
groupUuid,
newGroupUpdate()
@@ -74,7 +74,7 @@ public class GroupIndexerIT {
public void indexCannotBeCorruptedByStaleCache() throws Exception {
AccountGroup.UUID groupUuid = createGroup("verifiers");
loadGroupToCache(groupUuid);
- AccountGroup.UUID subgroupUuid = new AccountGroup.UUID("contributors");
+ AccountGroup.UUID subgroupUuid = AccountGroup.uuid("contributors");
updateGroupWithoutCacheOrIndex(
groupUuid,
newGroupUpdate()
@@ -102,7 +102,7 @@ public class GroupIndexerIT {
@Test
public void reindexingStaleGroupUpdatesTheIndex() throws Exception {
AccountGroup.UUID groupUuid = createGroup("users");
- AccountGroup.UUID subgroupUuid = new AccountGroup.UUID("contributors");
+ AccountGroup.UUID subgroupUuid = AccountGroup.uuid("contributors");
updateGroupWithoutCacheOrIndex(
groupUuid,
newGroupUpdate()
@@ -139,7 +139,7 @@ public class GroupIndexerIT {
private AccountGroup.UUID createGroup(String name) throws RestApiException {
GroupInfo group = gApi.groups().create(name).get();
- return new AccountGroup.UUID(group.id);
+ return AccountGroup.uuid(group.id);
}
private void reloadGroupToCache(AccountGroup.UUID groupUuid) {
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
index e269e682f5..e6c3919e99 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
@@ -15,19 +15,22 @@
package com.google.gerrit.acceptance.api.group;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInput;
import com.google.gerrit.extensions.common.GroupInfo;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupNameNotes;
import com.google.gerrit.server.group.db.testing.GroupTestUtil;
@@ -51,6 +54,7 @@ import org.junit.Test;
public class GroupsConsistencyIT extends AbstractDaemonTest {
@Inject protected GroupOperations groupOperations;
+ @Inject private ProjectOperations projectOperations;
private GroupInfo gAdmin;
private GroupInfo g1;
private GroupInfo g2;
@@ -59,7 +63,10 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
@Before
public void basicSetup() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
String name1 = groupOperations.newGroup().name("g1").create().get();
String name2 = groupOperations.newGroup().name("g2").create().get();
@@ -94,7 +101,7 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
public void missingGroupRef() throws Exception {
try (Repository repo = repoManager.openRepository(allUsers)) {
- RefUpdate ru = repo.updateRef(RefNames.refsGroups(new AccountGroup.UUID(g1.id)));
+ RefUpdate ru = repo.updateRef(RefNames.refsGroups(AccountGroup.uuid(g1.id)));
ru.setForceUpdate(true);
RefUpdate.Result result = ru.delete();
assertThat(result).isEqualTo(Result.FORCED);
@@ -109,7 +116,7 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
try (Repository repo = repoManager.openRepository(allUsers)) {
RefRename ru =
repo.renameRef(
- RefNames.refsGroups(new AccountGroup.UUID(g1.id)), RefNames.REFS_GROUPS + BOGUS_UUID);
+ RefNames.refsGroups(AccountGroup.uuid(g1.id)), RefNames.REFS_GROUPS + BOGUS_UUID);
RefUpdate.Result result = ru.rename();
assertThat(result).isEqualTo(Result.RENAMED);
}
@@ -123,8 +130,8 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
try (Repository repo = repoManager.openRepository(allUsers)) {
RefRename ru =
repo.renameRef(
- RefNames.refsGroups(new AccountGroup.UUID(g1.id)),
- RefNames.refsGroups(new AccountGroup.UUID(BOGUS_UUID)));
+ RefNames.refsGroups(AccountGroup.uuid(g1.id)),
+ RefNames.refsGroups(AccountGroup.uuid(BOGUS_UUID)));
RefUpdate.Result result = ru.rename();
assertThat(result).isEqualTo(Result.RENAMED);
}
@@ -135,7 +142,7 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
@Test
public void groupRefDoesNotParse() throws Exception {
updateGroupFile(
- RefNames.refsGroups(new AccountGroup.UUID(g1.id)),
+ RefNames.refsGroups(AccountGroup.uuid(g1.id)),
GroupConfig.GROUP_CONFIG_FILE,
"[this is not valid\n");
assertError("does not parse");
@@ -145,7 +152,7 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
public void nameRefDoesNotParse() throws Exception {
updateGroupFile(
RefNames.REFS_GROUPNAMES,
- GroupNameNotes.getNoteKey(new AccountGroup.NameKey(g1.name)).getName(),
+ GroupNameNotes.getNoteKey(AccountGroup.nameKey(g1.name)).getName(),
"[this is not valid\n");
assertError("does not parse");
}
@@ -158,9 +165,7 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
cfg.setString("group", null, "ownerGroupUuid", gAdmin.id);
updateGroupFile(
- RefNames.refsGroups(new AccountGroup.UUID(g1.id)),
- GroupConfig.GROUP_CONFIG_FILE,
- cfg.toText());
+ RefNames.refsGroups(AccountGroup.uuid(g1.id)), GroupConfig.GROUP_CONFIG_FILE, cfg.toText());
assertError("inconsistent name");
}
@@ -172,9 +177,7 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
cfg.setString("group", null, "ownerGroupUuid", gAdmin.id);
updateGroupFile(
- RefNames.refsGroups(new AccountGroup.UUID(g1.id)),
- GroupConfig.GROUP_CONFIG_FILE,
- cfg.toText());
+ RefNames.refsGroups(AccountGroup.uuid(g1.id)), GroupConfig.GROUP_CONFIG_FILE, cfg.toText());
assertError("shared group id");
}
@@ -186,9 +189,7 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
cfg.setString("group", null, "ownerGroupUuid", BOGUS_UUID);
updateGroupFile(
- RefNames.refsGroups(new AccountGroup.UUID(g1.id)),
- GroupConfig.GROUP_CONFIG_FILE,
- cfg.toText());
+ RefNames.refsGroups(AccountGroup.uuid(g1.id)), GroupConfig.GROUP_CONFIG_FILE, cfg.toText());
assertError("nonexistent owner group");
}
@@ -201,27 +202,26 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
updateGroupFile(
RefNames.REFS_GROUPNAMES,
- GroupNameNotes.getNoteKey(new AccountGroup.NameKey(bogusName)).getName(),
+ GroupNameNotes.getNoteKey(AccountGroup.nameKey(bogusName)).getName(),
config.toText());
assertError("entry missing as group ref");
}
@Test
public void nonexistentMember() throws Exception {
- updateGroupFile(RefNames.refsGroups(new AccountGroup.UUID(g1.id)), "members", "314159265\n");
+ updateGroupFile(RefNames.refsGroups(AccountGroup.uuid(g1.id)), "members", "314159265\n");
assertError("nonexistent member 314159265");
}
@Test
public void nonexistentSubgroup() throws Exception {
- updateGroupFile(
- RefNames.refsGroups(new AccountGroup.UUID(g1.id)), "subgroups", BOGUS_UUID + "\n");
+ updateGroupFile(RefNames.refsGroups(AccountGroup.uuid(g1.id)), "subgroups", BOGUS_UUID + "\n");
assertError("has nonexistent subgroup");
}
@Test
public void cyclicSubgroup() throws Exception {
- updateGroupFile(RefNames.refsGroups(new AccountGroup.UUID(g1.id)), "subgroups", g1.id + "\n");
+ updateGroupFile(RefNames.refsGroups(AccountGroup.uuid(g1.id)), "subgroups", g1.id + "\n");
assertWarning("cycle");
}
@@ -252,7 +252,8 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
}
}
- fail(String.format("could not find %s substring '%s' in %s", want, msg, problems));
+ assertWithMessage(String.format("could not find %s substring '%s' in %s", want, msg, problems))
+ .fail();
}
private void updateGroupFile(String refName, String fileName, String content) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index 47ac7a9f9c..8f53393a21 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -21,19 +21,25 @@ import static com.google.gerrit.acceptance.GitUtil.deleteRef;
import static com.google.gerrit.acceptance.GitUtil.fetch;
import static com.google.gerrit.acceptance.api.group.GroupAssert.assertGroupInfo;
import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAccountInfos;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.truth.Correspondence;
-import com.google.common.truth.Correspondence.BinaryPredicate;
import com.google.common.util.concurrent.AtomicLongMap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.NoHttpd;
@@ -41,13 +47,19 @@ import com.google.gerrit.acceptance.ProjectResetter;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.UseClockStep;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.groups.GroupApi;
import com.google.gerrit.extensions.api.groups.GroupInput;
@@ -60,18 +72,12 @@ import com.google.gerrit.extensions.common.GroupAuditEventInfo.UserMemberAuditEv
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.GroupOptionsInfo;
import com.google.gerrit.extensions.events.GroupIndexedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.GroupIncludeCache;
import com.google.gerrit.server.group.InternalGroup;
@@ -85,11 +91,9 @@ import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.gerrit.server.index.group.StalenessChecker;
import com.google.gerrit.server.notedb.Sequences;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.util.MagicBranch;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.TestTimeUtil;
+import com.google.gerrit.testing.GerritJUnit.ThrowingRunnable;
import com.google.inject.Inject;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -100,7 +104,6 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
@@ -117,33 +120,24 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
@NoHttpd
+@UseClockStep
public class GroupsIT extends AbstractDaemonTest {
@Inject @ServerInitiated private GroupsUpdate groupsUpdate;
@Inject private AccountOperations accountOperations;
- @Inject private DynamicSet<GroupIndexedListener> groupIndexedListeners;
@Inject private GroupIncludeCache groupIncludeCache;
@Inject private GroupIndexer groupIndexer;
@Inject private GroupOperations groupOperations;
@Inject private Groups groups;
@Inject private GroupsConsistencyChecker consistencyChecker;
@Inject private PeriodicGroupIndexer slaveGroupIndexer;
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Inject private Sequences seq;
@Inject private StalenessChecker stalenessChecker;
-
- @Before
- public void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- }
-
- @After
- public void resetTime() {
- TestTimeUtil.useSystemTime();
- }
+ @Inject private ExtensionRegistry extensionRegistry;
@After
public void consistencyCheck() throws Exception {
@@ -168,14 +162,16 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void addToNonExistingGroup_NotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.groups().id("non-existing").addMembers("admin");
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.groups().id("non-existing").addMembers("admin"));
}
@Test
public void removeFromNonExistingGroup_NotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.groups().id("non-existing").removeMembers("admin");
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.groups().id("non-existing").removeMembers("admin"));
}
@Test
@@ -215,7 +211,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void cachedGroupByNameIsUpdatedOnCreation() throws Exception {
String newGroupName = name("newGroup");
- AccountGroup.NameKey nameKey = new AccountGroup.NameKey(newGroupName);
+ AccountGroup.NameKey nameKey = AccountGroup.nameKey(newGroupName);
assertThat(groupCache.get(nameKey)).isEmpty();
gApi.groups().create(newGroupName);
assertThat(groupCache.get(nameKey)).isPresent();
@@ -231,8 +227,9 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void addNonExistingMember_UnprocessableEntity() throws Exception {
- exception.expect(UnprocessableEntityException.class);
- gApi.groups().id("Administrators").addMembers("non-existing");
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.groups().id("Administrators").addMembers("non-existing"));
}
@Test
@@ -351,9 +348,9 @@ public class GroupsIT extends AbstractDaemonTest {
public void createDuplicateInternalGroupCaseSensitiveName_Conflict() throws Exception {
String dupGroupName = name("dupGroup");
gApi.groups().create(dupGroupName);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("group '" + dupGroupName + "' already exists");
- gApi.groups().create(dupGroupName);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.groups().create(dupGroupName));
+ assertThat(thrown).hasMessageThat().contains("group '" + dupGroupName + "' already exists");
}
@Test
@@ -369,33 +366,71 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void createDuplicateSystemGroupCaseSensitiveName_Conflict() throws Exception {
String newGroupName = "Registered Users";
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("group 'Registered Users' already exists");
- gApi.groups().create(newGroupName);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.groups().create(newGroupName));
+ assertThat(thrown).hasMessageThat().contains("group 'Registered Users' already exists");
}
@Test
public void createDuplicateSystemGroupCaseInsensitiveName_Conflict() throws Exception {
String newGroupName = "registered users";
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("group 'Registered Users' already exists");
- gApi.groups().create(newGroupName);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.groups().create(newGroupName));
+ assertThat(thrown).hasMessageThat().contains("group 'Registered Users' already exists");
}
@Test
@GerritConfig(name = "groups.global:Anonymous-Users.name", value = "All Users")
public void createGroupWithConfiguredNameOfSystemGroup_Conflict() throws Exception {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("group 'All Users' already exists");
- gApi.groups().create("all users");
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.groups().create("all users"));
+ assertThat(thrown).hasMessageThat().contains("group 'All Users' already exists");
}
@Test
@GerritConfig(name = "groups.global:Anonymous-Users.name", value = "All Users")
public void createGroupWithDefaultNameOfSystemGroup_Conflict() throws Exception {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("group name 'Anonymous Users' is reserved");
- gApi.groups().create("anonymous users");
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.groups().create("anonymous users"));
+ assertThat(thrown).hasMessageThat().contains("group name 'Anonymous Users' is reserved");
+ }
+
+ @Test
+ public void createGroupWithUuid() throws Exception {
+ AccountGroup.UUID uuid = AccountGroup.UUID.parse("4eb25d1cca562f53b9356117f33840706a36a349");
+ GroupInput input = new GroupInput();
+ input.uuid = uuid.get();
+ input.name = name("new-group");
+ GroupInfo info = gApi.groups().create(input).get();
+ assertThat(info.name).isEqualTo(input.name);
+ assertThat(info.id).isEqualTo(input.uuid);
+ }
+
+ @Test
+ public void createGroupWithExistingUuid_Conflict() throws Exception {
+ GroupInfo existingGroup = gApi.groups().create(name("new-group")).get();
+ GroupInput input = new GroupInput();
+ input.uuid = existingGroup.id;
+ input.name = name("another-new-group");
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.groups().create(input).get());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(String.format("group with UUID '%s' already exists", input.uuid));
+ }
+
+ @Test
+ public void createGroupWithInvalidUuid_BadRequest() throws Exception {
+ AccountGroup.UUID uuid = AccountGroup.UUID.parse("foo:bar");
+ GroupInput input = new GroupInput();
+ input.uuid = uuid.get();
+ input.name = name("new-group");
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.groups().create(input).get());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(String.format("invalid group UUID '%s'", input.uuid));
}
@Test
@@ -414,8 +449,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void createGroupWithoutCapability_Forbidden() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.groups().create(name("newGroup"));
+ assertThrows(AuthException.class, () -> gApi.groups().create(name("newGroup")));
}
@Test
@@ -441,7 +475,7 @@ public class GroupsIT extends AbstractDaemonTest {
GroupInfo group = gApi.groups().create(groupInput).get();
Collection<AccountGroup.UUID> groups = groupIncludeCache.getGroupsWithMember(accountId);
- assertThat(groups).containsExactly(new AccountGroup.UUID(group.id));
+ assertThat(groups).containsExactly(AccountGroup.uuid(group.id));
}
@Test
@@ -481,8 +515,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "groups.global:Anonymous-Users.name", value = "All Users")
public void getSystemGroupByDefaultName_NotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.groups().id("Anonymous-Users").get();
+ assertThrows(ResourceNotFoundException.class, () -> gApi.groups().id("Anonymous-Users").get());
}
@Test
@@ -498,8 +531,7 @@ public class GroupsIT extends AbstractDaemonTest {
String name = name("Users");
gApi.groups().create(name).get();
- exception.expect(ResourceConflictException.class);
- gApi.groups().create(name);
+ assertThrows(ResourceConflictException.class, () -> gApi.groups().create(name));
}
@Test
@@ -528,9 +560,7 @@ public class GroupsIT extends AbstractDaemonTest {
String name2 = name("Name2");
gApi.groups().create(name2);
-
- exception.expect(ResourceConflictException.class);
- gApi.groups().id(group1.id).name(name2);
+ assertThrows(ResourceConflictException.class, () -> gApi.groups().id(group1.id).name(name2));
}
@Test
@@ -554,8 +584,7 @@ public class GroupsIT extends AbstractDaemonTest {
gApi.groups().id(group.id).name(newName);
assertGroupDoesNotExist(name);
- exception.expect(ResourceNotFoundException.class);
- gApi.groups().id(name).get();
+ assertThrows(ResourceNotFoundException.class, () -> gApi.groups().id(name).get());
}
@Test
@@ -626,14 +655,15 @@ public class GroupsIT extends AbstractDaemonTest {
assertThat(Url.decode(gApi.groups().id(name).owner().id)).isEqualTo(adminUUID);
// set non existing owner
- exception.expect(UnprocessableEntityException.class);
- gApi.groups().id(name).owner("Non-Existing Group");
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.groups().id(name).owner("Non-Existing Group"));
}
@Test
public void listNonExistingGroupIncludes_NotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.groups().id("non-existing").includedGroups();
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.groups().id("non-existing").includedGroups());
}
@Test
@@ -645,8 +675,9 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void includeNonExistingGroup() throws Exception {
AccountGroup.UUID gx = groupOperations.newGroup().create();
- exception.expect(UnprocessableEntityException.class);
- gApi.groups().id(gx.get()).addGroups("non-existing");
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.groups().id(gx.get()).addGroups("non-existing"));
}
@Test
@@ -675,8 +706,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void listNonExistingGroupMembers_NotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.groups().id("non-existing").members();
+ assertThrows(ResourceNotFoundException.class, () -> gApi.groups().id("non-existing").members());
}
@Test
@@ -806,13 +836,13 @@ public class GroupsIT extends AbstractDaemonTest {
// By UUID
List<GroupInfo> owned = gApi.groups().list().withOwnedBy(parent.get()).get();
- assertThat(owned.stream().map(g -> new AccountGroup.UUID(g.id)).collect(toList()))
+ assertThat(owned.stream().map(g -> AccountGroup.uuid(g.id)).collect(toList()))
.containsExactlyElementsIn(children);
// By name
String parentName = groupOperations.group(parent).get().name();
owned = gApi.groups().list().withOwnedBy(parentName).get();
- assertThat(owned.stream().map(g -> new AccountGroup.UUID(g.id)).collect(toList()))
+ assertThat(owned.stream().map(g -> AccountGroup.uuid(g.id)).collect(toList()))
.containsExactlyElementsIn(children);
// By group that does not own any others
@@ -820,9 +850,11 @@ public class GroupsIT extends AbstractDaemonTest {
assertThat(owned).isEmpty();
// By non-existing group
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("Group Not Found: does-not-exist");
- gApi.groups().list().withOwnedBy("does-not-exist").get();
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.groups().list().withOwnedBy("does-not-exist").get());
+ assertThat(thrown).hasMessageThat().contains("Group Not Found: does-not-exist");
}
@Test
@@ -984,7 +1016,7 @@ public class GroupsIT extends AbstractDaemonTest {
}
private void deleteGroupRef(String groupId) throws Exception {
- AccountGroup.UUID uuid = new AccountGroup.UUID(groupId);
+ AccountGroup.UUID uuid = AccountGroup.uuid(groupId);
try (Repository repo = repoManager.openRepository(allUsers)) {
RefUpdate ru = repo.updateRef(RefNames.refsGroups(uuid));
ru.setForceUpdate(true);
@@ -996,11 +1028,7 @@ public class GroupsIT extends AbstractDaemonTest {
gApi.groups().id(uuid.get()).index();
// Verify "sub-group" has been deleted.
- try {
- gApi.groups().id(uuid.get()).get();
- fail("expected ResourceNotFoundException");
- } catch (ResourceNotFoundException e) {
- }
+ assertThrows(ResourceNotFoundException.class, () -> gApi.groups().id(uuid.get()).get());
}
// reindex is tested by {@link AbstractQueryGroupsTest#reindex}
@@ -1023,9 +1051,9 @@ public class GroupsIT extends AbstractDaemonTest {
// user cannot reindex any group
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("not allowed to index group");
- gApi.groups().id(group.id).index();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.groups().id(group.id).index());
+ assertThat(thrown).hasMessageThat().contains("not allowed to index group");
}
@Test
@@ -1037,8 +1065,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void pushToDeletedGroupBranchIsRejectedForAllUsersRepo() throws Exception {
String groupRef =
- RefNames.refsDeletedGroups(
- new AccountGroup.UUID(gApi.groups().create(name("foo")).get().id));
+ RefNames.refsDeletedGroups(AccountGroup.uuid(gApi.groups().create(name("foo")).get().id));
createBranch(allUsers, groupRef);
assertPushToGroupBranch(allUsers, groupRef, "group update not allowed");
}
@@ -1046,7 +1073,10 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void pushToGroupNamesBranchIsRejectedForAllUsersRepo() throws Exception {
// refs/meta/group-names isn't usually available for fetch, so grant ACCESS_DATABASE
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
assertPushToGroupBranch(allUsers, RefNames.REFS_GROUPNAMES, "group update not allowed");
}
@@ -1054,7 +1084,7 @@ public class GroupsIT extends AbstractDaemonTest {
public void pushToGroupsBranchForNonAllUsersRepo() throws Exception {
assertCreateGroupBranch(project);
String groupRef =
- RefNames.refsGroups(new AccountGroup.UUID(gApi.groups().create(name("foo")).get().id));
+ RefNames.refsGroups(AccountGroup.uuid(gApi.groups().create(name("foo")).get().id));
createBranch(project, groupRef);
assertPushToGroupBranch(project, groupRef, null);
}
@@ -1063,8 +1093,7 @@ public class GroupsIT extends AbstractDaemonTest {
public void pushToDeletedGroupsBranchForNonAllUsersRepo() throws Exception {
assertCreateGroupBranch(project);
String groupRef =
- RefNames.refsDeletedGroups(
- new AccountGroup.UUID(gApi.groups().create(name("foo")).get().id));
+ RefNames.refsDeletedGroups(AccountGroup.uuid(gApi.groups().create(name("foo")).get().id));
createBranch(project, groupRef);
assertPushToGroupBranch(project, groupRef, null);
}
@@ -1077,15 +1106,18 @@ public class GroupsIT extends AbstractDaemonTest {
private void assertPushToGroupBranch(
Project.NameKey project, String groupRefName, String expectedErrorOnUpdate) throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- ProjectConfig cfg = u.getConfig();
- Util.allow(cfg, Permission.CREATE, REGISTERED_USERS, RefNames.REFS_GROUPS + "*");
- Util.allow(cfg, Permission.PUSH, REGISTERED_USERS, RefNames.REFS_GROUPS + "*");
- Util.allow(cfg, Permission.CREATE, REGISTERED_USERS, RefNames.REFS_DELETED_GROUPS + "*");
- Util.allow(cfg, Permission.PUSH, REGISTERED_USERS, RefNames.REFS_DELETED_GROUPS + "*");
- Util.allow(cfg, Permission.PUSH, REGISTERED_USERS, RefNames.REFS_GROUPNAMES);
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .add(
+ allow(Permission.CREATE)
+ .ref(RefNames.REFS_DELETED_GROUPS + "*")
+ .group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_DELETED_GROUPS + "*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_GROUPNAMES).group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> repo = cloneProject(project);
@@ -1104,12 +1136,12 @@ public class GroupsIT extends AbstractDaemonTest {
}
private void assertCreateGroupBranch(Project.NameKey project) throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- ProjectConfig cfg = u.getConfig();
- Util.allow(cfg, Permission.CREATE, REGISTERED_USERS, RefNames.REFS_GROUPS + "*");
- Util.allow(cfg, Permission.PUSH, REGISTERED_USERS, RefNames.REFS_GROUPS + "*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> repo = cloneProject(project);
PushOneCommit.Result r =
pushFactory
@@ -1120,13 +1152,13 @@ public class GroupsIT extends AbstractDaemonTest {
}
@Test
- public void pushToGroupBranchForReviewForAllUsersRepoIsRejectedOnSubmit() throws Exception {
+ public void pushToGroupBranchForReviewForAllUsersRepoIsRejectedOnSubmit() throws Throwable {
pushToGroupBranchForReviewAndSubmit(
allUsers, RefNames.refsGroups(adminGroupUuid()), "group update not allowed");
}
@Test
- public void pushToGroupBranchForReviewForNonAllUsersRepoAndSubmit() throws Exception {
+ public void pushToGroupBranchForReviewForNonAllUsersRepoAndSubmit() throws Throwable {
String groupRef = RefNames.refsGroups(adminGroupUuid());
createBranch(project, groupRef);
pushToGroupBranchForReviewAndSubmit(project, groupRef, null);
@@ -1160,14 +1192,14 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void cannotCreateGroupBranch() throws Exception {
testCannotCreateGroupBranch(
- RefNames.REFS_GROUPS + "*", RefNames.refsGroups(new AccountGroup.UUID(name("foo"))));
+ RefNames.REFS_GROUPS + "*", RefNames.refsGroups(AccountGroup.uuid(name("foo"))));
}
@Test
public void cannotCreateDeletedGroupBranch() throws Exception {
testCannotCreateGroupBranch(
RefNames.REFS_DELETED_GROUPS + "*",
- RefNames.refsDeletedGroups(new AccountGroup.UUID(name("foo"))));
+ RefNames.refsDeletedGroups(AccountGroup.uuid(name("foo"))));
}
@Test
@@ -1190,15 +1222,22 @@ public class GroupsIT extends AbstractDaemonTest {
}
// refs/meta/group-names is only visible with ACCESS_DATABASE
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
testCannotCreateGroupBranch(RefNames.REFS_GROUPNAMES, RefNames.REFS_GROUPNAMES);
}
}
private void testCannotCreateGroupBranch(String refPattern, String groupRef) throws Exception {
- grant(allUsers, refPattern, Permission.CREATE);
- grant(allUsers, refPattern, Permission.PUSH);
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(refPattern).group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref(refPattern).group(adminGroupUuid()))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(groupRef);
@@ -1217,7 +1256,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void cannotDeleteDeletedGroupBranch() throws Exception {
- String groupRef = RefNames.refsDeletedGroups(new AccountGroup.UUID(name("foo")));
+ String groupRef = RefNames.refsDeletedGroups(AccountGroup.uuid(name("foo")));
createBranch(allUsers, groupRef);
testCannotDeleteGroupBranch(RefNames.REFS_DELETED_GROUPS + "*", groupRef);
}
@@ -1225,13 +1264,20 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void cannotDeleteGroupNamesBranch() throws Exception {
// refs/meta/group-names is only visible with ACCESS_DATABASE
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
testCannotDeleteGroupBranch(RefNames.REFS_GROUPNAMES, RefNames.REFS_GROUPNAMES);
}
private void testCannotDeleteGroupBranch(String refPattern, String groupRef) throws Exception {
- grant(allUsers, refPattern, Permission.DELETE, true, REGISTERED_USERS);
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.DELETE).ref(refPattern).group(REGISTERED_USERS).force(true))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
PushResult r = deleteRef(allUsersRepo, groupRef);
@@ -1255,7 +1301,7 @@ public class GroupsIT extends AbstractDaemonTest {
public void stalenessChecker() throws Exception {
// Newly created group is not stale
GroupInfo groupInfo = gApi.groups().create(name("foo")).get();
- AccountGroup.UUID groupUuid = new AccountGroup.UUID(groupInfo.id);
+ AccountGroup.UUID groupUuid = AccountGroup.uuid(groupInfo.id);
assertThat(stalenessChecker.isStale(groupUuid)).isFalse();
// Manual update makes index document stale
@@ -1325,9 +1371,7 @@ public class GroupsIT extends AbstractDaemonTest {
restartAsSlave();
GroupIndexedCounter groupIndexedCounter = new GroupIndexedCounter();
- RegistrationHandle groupIndexEventCounterHandle =
- groupIndexedListeners.add("gerrit", groupIndexedCounter);
- try {
+ try (Registration registration = extensionRegistry.newRegistration().add(groupIndexedCounter)) {
// Running the reindexer right after startup should not need to reindex any group since
// reindexing was already done on startup.
slaveGroupIndexer.run();
@@ -1336,12 +1380,12 @@ public class GroupsIT extends AbstractDaemonTest {
// Create a group without updating the cache or index,
// then run the reindexer -> only the new group is reindexed.
String groupName = "foo";
- AccountGroup.UUID groupUuid = new AccountGroup.UUID(groupName + "-UUID");
+ AccountGroup.UUID groupUuid = AccountGroup.uuid(groupName + "-UUID");
groupsUpdate.createGroupInNoteDb(
InternalGroupCreation.builder()
.setGroupUUID(groupUuid)
- .setNameKey(new AccountGroup.NameKey(groupName))
- .setId(new AccountGroup.Id(seq.nextGroupId()))
+ .setNameKey(AccountGroup.nameKey(groupName))
+ .setId(AccountGroup.id(seq.nextGroupId()))
.build(),
InternalGroupUpdate.builder().build());
slaveGroupIndexer.run();
@@ -1363,8 +1407,6 @@ public class GroupsIT extends AbstractDaemonTest {
}
slaveGroupIndexer.run();
groupIndexedCounter.assertReindexOf(groupUuid);
- } finally {
- groupIndexEventCounterHandle.remove();
}
}
@@ -1382,25 +1424,18 @@ public class GroupsIT extends AbstractDaemonTest {
restartAsSlave();
GroupIndexedCounter groupIndexedCounter = new GroupIndexedCounter();
- RegistrationHandle groupIndexEventCounterHandle =
- groupIndexedListeners.add("gerrit", groupIndexedCounter);
- try {
+ try (Registration registration = extensionRegistry.newRegistration().add(groupIndexedCounter)) {
// No group indexing happened on startup. All groups should be reindexed now.
slaveGroupIndexer.run();
groupIndexedCounter.assertReindexOf(expectedGroups);
- } finally {
- groupIndexEventCounterHandle.remove();
}
}
private static Correspondence<AccountInfo, String> getAccountToUsernameCorrespondence() {
return Correspondence.from(
- new BinaryPredicate<AccountInfo, String>() {
- @Override
- public boolean apply(AccountInfo actualAccount, String expectedName) {
- String username = actualAccount == null ? null : actualAccount.username;
- return Objects.equals(username, expectedName);
- }
+ (actualAccount, expectedName) -> {
+ String username = actualAccount == null ? null : actualAccount.username;
+ return Objects.equals(username, expectedName);
},
"has username");
}
@@ -1416,10 +1451,17 @@ public class GroupsIT extends AbstractDaemonTest {
}
private void pushToGroupBranchForReviewAndSubmit(
- Project.NameKey project, String groupRef, String expectedError) throws Exception {
- grantLabel(
- "Code-Review", -2, 2, project, RefNames.REFS_GROUPS + "*", false, REGISTERED_USERS, false);
- grant(project, RefNames.REFS_GROUPS + "*", Permission.SUBMIT, false, REGISTERED_USERS);
+ Project.NameKey project, String groupRef, String expectedError) throws Throwable {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel("Code-Review")
+ .ref(RefNames.REFS_GROUPS + "*")
+ .group(REGISTERED_USERS)
+ .range(-2, 2))
+ .add(allow(Permission.SUBMIT).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> repo = cloneProject(project);
fetch(repo, groupRef + ":groupRef");
@@ -1430,14 +1472,16 @@ public class GroupsIT extends AbstractDaemonTest {
.create(admin.newIdent(), repo, "Update group config", "group.config", "some content")
.to(MagicBranch.NEW_CHANGE + groupRef);
r.assertOkStatus();
- assertThat(r.getChange().change().getDest().get()).isEqualTo(groupRef);
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(groupRef);
gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ ThrowingRunnable submit = () -> gApi.changes().id(r.getChangeId()).current().submit();
if (expectedError != null) {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("group update not allowed");
+ Throwable thrown = assertThrows(ResourceConflictException.class, submit);
+ assertThat(thrown).hasMessageThat().contains("group update not allowed");
+ } else {
+ submit.run();
}
- gApi.changes().id(r.getChangeId()).current().submit();
}
private void createBranch(Project.NameKey project, String ref) throws IOException {
@@ -1514,16 +1558,11 @@ public class GroupsIT extends AbstractDaemonTest {
private static void assertIncludes(List<GroupInfo> includes, String... expectedNames) {
List<String> names = includes.stream().map(i -> i.name).collect(toImmutableList());
assertThat(names).containsExactlyElementsIn(Arrays.asList(expectedNames));
- assertThat(names).isOrdered();
+ assertThat(names).isInOrder();
}
private void assertBadRequest(ListRequest req) throws Exception {
- try {
- req.get();
- fail("Expected BadRequestException");
- } catch (BadRequestException e) {
- // Expected
- }
+ assertThrows(BadRequestException.class, () -> req.get());
}
@Target({METHOD})
@@ -1543,24 +1582,18 @@ public class GroupsIT extends AbstractDaemonTest {
countsByGroup.clear();
}
- long getCount(AccountGroup.UUID groupUuid) {
- return countsByGroup.get(groupUuid.get());
- }
-
void assertReindexOf(AccountGroup.UUID groupUuid) {
assertReindexOf(ImmutableList.of(groupUuid));
}
void assertReindexOf(List<AccountGroup.UUID> groupUuids) {
- for (AccountGroup.UUID groupUuid : groupUuids) {
- assertThat(getCount(groupUuid)).named(groupUuid.get()).isEqualTo(1);
- }
- assertThat(countsByGroup).hasSize(groupUuids.size());
+ Map<String, Long> expected = groupUuids.stream().collect(toMap(u -> u.get(), u -> 1L));
+ assertThat(countsByGroup.asMap()).containsExactlyEntriesIn(expected);
clear();
}
void assertNoReindex() {
- assertThat(countsByGroup).isEmpty();
+ assertThat(countsByGroup.asMap()).isEmpty();
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
index 5e143c0d8b..6fcca8c68c 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
@@ -15,13 +15,14 @@
package com.google.gerrit.acceptance.api.group;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.group.db.GroupsUpdate;
@@ -36,12 +37,9 @@ import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
public class GroupsUpdateIT {
@Rule public InMemoryTestEnvironment testEnvironment = new InMemoryTestEnvironment();
- @Rule public ExpectedException expectedException = ExpectedException.none();
-
@Inject @ServerInitiated private Provider<GroupsUpdate> groupsUpdateProvider;
@Inject private Groups groups;
@@ -56,7 +54,7 @@ public class GroupsUpdateIT {
createGroup(groupCreation, groupUpdate);
Stream<String> allGroupNames = getAllGroupNames();
- assertThat(allGroupNames).containsAllOf("users", "verifiers");
+ assertThat(allGroupNames).containsAtLeast("users", "verifiers");
}
@Test
@@ -65,23 +63,23 @@ public class GroupsUpdateIT {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("contributors"))
+ .setName(AccountGroup.nameKey("contributors"))
.setMemberModification(
new CreateAnotherGroupOnceAsSideEffectOfMemberModification("verifiers"))
.build();
- updateGroup(new AccountGroup.UUID("users-UUID"), groupUpdate);
+ updateGroup(AccountGroup.uuid("users-UUID"), groupUpdate);
Stream<String> allGroupNames = getAllGroupNames();
- assertThat(allGroupNames).containsAllOf("contributors", "verifiers");
+ assertThat(allGroupNames).containsAtLeast("contributors", "verifiers");
}
@Test
public void groupUpdateFailsWithExceptionForNotExistingGroup() throws Exception {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setDescription("A description for the group").build();
-
- expectedException.expect(NoSuchGroupException.class);
- updateGroup(new AccountGroup.UUID("nonexistent-group-UUID"), groupUpdate);
+ assertThrows(
+ NoSuchGroupException.class,
+ () -> updateGroup(AccountGroup.uuid("nonexistent-group-UUID"), groupUpdate));
}
private void createGroup(String groupName, String groupUuid) throws Exception {
@@ -107,9 +105,9 @@ public class GroupsUpdateIT {
private static InternalGroupCreation getGroupCreation(String groupName, String groupUuid) {
return InternalGroupCreation.builder()
- .setGroupUUID(new AccountGroup.UUID(groupUuid))
- .setNameKey(new AccountGroup.NameKey(groupName))
- .setId(new AccountGroup.Id(Math.abs(groupName.hashCode())))
+ .setGroupUUID(AccountGroup.uuid(groupUuid))
+ .setNameKey(AccountGroup.nameKey(groupName))
+ .setId(AccountGroup.id(Math.abs(groupName.hashCode())))
.build();
}
diff --git a/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java b/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
index 2e455528c3..67da084fc6 100644
--- a/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.api.plugin;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
@@ -34,6 +35,7 @@ import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.plugins.MandatoryPluginsCollection;
import com.google.inject.Inject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -64,6 +66,7 @@ public class PluginIT extends AbstractDaemonTest {
"plugin_e.js");
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private MandatoryPluginsCollection mandatoryPluginsCollection;
@Test
@GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
@@ -111,7 +114,13 @@ public class PluginIT extends AbstractDaemonTest {
assertBadRequest(list().regex(".*in-b").prefix("a"));
assertBadRequest(list().substring(".*in-b").prefix("a"));
- // Disable
+ // Disable mandatory
+ mandatoryPluginsCollection.add("plugin_e");
+ assertThrows(MethodNotAllowedException.class, () -> gApi.plugins().name("plugin_e").disable());
+ api = gApi.plugins().name("plugin_e");
+ assertThat(api.get().disabled).isNull();
+
+ // Disable non-mandatory
api = gApi.plugins().name("plugin-a");
api.disable();
api = gApi.plugins().name("plugin-a");
@@ -130,12 +139,7 @@ public class PluginIT extends AbstractDaemonTest {
// Non-admin cannot disable
requestScopeOperations.setApiUser(user.id());
- try {
- gApi.plugins().name("plugin-a").disable();
- fail("Expected AuthException");
- } catch (AuthException expected) {
- // Expected
- }
+ assertThrows(AuthException.class, () -> gApi.plugins().name("plugin-a").disable());
}
@SuppressWarnings("deprecation")
@@ -149,15 +153,16 @@ public class PluginIT extends AbstractDaemonTest {
@Test
public void installNotAllowed() throws Exception {
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("remote plugin administration is disabled");
- gApi.plugins().install("test.js", new InstallPluginInput());
+ MethodNotAllowedException thrown =
+ assertThrows(
+ MethodNotAllowedException.class,
+ () -> gApi.plugins().install("test.js", new InstallPluginInput()));
+ assertThat(thrown).hasMessageThat().contains("remote plugin administration is disabled");
}
@Test
public void getNonExistingThrowsNotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.plugins().name("does-not-exist");
+ assertThrows(ResourceNotFoundException.class, () -> gApi.plugins().name("does-not-exist"));
}
private ListRequest list() throws RestApiException {
@@ -223,11 +228,6 @@ public class PluginIT extends AbstractDaemonTest {
}
private void assertBadRequest(ListRequest req) throws Exception {
- try {
- req.get();
- fail("Expected BadRequestException");
- } catch (BadRequestException e) {
- // Expected
- }
+ assertThrows(BadRequestException.class, () -> req.get());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/plugin/PluginLoaderIT.java b/javatests/com/google/gerrit/acceptance/api/plugin/PluginLoaderIT.java
new file mode 100644
index 0000000000..7eb3680659
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/plugin/PluginLoaderIT.java
@@ -0,0 +1,42 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.api.plugin;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.server.plugins.MissingMandatoryPluginsException;
+import org.junit.Test;
+import org.junit.runner.Description;
+
+@NoHttpd
+public class PluginLoaderIT extends AbstractDaemonTest {
+
+ Description testDescription;
+
+ @Override
+ protected void beforeTest(Description description) throws Exception {
+ this.testDescription = description;
+ }
+
+ @Override
+ protected void afterTest() throws Exception {}
+
+ @Test(expected = MissingMandatoryPluginsException.class)
+ @GerritConfig(name = "plugins.mandatory", value = "my-mandatory-plugin")
+ public void shouldFailToStartGerritWhenMandatoryPluginsAreMissing() throws Exception {
+ super.beforeTest(testDescription);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
index 8cdd2f66e2..b8c1818c1a 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
@@ -15,6 +15,11 @@
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -23,17 +28,15 @@ import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.config.AccessCheckInfo;
import com.google.gerrit.extensions.api.config.AccessCheckInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.testing.Util;
import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.lib.RefUpdate;
@@ -62,36 +65,41 @@ public class CheckAccessIT extends AbstractDaemonTest {
privilegedUser = accountCreator.create("privilegedUser", "snowden@nsa.gov", "Ed Snowden");
groupOperations.group(privilegedGroupUuid).forUpdate().addMember(privilegedUser.id()).update();
- try (ProjectConfigUpdate u = updateProject(secretProject)) {
- ProjectConfig cfg = u.getConfig();
- Util.allow(cfg, Permission.READ, privilegedGroupUuid, "refs/*");
- Util.block(cfg, Permission.READ, SystemGroupBackend.REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(secretProject)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(privilegedGroupUuid))
+ .add(block(Permission.READ).ref("refs/*").group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
- try (ProjectConfigUpdate u = updateProject(secretRefProject)) {
- ProjectConfig cfg = u.getConfig();
- Util.deny(cfg, Permission.READ, SystemGroupBackend.ANONYMOUS_USERS, "refs/*");
- Util.allow(cfg, Permission.READ, privilegedGroupUuid, "refs/heads/secret/*");
- Util.block(cfg, Permission.READ, SystemGroupBackend.REGISTERED_USERS, "refs/heads/secret/*");
- Util.allow(cfg, Permission.READ, SystemGroupBackend.REGISTERED_USERS, "refs/heads/*");
- u.save();
- }
+ projectOperations
+ .project(secretRefProject)
+ .forUpdate()
+ .add(deny(Permission.READ).ref("refs/*").group(SystemGroupBackend.ANONYMOUS_USERS))
+ .add(allow(Permission.READ).ref("refs/heads/secret/*").group(privilegedGroupUuid))
+ .add(
+ block(Permission.READ)
+ .ref("refs/heads/secret/*")
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .add(allow(Permission.READ).ref("refs/heads/*").group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
// Ref permission
- try (ProjectConfigUpdate u = updateProject(normalProject)) {
- ProjectConfig cfg = u.getConfig();
- Util.allow(cfg, Permission.VIEW_PRIVATE_CHANGES, privilegedGroupUuid, "refs/*");
- Util.allow(cfg, Permission.FORGE_SERVER, privilegedGroupUuid, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(normalProject)
+ .forUpdate()
+ .add(allow(Permission.VIEW_PRIVATE_CHANGES).ref("refs/*").group(privilegedGroupUuid))
+ .add(allow(Permission.FORGE_SERVER).ref("refs/*").group(privilegedGroupUuid))
+ .update();
}
@Test
public void emptyInput() throws Exception {
- exception.expect(BadRequestException.class);
- exception.expectMessage("input requires 'account'");
- gApi.projects().name(normalProject.get()).checkAccess(new AccessCheckInput());
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.projects().name(normalProject.get()).checkAccess(new AccessCheckInput()));
+ assertThat(thrown).hasMessageThat().contains("input requires 'account'");
}
@Test
@@ -101,9 +109,11 @@ public class CheckAccessIT extends AbstractDaemonTest {
in.permission = "notapermission";
in.ref = "refs/heads/master";
- exception.expect(BadRequestException.class);
- exception.expectMessage("not recognized");
- gApi.projects().name(normalProject.get()).checkAccess(in);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.projects().name(normalProject.get()).checkAccess(in));
+ assertThat(thrown).hasMessageThat().contains("not recognized");
}
@Test
@@ -112,9 +122,11 @@ public class CheckAccessIT extends AbstractDaemonTest {
in.account = user.email();
in.permission = "forge_author";
- exception.expect(BadRequestException.class);
- exception.expectMessage("must set 'ref'");
- gApi.projects().name(normalProject.get()).checkAccess(in);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.projects().name(normalProject.get()).checkAccess(in));
+ assertThat(thrown).hasMessageThat().contains("must set 'ref'");
}
@Test
@@ -124,9 +136,11 @@ public class CheckAccessIT extends AbstractDaemonTest {
in.permission = "rebase";
in.ref = "refs/heads/master";
- exception.expect(BadRequestException.class);
- exception.expectMessage("recognized as ref permission");
- gApi.projects().name(normalProject.get()).checkAccess(in);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.projects().name(normalProject.get()).checkAccess(in));
+ assertThat(thrown).hasMessageThat().contains("recognized as ref permission");
}
@Test
@@ -136,9 +150,11 @@ public class CheckAccessIT extends AbstractDaemonTest {
in.permission = "rebase";
in.ref = "refs/heads/master";
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("Account 'doesnotexist@invalid.com' not found");
- gApi.projects().name(normalProject.get()).checkAccess(in);
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.projects().name(normalProject.get()).checkAccess(in));
+ assertThat(thrown).hasMessageThat().contains("Account 'doesnotexist@invalid.com' not found");
}
private static class TestCase {
@@ -231,13 +247,16 @@ public class CheckAccessIT extends AbstractDaemonTest {
try {
info = gApi.projects().name(tc.project).checkAccess(tc.input);
} catch (RestApiException e) {
- fail(String.format("check.access(%s, %s): exception %s", tc.project, in, e));
+ assertWithMessage(String.format("check.access(%s, %s): exception %s", tc.project, in, e))
+ .fail();
}
int want = tc.want;
if (want != info.status) {
- fail(
- String.format("check.access(%s, %s) = %d, want %d", tc.project, in, info.status, want));
+ assertWithMessage(
+ String.format(
+ "check.access(%s, %s) = %d, want %d", tc.project, in, info.status, want))
+ .fail();
}
switch (want) {
@@ -253,7 +272,7 @@ public class CheckAccessIT extends AbstractDaemonTest {
assertThat(info.message).isNull();
break;
default:
- fail(String.format("unknown code %d", want));
+ assertWithMessage(String.format("unknown code %d", want)).fail();
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
index 388ea30a1d..27dd16afe9 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
@@ -16,6 +16,7 @@ package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
@@ -50,7 +51,7 @@ public class CheckProjectIT extends AbstractDaemonTest {
@Test
public void noProblem() throws Exception {
PushOneCommit.Result r = createChange("refs/for/master");
- String branch = r.getChange().change().getDest().get();
+ String branch = r.getChange().change().getDest().branch();
ChangeInfo info = gApi.changes().id(r.getChange().getId().get()).info();
assertThat(info.status).isEqualTo(ChangeStatus.NEW);
@@ -115,7 +116,7 @@ public class CheckProjectIT extends AbstractDaemonTest {
@Test
public void detectAutoCloseableChangeByChangeId() throws Exception {
PushOneCommit.Result r = createChange("refs/for/master");
- String branch = r.getChange().change().getDest().get();
+ String branch = r.getChange().change().getDest().branch();
RevCommit amendedCommit = serverSideTestRepo.amend(r.getCommit()).create();
serverSideTestRepo.branch(branch).update(amendedCommit);
@@ -138,7 +139,7 @@ public class CheckProjectIT extends AbstractDaemonTest {
@Test
public void fixAutoCloseableChangeByChangeId() throws Exception {
PushOneCommit.Result r = createChange("refs/for/master");
- String branch = r.getChange().change().getDest().get();
+ String branch = r.getChange().change().getDest().branch();
RevCommit amendedCommit = serverSideTestRepo.amend(r.getCommit()).create();
serverSideTestRepo.branch(branch).update(amendedCommit);
@@ -162,7 +163,7 @@ public class CheckProjectIT extends AbstractDaemonTest {
@Test
public void maxCommits() throws Exception {
PushOneCommit.Result r = createChange("refs/for/master");
- String branch = r.getChange().change().getDest().get();
+ String branch = r.getChange().change().getDest().branch();
RevCommit amendedCommit = serverSideTestRepo.amend(r.getCommit()).create();
serverSideTestRepo.branch(branch).update(amendedCommit);
@@ -196,7 +197,7 @@ public class CheckProjectIT extends AbstractDaemonTest {
@Test
public void skipCommits() throws Exception {
PushOneCommit.Result r = createChange("refs/for/master");
- String branch = r.getChange().change().getDest().get();
+ String branch = r.getChange().change().getDest().branch();
RevCommit amendedCommit = serverSideTestRepo.amend(r.getCommit()).create();
serverSideTestRepo.branch(branch).update(amendedCommit);
@@ -232,18 +233,21 @@ public class CheckProjectIT extends AbstractDaemonTest {
CheckProjectInput input = new CheckProjectInput();
input.autoCloseableChangesCheck = new AutoCloseableChangesCheckInput();
- exception.expect(BadRequestException.class);
- exception.expectMessage("branch is required");
- gApi.projects().name(project.get()).check(input);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> gApi.projects().name(project.get()).check(input));
+ assertThat(thrown).hasMessageThat().contains("branch is required");
}
@Test
public void nonExistingBranch() throws Exception {
CheckProjectInput input = checkProjectInputForAutoCloseableCheck("non-existing");
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("branch 'non-existing' not found");
- gApi.projects().name(project.get()).check(input);
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.projects().name(project.get()).check(input));
+ assertThat(thrown).hasMessageThat().contains("branch 'non-existing' not found");
}
@Test
@@ -266,11 +270,14 @@ public class CheckProjectIT extends AbstractDaemonTest {
input.autoCloseableChangesCheck.maxCommits =
ProjectsConsistencyChecker.AUTO_CLOSE_MAX_COMMITS_LIMIT + 1;
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- "max commits can at most be set to "
- + ProjectsConsistencyChecker.AUTO_CLOSE_MAX_COMMITS_LIMIT);
- gApi.projects().name(project.get()).check(input);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> gApi.projects().name(project.get()).check(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "max commits can at most be set to "
+ + ProjectsConsistencyChecker.AUTO_CLOSE_MAX_COMMITS_LIMIT);
}
private RevCommit pushCommitWithoutChangeIdForReview() throws Exception {
@@ -280,10 +287,12 @@ public class CheckProjectIT extends AbstractDaemonTest {
.branch("HEAD")
.commit()
.message("A change")
+ .insertChangeId()
.author(admin.newIdent())
.committer(new PersonIdent(admin.newIdent(), testRepo.getDate()))
.create();
pushHead(testRepo, "refs/for/master");
+
return commit;
}
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java b/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
index a341b3c64c..04625c58df 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -22,7 +23,9 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -33,7 +36,7 @@ import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.GitPerson;
import com.google.gerrit.extensions.common.RevisionInfo;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.inject.Inject;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jgit.lib.ObjectId;
@@ -42,6 +45,8 @@ import org.junit.Test;
@NoHttpd
public class CommitIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+
@Test
public void getCommitInfo() throws Exception {
Result result = createChange();
@@ -75,22 +80,46 @@ public class CommitIT extends AbstractDaemonTest {
assertThat(getIncludedIn(result.getCommit().getId()).branches).containsExactly("master");
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
- grant(project, R_TAGS + "*", Permission.CREATE_TAG);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE_TAG).ref(R_TAGS + "*").group(adminGroupUuid()))
+ .update();
gApi.projects().name(result.getChange().project().get()).tag("test-tag").create(new TagInput());
assertThat(getIncludedIn(result.getCommit().getId()).tags).containsExactly("test-tag");
- createBranch(new Branch.NameKey(project.get(), "test-branch"));
+ createBranch(BranchNameKey.create(project, "test-branch"));
assertThat(getIncludedIn(result.getCommit().getId()).branches)
.containsExactly("master", "test-branch");
}
@Test
+ public void cherryPickWithoutMessage() throws Exception {
+ String branch = "foo";
+
+ // Create change to cherry-pick
+ RevCommit revCommit = createChange().getCommit();
+
+ // Create target branch to cherry-pick to.
+ gApi.projects().name(project.get()).branch(branch).create(new BranchInput());
+
+ // Cherry-pick without message.
+ CherryPickInput input = new CherryPickInput();
+ input.destination = branch;
+ String changeId =
+ gApi.projects().name(project.get()).commit(revCommit.name()).cherryPick(input).get().id;
+
+ // Expect that the message of the cherry-picked commit was used for the cherry-pick change.
+ ChangeInfo changeInfo = gApi.changes().id(changeId).get();
+ RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
+ assertThat(revInfo).isNotNull();
+ assertThat(revInfo.commit.message).isEqualTo(revCommit.getFullMessage());
+ }
+
+ @Test
public void cherryPickCommitWithoutChangeId() throws Exception {
- // This test is a little superfluous, since the current cherry-pick code ignores
- // the commit message of the to-be-cherry-picked change, using the one in
- // CherryPickInput instead.
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
input.message = "it goes to foo branch";
diff --git a/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java b/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
index f597392a5a..6442645563 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
@@ -15,12 +15,15 @@
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.DashboardInfo;
@@ -31,6 +34,7 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.restapi.project.DashboardsCollection;
+import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Repository;
@@ -40,21 +44,25 @@ import org.junit.Test;
@NoHttpd
public class DashboardIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+
@Before
public void setup() throws Exception {
- allow("refs/meta/dashboards/*", Permission.CREATE, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref("refs/meta/dashboards/*").group(REGISTERED_USERS))
+ .update();
}
@Test
public void defaultDashboardDoesNotExist() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- project().defaultDashboard().get();
+ assertThrows(ResourceNotFoundException.class, () -> project().defaultDashboard().get());
}
@Test
public void dashboardDoesNotExist() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- project().dashboard("my:dashboard").get();
+ assertThrows(ResourceNotFoundException.class, () -> project().dashboard("my:dashboard").get());
}
@Test
@@ -110,8 +118,7 @@ public class DashboardIT extends AbstractDaemonTest {
project().removeDefaultDashboard();
assertThat(project().dashboard(info.id).get().isDefault).isNull();
- exception.expect(ResourceNotFoundException.class);
- project().defaultDashboard().get();
+ assertThrows(ResourceNotFoundException.class, () -> project().defaultDashboard().get());
}
@Test
@@ -133,9 +140,9 @@ public class DashboardIT extends AbstractDaemonTest {
@Test
public void cannotGetDashboardWithInheritedForNonDefault() throws Exception {
DashboardInfo info = createTestDashboard();
- exception.expect(BadRequestException.class);
- exception.expectMessage("inherited flag can only be used with default");
- project().dashboard(info.id).get(true);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> project().dashboard(info.id).get(true));
+ assertThat(thrown).hasMessageThat().contains("inherited flag can only be used with default");
}
private void assertDashboardInfo(DashboardInfo actual, DashboardInfo expected) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
index 766f8537b1..7f00930b74 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -15,17 +15,24 @@
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.server.project.ProjectState.INHERITED_FROM_GLOBAL;
import static com.google.gerrit.server.project.ProjectState.INHERITED_FROM_PARENT;
import static com.google.gerrit.server.project.ProjectState.OVERRIDDEN_BY_GLOBAL;
import static com.google.gerrit.server.project.ProjectState.OVERRIDDEN_BY_PARENT;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toSet;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.AtomicLongMap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.NoHttpd;
@@ -33,6 +40,9 @@ import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
@@ -45,15 +55,12 @@ import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.events.ChangeIndexedListener;
import com.google.gerrit.extensions.events.ProjectIndexedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.CommentLinkInfoImpl;
@@ -66,16 +73,10 @@ import java.util.Map;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
@NoHttpd
@@ -87,12 +88,9 @@ public class ProjectIT extends AbstractDaemonTest {
private static final String JIRA_LINK = "http://jira.example.com/?id=$2";
private static final String JIRA_MATCH = "(jira\\\\s+#?)(\\\\d+)";
- @Inject private DynamicSet<ProjectIndexedListener> projectIndexedListeners;
@Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
-
- private ProjectIndexedCounter projectIndexedCounter;
- private RegistrationHandle projectIndexedCounterHandle;
+ @Inject private ExtensionRegistry extensionRegistry;
@Override
public Module createModule() {
@@ -106,53 +104,49 @@ public class ProjectIT extends AbstractDaemonTest {
};
}
- @Before
- public void addProjectIndexedCounter() {
- projectIndexedCounter = new ProjectIndexedCounter();
- projectIndexedCounterHandle = projectIndexedListeners.add("gerrit", projectIndexedCounter);
- }
-
- @After
- public void removeProjectIndexedCounter() {
- if (projectIndexedCounterHandle != null) {
- projectIndexedCounterHandle.remove();
- }
- }
-
@Test
public void createProject() throws Exception {
- String name = name("foo");
- assertThat(gApi.projects().create(name).get().name).isEqualTo(name);
+ ProjectIndexedCounter projectIndexedCounter = new ProjectIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectIndexedCounter)) {
+ String name = name("foo");
+ assertThat(gApi.projects().create(name).get().name).isEqualTo(name);
- RevCommit head = getRemoteHead(name, RefNames.REFS_CONFIG);
- eventRecorder.assertRefUpdatedEvents(name, RefNames.REFS_CONFIG, null, head);
+ RevCommit head = getRemoteHead(name, RefNames.REFS_CONFIG);
+ eventRecorder.assertRefUpdatedEvents(name, RefNames.REFS_CONFIG, null, head);
- eventRecorder.assertRefUpdatedEvents(name, "refs/heads/master", new String[] {});
- projectIndexedCounter.assertReindexOf(name);
+ eventRecorder.assertRefUpdatedEvents(name, "refs/heads/master", new String[] {});
+ projectIndexedCounter.assertReindexOf(name);
+ }
}
@Test
public void createProjectWithInitialBranches() throws Exception {
- String name = name("foo");
- ProjectInput input = new ProjectInput();
- input.name = name;
- input.createEmptyCommit = true;
- input.branches = ImmutableList.of("master", "foo");
- assertThat(gApi.projects().create(input).get().name).isEqualTo(name);
- assertThat(
- gApi.projects().name(name).branches().get().stream().map(b -> b.ref).collect(toSet()))
- .containsExactly("refs/heads/foo", "refs/heads/master", "HEAD", RefNames.REFS_CONFIG);
-
- RevCommit head = getRemoteHead(name, RefNames.REFS_CONFIG);
- eventRecorder.assertRefUpdatedEvents(name, RefNames.REFS_CONFIG, null, head);
-
- head = getRemoteHead(name, "refs/heads/foo");
- eventRecorder.assertRefUpdatedEvents(name, "refs/heads/foo", null, head);
-
- head = getRemoteHead(name, "refs/heads/master");
- eventRecorder.assertRefUpdatedEvents(name, "refs/heads/master", null, head);
-
- projectIndexedCounter.assertReindexOf(name);
+ ProjectIndexedCounter projectIndexedCounter = new ProjectIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectIndexedCounter)) {
+
+ String name = name("foo");
+ ProjectInput input = new ProjectInput();
+ input.name = name;
+ input.createEmptyCommit = true;
+ input.branches = ImmutableList.of("master", "foo");
+ assertThat(gApi.projects().create(input).get().name).isEqualTo(name);
+ assertThat(
+ gApi.projects().name(name).branches().get().stream().map(b -> b.ref).collect(toSet()))
+ .containsExactly("refs/heads/foo", "refs/heads/master", "HEAD", RefNames.REFS_CONFIG);
+
+ RevCommit head = getRemoteHead(name, RefNames.REFS_CONFIG);
+ eventRecorder.assertRefUpdatedEvents(name, RefNames.REFS_CONFIG, null, head);
+
+ head = getRemoteHead(name, "refs/heads/foo");
+ eventRecorder.assertRefUpdatedEvents(name, "refs/heads/foo", null, head);
+
+ head = getRemoteHead(name, "refs/heads/master");
+ eventRecorder.assertRefUpdatedEvents(name, "refs/heads/master", null, head);
+
+ projectIndexedCounter.assertReindexOf(name);
+ }
}
@Test
@@ -196,17 +190,17 @@ public class ProjectIT extends AbstractDaemonTest {
public void createProjectWithMismatchedInput() throws Exception {
ProjectInput in = new ProjectInput();
in.name = name("foo");
- exception.expect(BadRequestException.class);
- exception.expectMessage("name must match input.name");
- gApi.projects().name("bar").create(in);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.projects().name("bar").create(in));
+ assertThat(thrown).hasMessageThat().contains("name must match input.name");
}
@Test
public void createProjectNoNameInInput() throws Exception {
ProjectInput in = new ProjectInput();
- exception.expect(BadRequestException.class);
- exception.expectMessage("input.name is required");
- gApi.projects().create(in);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> gApi.projects().create(in));
+ assertThat(thrown).hasMessageThat().contains("input.name is required");
}
@Test
@@ -214,9 +208,9 @@ public class ProjectIT extends AbstractDaemonTest {
ProjectInput in = new ProjectInput();
in.name = name("baz");
gApi.projects().create(in);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Project already exists");
- gApi.projects().create(in);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.projects().create(in));
+ assertThat(thrown).hasMessageThat().contains("Project already exists");
}
@Test
@@ -225,9 +219,9 @@ public class ProjectIT extends AbstractDaemonTest {
in.name = name("baz");
in.parent = "non-existing";
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("Project Not Found: " + in.parent);
- gApi.projects().create(in);
+ UnprocessableEntityException thrown =
+ assertThrows(UnprocessableEntityException.class, () -> gApi.projects().create(in));
+ assertThat(thrown).hasMessageThat().contains("Project Not Found: " + in.parent);
}
@Test
@@ -236,9 +230,9 @@ public class ProjectIT extends AbstractDaemonTest {
in.name = name("baz");
in.parent = in.name;
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("Project Not Found: " + in.parent);
- gApi.projects().create(in);
+ UnprocessableEntityException thrown =
+ assertThrows(UnprocessableEntityException.class, () -> gApi.projects().create(in));
+ assertThat(thrown).hasMessageThat().contains("Project Not Found: " + in.parent);
}
@Test
@@ -246,52 +240,67 @@ public class ProjectIT extends AbstractDaemonTest {
ProjectInput in = new ProjectInput();
in.name = name("foo");
in.parent = allUsers.get();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(String.format("Cannot inherit from '%s' project", allUsers.get()));
- gApi.projects().create(in);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.projects().create(in));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("Cannot inherit from '%s' project", allUsers.get()));
}
@Test
public void createAndDeleteBranch() throws Exception {
- assertThat(hasHead(project, "foo")).isFalse();
+ ProjectIndexedCounter projectIndexedCounter = new ProjectIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectIndexedCounter)) {
+
+ assertThat(hasHead(project, "foo")).isFalse();
- gApi.projects().name(project.get()).branch("foo").create(new BranchInput());
- assertThat(getRemoteHead(project.get(), "foo")).isNotNull();
- projectIndexedCounter.assertNoReindex();
+ gApi.projects().name(project.get()).branch("foo").create(new BranchInput());
+ assertThat(getRemoteHead(project.get(), "foo")).isNotNull();
+ projectIndexedCounter.assertNoReindex();
- gApi.projects().name(project.get()).branch("foo").delete();
- assertThat(hasHead(project, "foo")).isFalse();
- projectIndexedCounter.assertNoReindex();
+ gApi.projects().name(project.get()).branch("foo").delete();
+ assertThat(hasHead(project, "foo")).isFalse();
+ projectIndexedCounter.assertNoReindex();
+ }
}
@Test
public void createAndDeleteBranchByPush() throws Exception {
- grant(project, "refs/*", Permission.PUSH, true);
- projectIndexedCounter.clear();
-
- assertThat(hasHead(project, "foo")).isFalse();
-
- PushOneCommit.Result r = pushTo("refs/heads/foo");
- r.assertOkStatus();
- assertThat(getRemoteHead(project.get(), "foo")).isEqualTo(r.getCommit());
- projectIndexedCounter.assertNoReindex();
-
- PushResult r2 = GitUtil.pushOne(testRepo, null, "refs/heads/foo", false, true, null);
- assertThat(r2.getRemoteUpdate("refs/heads/foo").getStatus()).isEqualTo(Status.OK);
- assertThat(hasHead(project, "foo")).isFalse();
- projectIndexedCounter.assertNoReindex();
+ ProjectIndexedCounter projectIndexedCounter = new ProjectIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectIndexedCounter)) {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/*").group(adminGroupUuid()).force(true))
+ .update();
+ projectIndexedCounter.clear();
+
+ assertThat(hasHead(project, "foo")).isFalse();
+
+ PushOneCommit.Result r = pushTo("refs/heads/foo");
+ r.assertOkStatus();
+ assertThat(getRemoteHead(project.get(), "foo")).isEqualTo(r.getCommit());
+ projectIndexedCounter.assertNoReindex();
+
+ PushResult r2 = GitUtil.pushOne(testRepo, null, "refs/heads/foo", false, true, null);
+ assertThat(r2.getRemoteUpdate("refs/heads/foo").getStatus()).isEqualTo(Status.OK);
+ assertThat(hasHead(project, "foo")).isFalse();
+ projectIndexedCounter.assertNoReindex();
+ }
}
@Test
public void descriptionChangeCausesRefUpdate() throws Exception {
- RevCommit initialHead = getRemoteHead(project, RefNames.REFS_CONFIG);
+ RevCommit initialHead = projectOperations.project(project).getHead(RefNames.REFS_CONFIG);
assertThat(gApi.projects().name(project.get()).description()).isEmpty();
DescriptionInput in = new DescriptionInput();
in.description = "new project description";
gApi.projects().name(project.get()).description(in);
assertThat(gApi.projects().name(project.get()).description()).isEqualTo(in.description);
- RevCommit updatedHead = getRemoteHead(project, RefNames.REFS_CONFIG);
+ RevCommit updatedHead = projectOperations.project(project).getHead(RefNames.REFS_CONFIG);
eventRecorder.assertRefUpdatedEvents(
project.get(), RefNames.REFS_CONFIG, initialHead, updatedHead);
}
@@ -310,7 +319,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void configChangeCausesRefUpdate() throws Exception {
- RevCommit initialHead = getRemoteHead(project, RefNames.REFS_CONFIG);
+ RevCommit initialHead = projectOperations.project(project).getHead(RefNames.REFS_CONFIG);
ConfigInfo info = gApi.projects().name(project.get()).config();
assertThat(info.defaultSubmitType.value).isEqualTo(SubmitType.MERGE_IF_NECESSARY);
@@ -321,7 +330,7 @@ public class ProjectIT extends AbstractDaemonTest {
info = gApi.projects().name(project.get()).config();
assertThat(info.defaultSubmitType.value).isEqualTo(SubmitType.CHERRY_PICK);
- RevCommit updatedHead = getRemoteHead(project, RefNames.REFS_CONFIG);
+ RevCommit updatedHead = projectOperations.project(project).getHead(RefNames.REFS_CONFIG);
eventRecorder.assertRefUpdatedEvents(
project.get(), RefNames.REFS_CONFIG, initialHead, updatedHead);
}
@@ -385,9 +394,9 @@ public class ProjectIT extends AbstractDaemonTest {
public void nonOwnerCannotSetConfig() throws Exception {
ConfigInput input = createTestConfigInput();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("write refs/meta/config not permitted");
- gApi.projects().name(project.get()).config(input);
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.projects().name(project.get()).config(input));
+ assertThat(thrown).hasMessageThat().contains("write refs/meta/config not permitted");
}
@Test
@@ -403,8 +412,9 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void setHeadToNonexistentBranch() throws Exception {
- exception.expect(UnprocessableEntityException.class);
- gApi.projects().name(project.get()).head("does-not-exist");
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.projects().name(project.get()).head("does-not-exist"));
}
@Test
@@ -420,9 +430,9 @@ public class ProjectIT extends AbstractDaemonTest {
public void setHeadNotAllowed() throws Exception {
gApi.projects().name(project.get()).branch("test").create(new BranchInput());
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("not permitted: set HEAD on refs/heads/test");
- gApi.projects().name(project.get()).head("test");
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> gApi.projects().name(project.get()).head("test"));
+ assertThat(thrown).hasMessageThat().contains("not permitted: set HEAD on refs/heads/test");
}
@Test
@@ -452,8 +462,18 @@ public class ProjectIT extends AbstractDaemonTest {
assertThat(gApi.projects().name(project.get()).config().state).isEqualTo(ProjectState.HIDDEN);
// Revoke OWNER permission for admin and block them from reading the project's refs
- block(project, RefNames.REFS + "*", Permission.OWNER, SystemGroupBackend.REGISTERED_USERS);
- block(project, RefNames.REFS + "*", Permission.READ, SystemGroupBackend.REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ block(Permission.OWNER)
+ .ref(RefNames.REFS + "*")
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .add(
+ block(Permission.READ)
+ .ref(RefNames.REFS + "*")
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
// HIDDEN => ACTIVE
ConfigInput ci2 = new ConfigInput();
@@ -465,22 +485,49 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void reindexProject() throws Exception {
- projectOperations.newProject().parent(project).create();
- projectIndexedCounter.clear();
+ ProjectIndexedCounter projectIndexedCounter = new ProjectIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectIndexedCounter)) {
+
+ projectOperations.newProject().parent(project).create();
+ projectIndexedCounter.clear();
- gApi.projects().name(allProjects.get()).index(false);
- projectIndexedCounter.assertReindexOf(allProjects.get());
+ gApi.projects().name(allProjects.get()).index(false);
+ projectIndexedCounter.assertReindexOf(allProjects.get());
+ }
}
@Test
public void reindexProjectWithChildren() throws Exception {
- Project.NameKey middle = projectOperations.newProject().parent(project).create();
- Project.NameKey leave = projectOperations.newProject().parent(middle).create();
- projectIndexedCounter.clear();
+ ProjectIndexedCounter projectIndexedCounter = new ProjectIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectIndexedCounter)) {
+
+ Project.NameKey middle = projectOperations.newProject().parent(project).create();
+ Project.NameKey leave = projectOperations.newProject().parent(middle).create();
+ projectIndexedCounter.clear();
- gApi.projects().name(project.get()).index(true);
- projectIndexedCounter.assertReindexExactly(
- ImmutableMap.of(project.get(), 1L, middle.get(), 1L, leave.get(), 1L));
+ gApi.projects().name(project.get()).index(true);
+ projectIndexedCounter.assertReindexExactly(
+ ImmutableMap.of(project.get(), 1L, middle.get(), 1L, leave.get(), 1L));
+ }
+ }
+
+ @Test
+ public void reindexChangesOfProject() throws Exception {
+ Change.Id changeId1 = createChange().getChange().getId();
+ Change.Id changeId2 = createChange().getChange().getId();
+
+ ChangeIndexedListener changeIndexedListener = mock(ChangeIndexedListener.class);
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedListener)) {
+ gApi.projects().name(project.get()).indexChanges();
+
+ verify(changeIndexedListener, times(1))
+ .onChangeScheduledForIndexing(project.get(), changeId1.get());
+ verify(changeIndexedListener, times(1))
+ .onChangeScheduledForIndexing(project.get(), changeId2.get());
+ }
}
@Test
@@ -645,9 +692,9 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void invalidMaxObjectSizeIsRejected() throws Exception {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("100 foo");
- setMaxObjectSize("100 foo");
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> setMaxObjectSize("100 foo"));
+ assertThat(thrown).hasMessageThat().contains("100 foo");
}
@Test
@@ -669,7 +716,8 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void cannotPushLabelDefinitionWithDuplicateValues() throws Exception {
- Config cfg = readAllProjectsConfig();
+ Config cfg = new Config();
+ cfg.fromText(projectOperations.project(allProjects).getConfig().toText());
cfg.setStringList(
"label",
"Code-Review",
@@ -692,7 +740,8 @@ public class ProjectIT extends AbstractDaemonTest {
// Update the definition of the Code-Review label so that it has the value "+1 LGTM" twice.
// This update bypasses all validation checks so that the duplicate label value doesn't get
// rejected.
- Config cfg = readAllProjectsConfig();
+ Config cfg = new Config();
+ cfg.fromText(projectOperations.project(allProjects).getConfig().toText());
cfg.setStringList(
"label",
"Code-Review",
@@ -717,20 +766,6 @@ public class ProjectIT extends AbstractDaemonTest {
.containsExactly("+1", "LGTM", " 0", "No Value", "-1", "Looks Bad");
}
- private Config readAllProjectsConfig() throws Exception {
- try (TestRepository<Repository> repo =
- new TestRepository<>(repoManager.openRepository(allProjects))) {
- RevWalk rw = repo.getRevWalk();
- RevTree tree = rw.parseTree(repo.getRepository().resolve("HEAD"));
- RevObject obj = rw.parseAny(repo.get(tree, "project.config"));
- ObjectLoader loader = rw.getObjectReader().open(obj);
- String text = new String(loader.getCachedBytes(), UTF_8);
- Config cfg = new Config();
- cfg.fromText(text);
- return cfg;
- }
- }
-
private CommentLinkInfo commentLinkInfo(String name, String match, String link) {
return new CommentLinkInfoImpl(name, match, link, null /*html*/, null /*enabled*/);
}
@@ -800,22 +835,17 @@ public class ProjectIT extends AbstractDaemonTest {
countsByProject.clear();
}
- long getCount(String projectName) {
- return countsByProject.get(projectName);
- }
-
void assertReindexOf(String projectName) {
assertReindexOf(projectName, 1);
}
- void assertReindexOf(String projectName, int expectedCount) {
- assertThat(getCount(projectName)).isEqualTo(expectedCount);
- assertThat(countsByProject).hasSize(1);
+ void assertReindexOf(String projectName, long expectedCount) {
+ assertThat(countsByProject.asMap()).containsExactly(projectName, expectedCount);
clear();
}
void assertNoReindex() {
- assertThat(countsByProject).isEmpty();
+ assertThat(countsByProject.asMap()).isEmpty();
}
void assertReindexExactly(ImmutableMap<String, Long> expected) {
@@ -825,7 +855,7 @@ public class ProjectIT extends AbstractDaemonTest {
}
protected RevCommit getRemoteHead(String project, String branch) throws Exception {
- return getRemoteHead(new Project.NameKey(project), branch);
+ return projectOperations.project(Project.nameKey(project)).getHead(branch);
}
boolean hasHead(Project.NameKey k, String b) {
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
index 6b511f66e6..019df0e0b4 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
@@ -15,11 +15,14 @@
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.acceptance.GitUtil.fetch;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.RefState;
@@ -28,7 +31,6 @@ import com.google.gerrit.index.project.ProjectIndex;
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.index.query.FieldBundle;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.index.project.StalenessChecker;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.inject.Inject;
@@ -60,7 +62,7 @@ public class ProjectIndexerIT extends AbstractDaemonTest {
Optional<FieldBundle> result =
i.getRaw(project, QueryOptions.create(indexConfig, 0, 1, FIELDS));
- assertThat(result.isPresent()).isTrue();
+ assertThat(result).isPresent();
Iterable<byte[]> refState = result.get().getValue(ProjectField.REF_STATE);
assertThat(refState).isNotEmpty();
@@ -117,15 +119,15 @@ public class ProjectIndexerIT extends AbstractDaemonTest {
private void updateProjectConfigWithoutIndexUpdate(
Project.NameKey project, Consumer<ProjectConfig> update) throws Exception {
- try (AutoCloseable ignored = disableProjectIndex()) {
- try (ProjectConfigUpdate u = updateProject(project)) {
- update.accept(u.getConfig());
- u.save();
- }
- } catch (UnsupportedOperationException e) {
- // Drop, as we just wanted to drop the index update
- return;
- }
- fail("should have a UnsupportedOperationException");
+ assertThrows(
+ UnsupportedOperationException.class,
+ () -> {
+ try (AutoCloseable ignored = disableProjectIndex()) {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ update.accept(u.getConfig());
+ u.save();
+ }
+ }
+ });
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java b/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java
index 3c1428d3ae..cf7aab4798 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java
@@ -15,6 +15,8 @@
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
@@ -22,11 +24,11 @@ import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.inject.Inject;
@@ -34,7 +36,6 @@ import org.junit.Test;
@NoHttpd
public class SetParentIT extends AbstractDaemonTest {
-
@Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@@ -42,8 +43,7 @@ public class SetParentIT extends AbstractDaemonTest {
public void setParentNotAllowed() throws Exception {
String parent = projectOperations.newProject().create().get();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.projects().name(project.get()).parent(parent);
+ assertThrows(AuthException.class, () -> gApi.projects().name(project.get()).parent(parent));
}
@Test
@@ -51,8 +51,7 @@ public class SetParentIT extends AbstractDaemonTest {
public void setParentNotAllowedForNonOwners() throws Exception {
String parent = projectOperations.newProject().create().get();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.projects().name(project.get()).parent(parent);
+ assertThrows(AuthException.class, () -> gApi.projects().name(project.get()).parent(parent));
}
@Test
@@ -75,7 +74,11 @@ public class SetParentIT extends AbstractDaemonTest {
public void setParentAllowedForOwners() throws Exception {
String parent = projectOperations.newProject().create().get();
requestScopeOperations.setApiUser(user.id());
- grant(project, "refs/*", Permission.OWNER, false, SystemGroupBackend.REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
gApi.projects().name(project.get()).parent(parent);
assertThat(gApi.projects().name(project.get()).parent()).isEqualTo(parent);
}
@@ -96,47 +99,63 @@ public class SetParentIT extends AbstractDaemonTest {
@Test
public void setParentForAllProjectsNotAllowed() throws Exception {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("cannot set parent of " + AllProjectsNameProvider.DEFAULT);
- gApi.projects().name(allProjects.get()).parent(project.get());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.projects().name(allProjects.get()).parent(project.get()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("cannot set parent of " + AllProjectsNameProvider.DEFAULT);
}
@Test
public void setParentToSelfNotAllowed() throws Exception {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("cannot set parent to self");
- gApi.projects().name(project.get()).parent(project.get());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.projects().name(project.get()).parent(project.get()));
+ assertThat(thrown).hasMessageThat().contains("cannot set parent to self");
}
@Test
public void setParentToOwnChildNotAllowed() throws Exception {
String child = projectOperations.newProject().parent(project).create().get();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("cycle exists between");
- gApi.projects().name(project.get()).parent(child);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.projects().name(project.get()).parent(child));
+ assertThat(thrown).hasMessageThat().contains("cycle exists between");
}
@Test
public void setParentToGrandchildNotAllowed() throws Exception {
Project.NameKey child = projectOperations.newProject().parent(project).create();
String grandchild = projectOperations.newProject().parent(child).create().get();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("cycle exists between");
- gApi.projects().name(project.get()).parent(grandchild);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.projects().name(project.get()).parent(grandchild));
+ assertThat(thrown).hasMessageThat().contains("cycle exists between");
}
@Test
public void setParentToNonexistentProject() throws Exception {
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("not found");
- gApi.projects().name(project.get()).parent("non-existing");
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.projects().name(project.get()).parent("non-existing"));
+ assertThat(thrown).hasMessageThat().contains("not found");
}
@Test
public void setParentToAllUsersNotAllowed() throws Exception {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(String.format("Cannot inherit from '%s' project", allUsers.get()));
- gApi.projects().name(project.get()).parent(allUsers.get());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.projects().name(project.get()).parent(allUsers.get()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("Cannot inherit from '%s' project", allUsers.get()));
}
@Test
@@ -145,8 +164,9 @@ public class SetParentIT extends AbstractDaemonTest {
String parent = projectOperations.newProject().create().get();
- exception.expect(BadRequestException.class);
- exception.expectMessage("All-Users must inherit from All-Projects");
- gApi.projects().name(allUsers.get()).parent(parent);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> gApi.projects().name(allUsers.get()).parent(parent));
+ assertThat(thrown).hasMessageThat().contains("All-Users must inherit from All-Projects");
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index c27d637edf..d4a4c45073 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -16,10 +16,11 @@ package com.google.gerrit.acceptance.api.revision;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.entities.Patch.COMMIT_MSG;
+import static com.google.gerrit.entities.Patch.MERGE_LIST;
import static com.google.gerrit.extensions.common.testing.DiffInfoSubject.assertThat;
import static com.google.gerrit.extensions.common.testing.FileInfoSubject.assertThat;
-import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
-import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toMap;
@@ -2752,7 +2753,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
RevCommit parentCommit = c.getParents()[0];
String parentCommitId =
- testRepo.getRevWalk().getObjectReader().abbreviate(parentCommit.getId(), 8).name();
+ abbreviateName(parentCommit, 8, testRepo.getRevWalk().getObjectReader());
headers.add("Parent: " + parentCommitId + " (" + parentCommit.getShortMessage() + ")");
SimpleDateFormat dtfmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.US);
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 237560767b..32941ffcdc 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -21,10 +21,12 @@ import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
import static com.google.gerrit.acceptance.PushOneCommit.PATCH;
import static com.google.gerrit.acceptance.PushOneCommit.PATCH_FILE_ONLY;
import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.entities.Patch.COMMIT_MSG;
+import static com.google.gerrit.entities.Patch.MERGE_LIST;
import static com.google.gerrit.extensions.client.ListChangesOption.ALL_REVISIONS;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
-import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
-import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -38,12 +40,20 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.ListMultimap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.DraftApi;
@@ -72,8 +82,6 @@ import com.google.gerrit.extensions.common.MergeableInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.events.ChangeIndexedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
@@ -83,11 +91,6 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.webui.PatchSetWebLink;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.restapi.change.GetRevisionActions;
@@ -115,11 +118,10 @@ import org.eclipse.jgit.transport.RefSpec;
import org.junit.Test;
public class RevisionIT extends AbstractDaemonTest {
-
- @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
- @Inject private DynamicSet<PatchSetWebLink> patchSetLinks;
@Inject private GetRevisionActions getRevisionActions;
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
@Test
public void reviewTriplet() throws Exception {
@@ -182,14 +184,13 @@ public class RevisionIT extends AbstractDaemonTest {
assertThat(approval.postSubmit).isNull();
// Reducing vote is not allowed.
- try {
- gApi.changes().id(changeId).current().review(ReviewInput.dislike());
- fail("expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo("Cannot reduce vote on labels for closed change: Code-Review");
- }
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).current().review(ReviewInput.dislike()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo("Cannot reduce vote on labels for closed change: Code-Review");
approval = getApproval(changeId, label);
assertThat(approval.value).isEqualTo(1);
assertThat(approval.postSubmit).isNull();
@@ -202,14 +203,13 @@ public class RevisionIT extends AbstractDaemonTest {
assertPermitted(gApi.changes().id(changeId).get(DETAILED_LABELS), "Code-Review", 2);
// Decreasing to previous post-submit vote is still not allowed.
- try {
- gApi.changes().id(changeId).current().review(ReviewInput.dislike());
- fail("expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo("Cannot reduce vote on labels for closed change: Code-Review");
- }
+ thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).current().review(ReviewInput.dislike()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo("Cannot reduce vote on labels for closed change: Code-Review");
approval = getApproval(changeId, label);
assertThat(approval.value).isEqualTo(2);
assertThat(approval.postSubmit).isTrue();
@@ -261,9 +261,11 @@ public class RevisionIT extends AbstractDaemonTest {
ReviewInput in = new ReviewInput();
in.label("Code-Review", 0);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Cannot reduce vote on labels for closed change: Code-Review");
- revision(r).review(in);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> revision(r).review(in));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Cannot reduce vote on labels for closed change: Code-Review");
}
@TestProjectInput(submitType = SubmitType.CHERRY_PICK)
@@ -279,28 +281,36 @@ public class RevisionIT extends AbstractDaemonTest {
PatchSetApproval psa =
Iterators.getOnlyElement(
cd.currentApprovals().stream().filter(a -> !a.isLegacySubmit()).iterator());
- assertThat(psa.getPatchSetId().get()).isEqualTo(2);
- assertThat(psa.getLabel()).isEqualTo("Code-Review");
- assertThat(psa.getValue()).isEqualTo(2);
- assertThat(psa.isPostSubmit()).isFalse();
+ assertThat(psa.patchSetId().get()).isEqualTo(2);
+ assertThat(psa.label()).isEqualTo("Code-Review");
+ assertThat(psa.value()).isEqualTo(2);
+ assertThat(psa.postSubmit()).isFalse();
}
@Test
public void voteOnAbandonedChange() throws Exception {
PushOneCommit.Result r = createChange();
gApi.changes().id(r.getChangeId()).abandon();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("change is closed");
- gApi.changes().id(r.getChangeId()).current().review(ReviewInput.reject());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r.getChangeId()).current().review(ReviewInput.reject()));
+ assertThat(thrown).hasMessageThat().contains("change is closed");
}
@Test
public void voteNotAllowedWithoutPermission() throws Exception {
PushOneCommit.Result r = createChange();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("is restricted");
- gApi.changes().id(r.getChange().getId().get()).current().review(ReviewInput.approve());
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () ->
+ gApi.changes()
+ .id(r.getChange().getId().get())
+ .current()
+ .review(ReviewInput.approve()));
+ assertThat(thrown).hasMessageThat().contains("is restricted");
}
@Test
@@ -331,6 +341,34 @@ public class RevisionIT extends AbstractDaemonTest {
}
@Test
+ public void cherryPickWithoutMessage() throws Exception {
+ String branch = "foo";
+
+ // Create change to cherry-pick
+ PushOneCommit.Result change = createChange();
+ RevCommit revCommit = change.getCommit();
+
+ // Create target branch to cherry-pick to.
+ gApi.projects().name(project.get()).branch(branch).create(new BranchInput());
+
+ // Cherry-pick without message.
+ CherryPickInput input = new CherryPickInput();
+ input.destination = branch;
+ String changeId =
+ gApi.changes()
+ .id(change.getChangeId())
+ .revision(revCommit.name())
+ .cherryPickAsInfo(input)
+ .id;
+
+ // Expect that the message of the cherry-picked commit was used for the cherry-pick change.
+ ChangeInfo changeInfo = gApi.changes().id(changeId).get();
+ RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
+ assertThat(revInfo).isNotNull();
+ assertThat(revInfo.commit.message).isEqualTo(revCommit.getFullMessage());
+ }
+
+ @Test
public void cherryPickSetChangeId() throws Exception {
PushOneCommit.Result r = pushTo("refs/for/master");
CherryPickInput in = new CherryPickInput();
@@ -456,9 +494,11 @@ public class RevisionIT extends AbstractDaemonTest {
cherry.current().review(ReviewInput.approve());
cherry.current().submit();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Cherry pick failed: identical tree");
- orig.revision(r.getCommit().name()).cherryPick(in);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> orig.revision(r.getCommit().name()).cherryPick(in));
+ assertThat(thrown).hasMessageThat().contains("Cherry pick failed: identical tree");
}
@Test
@@ -482,9 +522,11 @@ public class RevisionIT extends AbstractDaemonTest {
ChangeApi orig = gApi.changes().id(triplet);
assertThat(orig.get().messages).hasSize(1);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Cherry pick failed: merge conflict");
- orig.revision(r.getCommit().name()).cherryPick(in);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> orig.revision(r.getCommit().name()).cherryPick(in));
+ assertThat(thrown).hasMessageThat().contains("Cherry pick failed: merge conflict");
}
@Test
@@ -522,12 +564,11 @@ public class RevisionIT extends AbstractDaemonTest {
CherryPickInput in = new CherryPickInput();
in.destination = destBranch;
in.message = "Cherry-Pick";
- try {
- changeApi.revision(r.getCommit().name()).cherryPickAsInfo(in);
- fail("expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e.getMessage()).isEqualTo("Cherry pick failed: merge conflict");
- }
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> changeApi.revision(r.getCommit().name()).cherryPickAsInfo(in));
+ assertThat(thrown).hasMessageThat().isEqualTo("Cherry pick failed: merge conflict");
// Cherry-pick with auto merge should succeed.
in.allowConflicts = true;
@@ -551,8 +592,8 @@ public class RevisionIT extends AbstractDaemonTest {
ByteArrayOutputStream os = new ByteArrayOutputStream();
bin.writeTo(os);
String fileContent = new String(os.toByteArray(), UTF_8);
- String destSha1 = getRemoteHead(project, destBranch).abbreviate(6).name();
- String changeSha1 = r.getCommit().abbreviate(6).name();
+ String destSha1 = abbreviateName(projectOperations.project(project).getHead(destBranch), 6);
+ String changeSha1 = abbreviateName(r.getCommit(), 6);
assertThat(fileContent)
.isEqualTo(
"<<<<<<< HEAD ("
@@ -604,16 +645,15 @@ public class RevisionIT extends AbstractDaemonTest {
CherryPickInput in = new CherryPickInput();
in.destination = "foo";
in.message = r1.getCommit().getFullMessage();
- try {
- gApi.changes().id(t1).current().cherryPick(in);
- fail("expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e.getMessage())
- .isEqualTo(
- "Cannot create new patch set of change "
- + info(t2)._number
- + " because it is abandoned");
- }
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.changes().id(t1).current().cherryPick(in));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ "Cannot create new patch set of change "
+ + info(t2)._number
+ + " because it is abandoned");
gApi.changes().id(t2).restore();
gApi.changes().id(t1).current().cherryPick(in);
@@ -629,7 +669,7 @@ public class RevisionIT extends AbstractDaemonTest {
createCherryPickableMerge(parent1FileName, parent2FileName);
String cherryPickBranchName = "branch_for_cherry_pick";
- createBranch(new Branch.NameKey(project, cherryPickBranchName));
+ createBranch(BranchNameKey.create(project, cherryPickBranchName));
CherryPickInput cherryPickInput = new CherryPickInput();
cherryPickInput.destination = cherryPickBranchName;
@@ -656,7 +696,7 @@ public class RevisionIT extends AbstractDaemonTest {
createCherryPickableMerge(parent1FileName, parent2FileName);
String cherryPickBranchName = "branch_for_cherry_pick";
- createBranch(new Branch.NameKey(project, cherryPickBranchName));
+ createBranch(BranchNameKey.create(project, cherryPickBranchName));
CherryPickInput cherryPickInput = new CherryPickInput();
cherryPickInput.destination = cherryPickBranchName;
@@ -684,17 +724,24 @@ public class RevisionIT extends AbstractDaemonTest {
createCherryPickableMerge(parent1FileName, parent2FileName);
String cherryPickBranchName = "branch_for_cherry_pick";
- createBranch(new Branch.NameKey(project, cherryPickBranchName));
+ createBranch(BranchNameKey.create(project, cherryPickBranchName));
CherryPickInput cherryPickInput = new CherryPickInput();
cherryPickInput.destination = cherryPickBranchName;
cherryPickInput.message = "Cherry-pick a merge commit to another branch";
cherryPickInput.parent = 0;
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- "Cherry Pick: Parent 0 does not exist. Please specify a parent in range [1, 2].");
- gApi.changes().id(mergeChangeResult.getChangeId()).current().cherryPick(cherryPickInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.changes()
+ .id(mergeChangeResult.getChangeId())
+ .current()
+ .cherryPick(cherryPickInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Cherry Pick: Parent 0 does not exist. Please specify a parent in range [1, 2].");
}
@Test
@@ -705,24 +752,31 @@ public class RevisionIT extends AbstractDaemonTest {
createCherryPickableMerge(parent1FileName, parent2FileName);
String cherryPickBranchName = "branch_for_cherry_pick";
- createBranch(new Branch.NameKey(project, cherryPickBranchName));
+ createBranch(BranchNameKey.create(project, cherryPickBranchName));
CherryPickInput cherryPickInput = new CherryPickInput();
cherryPickInput.destination = cherryPickBranchName;
cherryPickInput.message = "Cherry-pick a merge commit to another branch";
cherryPickInput.parent = 3;
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- "Cherry Pick: Parent 3 does not exist. Please specify a parent in range [1, 2].");
- gApi.changes().id(mergeChangeResult.getChangeId()).current().cherryPick(cherryPickInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ gApi.changes()
+ .id(mergeChangeResult.getChangeId())
+ .current()
+ .cherryPick(cherryPickInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Cherry Pick: Parent 3 does not exist. Please specify a parent in range [1, 2].");
}
@Test
public void cherryPickNotify() throws Exception {
- createBranch(new Branch.NameKey(project, "branch-1"));
- createBranch(new Branch.NameKey(project, "branch-2"));
- createBranch(new Branch.NameKey(project, "branch-3"));
+ createBranch(BranchNameKey.create(project, "branch-1"));
+ createBranch(BranchNameKey.create(project, "branch-2"));
+ createBranch(BranchNameKey.create(project, "branch-3"));
// Creates a change for 'admin'.
PushOneCommit.Result result = createChange();
@@ -761,7 +815,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void cherryPickKeepReviewers() throws Exception {
- createBranch(new Branch.NameKey(project, "stable"));
+ createBranch(BranchNameKey.create(project, "stable"));
// Change is created by 'admin'.
PushOneCommit.Result r = createChange();
@@ -795,7 +849,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void cherryPickToMergedChangeRevision() throws Exception {
- createBranch(new Branch.NameKey(project, "foo"));
+ createBranch(BranchNameKey.create(project, "foo"));
PushOneCommit.Result dstChange = createChange(testRepo, "foo", SUBJECT, "b.txt", "b", "t");
dstChange.assertOkStatus();
@@ -819,7 +873,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void cherryPickToOpenChangeRevision() throws Exception {
- createBranch(new Branch.NameKey(project, "foo"));
+ createBranch(BranchNameKey.create(project, "foo"));
PushOneCommit.Result dstChange = createChange(testRepo, "foo", SUBJECT, "b.txt", "b", "t");
dstChange.assertOkStatus();
@@ -837,7 +891,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void cherryPickToNonVisibleChangeFails() throws Exception {
- createBranch(new Branch.NameKey(project, "foo"));
+ createBranch(BranchNameKey.create(project, "foo"));
PushOneCommit.Result dstChange = createChange(testRepo, "foo", SUBJECT, "b.txt", "b", "t");
dstChange.assertOkStatus();
@@ -852,10 +906,13 @@ public class RevisionIT extends AbstractDaemonTest {
input.message = srcChange.getCommit().getFullMessage();
requestScopeOperations.setApiUser(user.id());
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage(
- String.format("Commit %s does not exist on branch refs/heads/foo", input.base));
- gApi.changes().id(srcChange.getChangeId()).current().cherryPick(input).get();
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.changes().id(srcChange.getChangeId()).current().cherryPick(input).get());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("Commit %s does not exist on branch refs/heads/foo", input.base));
}
@Test
@@ -869,12 +926,16 @@ public class RevisionIT extends AbstractDaemonTest {
input.base = change2.getCommit().name();
input.message = change1.getCommit().getFullMessage();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- String.format(
- "Change %s with commit %s is abandoned",
- change2.getChange().getId().get(), input.base));
- gApi.changes().id(change1.getChangeId()).current().cherryPick(input);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(change1.getChangeId()).current().cherryPick(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format(
+ "Change %s with commit %s is abandoned",
+ change2.getChange().getId().get(), input.base));
}
@Test
@@ -886,9 +947,13 @@ public class RevisionIT extends AbstractDaemonTest {
input.base = "invalid-sha1";
input.message = change1.getCommit().getFullMessage();
- exception.expect(BadRequestException.class);
- exception.expectMessage(String.format("Base %s doesn't represent a valid SHA-1", input.base));
- gApi.changes().id(change1.getChangeId()).current().cherryPick(input);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(change1.getChangeId()).current().cherryPick(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("Base %s doesn't represent a valid SHA-1", input.base));
}
@Test
@@ -929,7 +994,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void cherryPickToNonExistingBaseCommit() throws Exception {
- createBranch(new Branch.NameKey(project, "foo"));
+ createBranch(BranchNameKey.create(project, "foo"));
PushOneCommit.Result result = createChange();
CherryPickInput input = new CherryPickInput();
@@ -1050,25 +1115,22 @@ public class RevisionIT extends AbstractDaemonTest {
// Using the API returns the correct value, and reindexes as well.
CountDownLatch reindexed = new CountDownLatch(1);
- RegistrationHandle handle =
- changeIndexedListeners.add(
- "gerrit",
- new ChangeIndexedListener() {
- @Override
- public void onChangeIndexed(String projectName, int id) {
- if (id == id2.get()) {
- reindexed.countDown();
- }
- }
-
- @Override
- public void onChangeDeleted(int id) {}
- });
- try {
+ ChangeIndexedListener listener =
+ new ChangeIndexedListener() {
+ @Override
+ public void onChangeIndexed(String projectName, int id) {
+ if (id == id2.get()) {
+ reindexed.countDown();
+ }
+ }
+
+ @Override
+ public void onChangeDeleted(int id) {}
+ };
+
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
assertMergeable(r2.getChangeId(), true);
reindexed.await();
- } finally {
- handle.remove();
}
List<ChangeInfo> changes = search.call();
@@ -1228,16 +1290,26 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
assertDescription(r, "");
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("edit description not permitted");
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).description("test");
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () ->
+ gApi.changes()
+ .id(r.getChangeId())
+ .revision(r.getCommit().name())
+ .description("test"));
+ assertThat(thrown).hasMessageThat().contains("edit description not permitted");
}
@Test
public void setDescriptionAllowedWithPermission() throws Exception {
PushOneCommit.Result r = createChange();
assertDescription(r, "");
- grant(project, "refs/heads/master", Permission.OWNER, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).description("test");
assertDescription(r, "test");
@@ -1276,10 +1348,14 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void commit() throws Exception {
WebLinkInfo expectedWebLinkInfo = new WebLinkInfo("foo", "imageUrl", "url");
- RegistrationHandle handle =
- patchSetLinks.add("gerrit", (projectName, commit) -> expectedWebLinkInfo);
-
- try {
+ PatchSetWebLink link =
+ new PatchSetWebLink() {
+ @Override
+ public WebLinkInfo getPatchSetWebLink(String projectName, String commit) {
+ return expectedWebLinkInfo;
+ }
+ };
+ try (Registration registration = extensionRegistry.newRegistration().add(link)) {
PushOneCommit.Result r = createChange();
RevCommit c = r.getCommit();
@@ -1301,8 +1377,6 @@ public class RevisionIT extends AbstractDaemonTest {
assertThat(webLinkInfo.imageUrl).isEqualTo(expectedWebLinkInfo.imageUrl);
assertThat(webLinkInfo.url).isEqualTo(expectedWebLinkInfo.url);
assertThat(webLinkInfo.target).isEqualTo(expectedWebLinkInfo.target);
- } finally {
- handle.remove();
}
}
@@ -1413,8 +1487,8 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void commentOnNonExistingFile() throws Exception {
- PushOneCommit.Result r = createChange();
- r = updateChange(r, "new content");
+ PushOneCommit.Result r1 = createChange();
+ PushOneCommit.Result r2 = updateChange(r1, "new content");
CommentInput in = new CommentInput();
in.line = 1;
in.message = "nit: trailing whitespace";
@@ -1425,10 +1499,14 @@ public class RevisionIT extends AbstractDaemonTest {
reviewInput.comments = comments;
reviewInput.message = "comment test";
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- String.format("not found in revision %d,1", r.getChange().change().getId().id));
- gApi.changes().id(r.getChangeId()).revision(1).review(reviewInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(r2.getChangeId()).revision(1).review(reviewInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format("not found in revision %d,1", r2.getChange().change().getId().get()));
}
@Test
@@ -1456,9 +1534,11 @@ public class RevisionIT extends AbstractDaemonTest {
String res = new String(os.toByteArray(), UTF_8);
assertThat(res).isEqualTo(PATCH_FILE_ONLY);
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("File not found: nonexistent-file.");
- changeApi.revision(r.getCommit().name()).patch("nonexistent-file");
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> changeApi.revision(r.getCommit().name()).patch("nonexistent-file"));
+ assertThat(thrown).hasMessageThat().contains("File not found: nonexistent-file.");
}
@Test
@@ -1506,13 +1586,16 @@ public class RevisionIT extends AbstractDaemonTest {
// check if it's blocked to delete a vote on a non-current patch set.
requestScopeOperations.setApiUser(admin.id());
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("Cannot access on non-current patch set");
- gApi.changes()
- .id(r.getChangeId())
- .revision(r.getCommit().getName())
- .reviewer(user.id().toString())
- .deleteVote("Code-Review");
+ MethodNotAllowedException thrown =
+ assertThrows(
+ MethodNotAllowedException.class,
+ () ->
+ gApi.changes()
+ .id(r.getChangeId())
+ .revision(r.getCommit().getName())
+ .reviewer(user.id().toString())
+ .deleteVote("Code-Review"));
+ assertThat(thrown).hasMessageThat().contains("Cannot access on non-current patch set");
}
@Test
@@ -1622,9 +1705,9 @@ public class RevisionIT extends AbstractDaemonTest {
RevCommit initialCommit = getHead(repo(), "HEAD");
String branchAName = "branchA";
- createBranch(new Branch.NameKey(project, branchAName));
+ createBranch(BranchNameKey.create(project, branchAName));
String branchBName = "branchB";
- createBranch(new Branch.NameKey(project, branchBName));
+ createBranch(BranchNameKey.create(project, branchBName));
PushOneCommit.Result changeAResult =
pushFactory
@@ -1659,6 +1742,6 @@ public class RevisionIT extends AbstractDaemonTest {
}
private static Iterable<Account.Id> getReviewers(Collection<AccountInfo> r) {
- return Iterables.transform(r, a -> new Account.Id(a._accountId));
+ return Iterables.transform(r, a -> Account.id(a._accountId));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
index ba228f6e3b..62a7037f12 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
import static com.google.gerrit.extensions.common.testing.RobotCommentInfoSubject.assertThatList;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
@@ -164,9 +165,10 @@ public class RobotCommentsIT extends AbstractDaemonTest {
int sizeOfRest = 451;
fixReplacementInfo.replacement = getStringFor(defaultSizeLimit - sizeOfRest + 1);
- exception.expect(BadRequestException.class);
- exception.expectMessage("limit");
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown).hasMessageThat().contains("limit");
}
@Test
@@ -187,9 +189,10 @@ public class RobotCommentsIT extends AbstractDaemonTest {
int sizeLimit = 10 * 1024;
fixReplacementInfo.replacement = getStringFor(sizeLimit);
- exception.expect(BadRequestException.class);
- exception.expectMessage("limit");
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown).hasMessageThat().contains("limit");
}
@Test
@@ -254,12 +257,15 @@ public class RobotCommentsIT extends AbstractDaemonTest {
public void descriptionOfFixSuggestionIsMandatory() throws Exception {
fixSuggestionInfo.description = null;
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- String.format(
- "A description is required for the suggested fix of the robot comment on %s",
- withFixRobotCommentInput.path));
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format(
+ "A description is required for the suggested fix of the robot comment on %s",
+ withFixRobotCommentInput.path));
}
@Test
@@ -278,13 +284,16 @@ public class RobotCommentsIT extends AbstractDaemonTest {
public void fixReplacementsAreMandatory() throws Exception {
fixSuggestionInfo.replacements = Collections.emptyList();
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- String.format(
- "At least one replacement is required"
- + " for the suggested fix of the robot comment on %s",
- withFixRobotCommentInput.path));
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format(
+ "At least one replacement is required"
+ + " for the suggested fix of the robot comment on %s",
+ withFixRobotCommentInput.path));
}
@Test
@@ -305,12 +314,15 @@ public class RobotCommentsIT extends AbstractDaemonTest {
public void pathOfFixReplacementIsMandatory() throws Exception {
fixReplacementInfo.path = null;
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- String.format(
- "A file path must be given for the replacement of the robot comment on %s",
- withFixRobotCommentInput.path));
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format(
+ "A file path must be given for the replacement of the robot comment on %s",
+ withFixRobotCommentInput.path));
}
@Test
@@ -331,20 +343,24 @@ public class RobotCommentsIT extends AbstractDaemonTest {
public void rangeOfFixReplacementIsMandatory() throws Exception {
fixReplacementInfo.range = null;
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- String.format(
- "A range must be given for the replacement of the robot comment on %s",
- withFixRobotCommentInput.path));
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format(
+ "A range must be given for the replacement of the robot comment on %s",
+ withFixRobotCommentInput.path));
}
@Test
public void rangeOfFixReplacementNeedsToBeValid() throws Exception {
fixReplacementInfo.range = createRange(13, 9, 5, 10);
- exception.expect(BadRequestException.class);
- exception.expectMessage("Range (13:9 - 5:10)");
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown).hasMessageThat().contains("Range (13:9 - 5:10)");
}
@Test
@@ -364,9 +380,10 @@ public class RobotCommentsIT extends AbstractDaemonTest {
createFixSuggestionInfo(fixReplacementInfo1, fixReplacementInfo2);
withFixRobotCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo);
- exception.expect(BadRequestException.class);
- exception.expectMessage("overlap");
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown).hasMessageThat().contains("overlap");
}
@Test
@@ -461,13 +478,16 @@ public class RobotCommentsIT extends AbstractDaemonTest {
public void replacementStringOfFixReplacementIsMandatory() throws Exception {
fixReplacementInfo.replacement = null;
- exception.expect(BadRequestException.class);
- exception.expectMessage(
- String.format(
- "A content for replacement must be "
- + "indicated for the replacement of the robot comment on %s",
- withFixRobotCommentInput.path));
- addRobotComment(changeId, withFixRobotCommentInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> addRobotComment(changeId, withFixRobotCommentInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format(
+ "A content for replacement must be "
+ + "indicated for the replacement of the robot comment on %s",
+ withFixRobotCommentInput.path));
}
@Test
@@ -602,9 +622,11 @@ public class RobotCommentsIT extends AbstractDaemonTest {
List<String> fixIds = getFixIds(robotCommentInfos);
gApi.changes().id(changeId).current().applyFix(fixIds.get(0));
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("merge");
- gApi.changes().id(changeId).current().applyFix(fixIds.get(1));
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).current().applyFix(fixIds.get(1)));
+ assertThat(thrown).hasMessageThat().contains("merge");
}
@Test
@@ -708,8 +730,9 @@ public class RobotCommentsIT extends AbstractDaemonTest {
List<String> fixIds = getFixIds(robotCommentInfos);
String fixId = Iterables.getOnlyElement(fixIds);
- exception.expect(ResourceNotFoundException.class);
- gApi.changes().id(changeId).current().applyFix(fixId);
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(changeId).current().applyFix(fixId));
}
@Test
@@ -728,9 +751,11 @@ public class RobotCommentsIT extends AbstractDaemonTest {
List<String> fixIds = getFixIds(robotCommentInfos);
String fixId = Iterables.getOnlyElement(fixIds);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("current");
- gApi.changes().id(changeId).revision(previousRevision).applyFix(fixId);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).revision(previousRevision).applyFix(fixId));
+ assertThat(thrown).hasMessageThat().contains("current");
}
@Test
@@ -783,9 +808,11 @@ public class RobotCommentsIT extends AbstractDaemonTest {
List<String> fixIds = getFixIds(robotCommentInfos);
String fixId = Iterables.getOnlyElement(fixIds);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("based");
- gApi.changes().id(changeId).current().applyFix(fixId);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).current().applyFix(fixId));
+ assertThat(thrown).hasMessageThat().contains("based");
}
@Test
@@ -845,8 +872,9 @@ public class RobotCommentsIT extends AbstractDaemonTest {
String fixId = Iterables.getOnlyElement(fixIds);
String nonExistentFixId = fixId + "_non-existent";
- exception.expect(ResourceNotFoundException.class);
- gApi.changes().id(changeId).current().applyFix(nonExistentFixId);
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(changeId).current().applyFix(nonExistentFixId));
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index 3a2a50d95d..0d6e1389de 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -16,17 +16,17 @@ package com.google.gerrit.acceptance.edit;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.entities.Patch.COMMIT_MSG;
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_COMMIT;
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
import static com.google.gerrit.extensions.restapi.testing.BinaryResultSubject.assertThat;
-import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
@@ -36,19 +36,25 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.UseClockStep;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.RawInputUtil;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.PublishChangeEditInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ChangeEditDetailOption;
+import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.common.FileInfo;
@@ -56,15 +62,11 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ChangeMessagesUtil;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.gerrit.server.restapi.change.ChangeEdits.EditMessage;
import com.google.gerrit.server.restapi.change.ChangeEdits.Post;
import com.google.gerrit.server.restapi.change.ChangeEdits.Put;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.inject.Inject;
@@ -80,11 +82,10 @@ import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
+@UseClockStep
public class ChangeEditIT extends AbstractDaemonTest {
private static final String FILE_NAME = "foo";
@@ -102,16 +103,6 @@ public class ChangeEditIT extends AbstractDaemonTest {
private String changeId2;
private PatchSet ps;
- @BeforeClass
- public static void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @AfterClass
- public static void restoreTime() {
- TestTimeUtil.useSystemTime();
- }
-
@Before
public void setUp() throws Exception {
changeId = newChange(admin.newIdent());
@@ -196,7 +187,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
adminRestSession.post(urlPublish(changeId)).assertNoContent();
assertThat(getEdit(changeId)).isAbsent();
PatchSet newCurrentPatchSet = getCurrentPatchSet(changeId);
- assertThat(newCurrentPatchSet.getId()).isNotEqualTo(oldCurrentPatchSet.getId());
+ assertThat(newCurrentPatchSet.id()).isNotEqualTo(oldCurrentPatchSet.id());
assertChangeMessages(
changeId,
ImmutableList.of(
@@ -249,13 +240,13 @@ public class ChangeEditIT extends AbstractDaemonTest {
PatchSet currentPatchSet = getCurrentPatchSet(changeId2);
Optional<EditInfo> originalEdit = getEdit(changeId2);
- assertThat(originalEdit).value().baseRevision().isEqualTo(previousPatchSet.getRevision().get());
+ assertThat(originalEdit).value().baseRevision().isEqualTo(previousPatchSet.commitId().name());
Timestamp beforeRebase = originalEdit.get().commit.committer.date;
gApi.changes().id(changeId2).edit().rebase();
ensureSameBytes(getFileContentOfEdit(changeId2, FILE_NAME), CONTENT_NEW);
ensureSameBytes(getFileContentOfEdit(changeId2, FILE_NAME2), CONTENT_NEW2);
Optional<EditInfo> rebasedEdit = getEdit(changeId2);
- assertThat(rebasedEdit).value().baseRevision().isEqualTo(currentPatchSet.getRevision().get());
+ assertThat(rebasedEdit).value().baseRevision().isEqualTo(currentPatchSet.commitId().name());
assertThat(rebasedEdit).value().commit().committer().date().isNotEqualTo(beforeRebase);
}
@@ -268,13 +259,13 @@ public class ChangeEditIT extends AbstractDaemonTest {
PatchSet currentPatchSet = getCurrentPatchSet(changeId2);
Optional<EditInfo> originalEdit = getEdit(changeId2);
- assertThat(originalEdit).value().baseRevision().isEqualTo(previousPatchSet.getRevision().get());
+ assertThat(originalEdit).value().baseRevision().isEqualTo(previousPatchSet.commitId().name());
Timestamp beforeRebase = originalEdit.get().commit.committer.date;
adminRestSession.post(urlRebase(changeId2)).assertNoContent();
ensureSameBytes(getFileContentOfEdit(changeId2, FILE_NAME), CONTENT_NEW);
ensureSameBytes(getFileContentOfEdit(changeId2, FILE_NAME2), CONTENT_NEW2);
Optional<EditInfo> rebasedEdit = getEdit(changeId2);
- assertThat(rebasedEdit).value().baseRevision().isEqualTo(currentPatchSet.getRevision().get());
+ assertThat(rebasedEdit).value().baseRevision().isEqualTo(currentPatchSet.commitId().name());
assertThat(rebasedEdit).value().commit().committer().date().isNotEqualTo(beforeRebase);
}
@@ -284,7 +275,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
createEmptyEditFor(changeId2);
gApi.changes().id(changeId2).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
Optional<EditInfo> edit = getEdit(changeId2);
- assertThat(edit).value().baseRevision().isEqualTo(currentPatchSet.getRevision().get());
+ assertThat(edit).value().baseRevision().isEqualTo(currentPatchSet.commitId().name());
PushOneCommit push =
pushFactory.create(
admin.newIdent(),
@@ -339,9 +330,13 @@ public class ChangeEditIT extends AbstractDaemonTest {
createEmptyEditFor(changeId);
String commitMessage = gApi.changes().id(changeId).edit().getCommitMessage();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("New commit message cannot be same as existing commit message");
- gApi.changes().id(changeId).edit().modifyCommitMessage(commitMessage);
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).edit().modifyCommitMessage(commitMessage));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("New commit message cannot be same as existing commit message");
}
@Test
@@ -349,9 +344,13 @@ public class ChangeEditIT extends AbstractDaemonTest {
createEmptyEditFor(changeId);
String commitMessage = gApi.changes().id(changeId).edit().getCommitMessage();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("New commit message cannot be same as existing commit message");
- gApi.changes().id(changeId).edit().modifyCommitMessage(commitMessage + "\n\n");
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(changeId).edit().modifyCommitMessage(commitMessage + "\n\n"));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("New commit message cannot be same as existing commit message");
}
@Test
@@ -402,7 +401,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
r = adminRestSession.getJsonAccept(urlEditMessage(changeId, true));
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
- RevCommit commit = rw.parseCommit(ObjectId.fromString(ps.getRevision().get()));
+ RevCommit commit = rw.parseCommit(ObjectId.fromString(ps.commitId().name()));
assertThat(readContentFromJson(r)).isEqualTo(commit.getFullMessage());
}
@@ -606,16 +605,22 @@ public class ChangeEditIT extends AbstractDaemonTest {
@Test
public void writeNoChanges() throws Exception {
createEmptyEditFor(changeId);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("no changes were made");
- gApi.changes().id(changeId).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_OLD));
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () ->
+ gApi.changes()
+ .id(changeId)
+ .edit()
+ .modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_OLD)));
+ assertThat(thrown).hasMessageThat().contains("no changes were made");
}
@Test
public void editCommitMessageCopiesLabelScores() throws Exception {
String cr = "Code-Review";
try (ProjectConfigUpdate u = updateProject(project)) {
- LabelType codeReview = Util.codeReview();
+ LabelType codeReview = TestLabels.codeReview();
codeReview.setCopyAllScoresIfNoCodeChange(true);
u.getConfig().getLabelSections().put(cr, codeReview);
u.save();
@@ -708,7 +713,11 @@ public class ChangeEditIT extends AbstractDaemonTest {
TestRepository<InMemoryRepository> userTestRepo = cloneProject(p, user);
// Block default permission
- block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(block(Permission.ADD_PATCH_SET).ref("refs/for/*").group(REGISTERED_USERS))
+ .update();
// Create change as user
PushOneCommit push = pushFactory.create(user.newIdent(), userTestRepo);
@@ -716,8 +725,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
r1.assertOkStatus();
// Try to create edit as admin
- exception.expect(AuthException.class);
- createEmptyEditFor(r1.getChangeId());
+ assertThrows(AuthException.class, () -> createEmptyEditFor(r1.getChangeId()));
}
@Test
@@ -726,9 +734,11 @@ public class ChangeEditIT extends AbstractDaemonTest {
gApi.changes().id(changeId).current().review(ReviewInput.approve());
gApi.changes().id(changeId).current().submit();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(String.format("change %s is merged", change._number));
- createArbitraryEditFor(changeId);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> createArbitraryEditFor(changeId));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("change %s is merged", change._number));
}
@Test
@@ -736,9 +746,32 @@ public class ChangeEditIT extends AbstractDaemonTest {
ChangeInfo change = gApi.changes().id(changeId).get();
gApi.changes().id(changeId).abandon();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(String.format("change %s is abandoned", change._number));
- createArbitraryEditFor(changeId);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> createArbitraryEditFor(changeId));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("change %s is abandoned", change._number));
+ }
+
+ @Test
+ public void sha1sOfTwoChangesWithSameContentAfterEditDiffer() throws Exception {
+ ChangeInput changeInput = new ChangeInput();
+ changeInput.project = project.get();
+ changeInput.branch = "master";
+ changeInput.subject = "Empty change";
+ changeInput.status = ChangeStatus.NEW;
+
+ ChangeInfo info1 = gApi.changes().create(changeInput).get();
+ gApi.changes().id(info1._number).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
+ gApi.changes().id(info1._number).edit().publish(new PublishChangeEditInput());
+ info1 = gApi.changes().id(info1._number).get();
+
+ ChangeInfo info2 = gApi.changes().create(changeInput).get();
+ gApi.changes().id(info2._number).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
+ gApi.changes().id(info2._number).edit().publish(new PublishChangeEditInput());
+ info2 = gApi.changes().id(info2._number).get();
+
+ assertThat(info1.currentRevision).isNotEqualTo(info2.currentRevision);
}
private void createArbitraryEditFor(String changeId) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java b/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
index 8ebb2bdaaf..3b80312a35 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
@@ -16,14 +16,17 @@ package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.deleteRef;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.OK;
import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_OTHER_REASON;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.transport.PushResult;
@@ -31,6 +34,7 @@ import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.junit.Test;
public abstract class AbstractForcePush extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Test
public void forcePushNotAllowed() throws Exception {
@@ -55,7 +59,11 @@ public abstract class AbstractForcePush extends AbstractDaemonTest {
@Test
public void forcePushAllowed() throws Exception {
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
- grant(project, "refs/*", Permission.PUSH, true);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/*").group(adminGroupUuid()).force(true))
+ .update();
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
@@ -80,19 +88,31 @@ public abstract class AbstractForcePush extends AbstractDaemonTest {
@Test
public void deleteNotAllowedWithOnlyPushPermission() throws Exception {
- grant(project, "refs/*", Permission.PUSH, false);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/*").group(adminGroupUuid()))
+ .update();
assertDeleteRef(REJECTED_OTHER_REASON);
}
@Test
public void deleteAllowedWithForcePushPermission() throws Exception {
- grant(project, "refs/*", Permission.PUSH, true);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/*").group(adminGroupUuid()).force(true))
+ .update();
assertDeleteRef(OK);
}
@Test
public void deleteAllowedWithDeletePermission() throws Exception {
- grant(project, "refs/*", Permission.DELETE, true);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE).ref("refs/*").group(adminGroupUuid()).force(true))
+ .update();
assertDeleteRef(OK);
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 216677d271..cab12b3d87 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -17,12 +17,17 @@ package com.google.gerrit.acceptance.git;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
import static com.google.gerrit.acceptance.GitUtil.assertPushRejected;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.acceptance.GitUtil.pushOne;
import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.common.FooterConstants.CHANGE_ID;
import static com.google.gerrit.extensions.client.ListChangesOption.ALL_REVISIONS;
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
@@ -33,10 +38,9 @@ import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assert
import static com.google.gerrit.server.git.receive.ReceiveConstants.PUSH_OPTION_SKIP_VALIDATION;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.project.testing.Util.category;
-import static com.google.gerrit.server.project.testing.Util.value;
+import static com.google.gerrit.server.project.testing.TestLabels.label;
+import static com.google.gerrit.server.project.testing.TestLabels.value;
import static java.util.Comparator.comparing;
-import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
@@ -46,17 +50,27 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.SkipProjectClone;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -77,30 +91,18 @@ import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
-import com.google.gerrit.extensions.common.testing.EditInfoSubject;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.git.ObjectIds;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.events.CommitReceivedEvent;
import com.google.gerrit.server.git.receive.NoteDbPushOption;
import com.google.gerrit.server.git.receive.ReceiveConstants;
import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
-import com.google.gerrit.server.git.validators.CommitValidators.ChangeIdValidator;
import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.testing.FakeEmailSender.Message;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
@@ -117,6 +119,7 @@ import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
@@ -128,52 +131,51 @@ import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
@SkipProjectClone
+@UseClockStep
public abstract class AbstractPushForReview extends AbstractDaemonTest {
protected enum Protocol {
- // TODO(dborowitz): TEST.
+ // Only test protocols which are actually served by the Gerrit server, since each separate test
+ // class is large and slow.
+ //
+ // This list excludes the test InProcessProtocol, which is used by large numbers of other
+ // acceptance tests. Small tests of InProcessProtocol are still possible, without incurring a
+ // new large slow test.
SSH,
HTTP
}
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
private static String NEW_CHANGE_INDICATOR = " [NEW]";
private LabelType patchSetLock;
- @Inject private DynamicSet<CommitValidationListener> commitValidators;
-
- @BeforeClass
- public static void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @AfterClass
- public static void restoreTime() {
- TestTimeUtil.useSystemTime();
- }
-
@Before
public void setUpPatchSetLock() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
- patchSetLock = Util.patchSetLock();
+ patchSetLock = TestLabels.patchSetLock();
u.getConfig().getLabelSections().put(patchSetLock.getName(), patchSetLock);
- AccountGroup.UUID anonymousUsers = systemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID();
- Util.allow(
- u.getConfig(),
- Permission.forLabel(patchSetLock.getName()),
- 0,
- 1,
- anonymousUsers,
- "refs/heads/*");
u.save();
}
- grant(project, "refs/heads/*", Permission.LABEL + "Patch-Set-Lock");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel(patchSetLock.getName())
+ .ref("refs/heads/*")
+ .group(ANONYMOUS_USERS)
+ .range(0, 1))
+ .add(
+ allowLabel(patchSetLock.getName())
+ .ref("refs/heads/*")
+ .group(adminGroupUuid())
+ .range(0, 1))
+ .update();
}
@After
@@ -859,7 +861,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(r.getChange().change().isWorkInProgress()).isTrue();
// Admin user trying to move from WIP to ready should succeed.
- GitUtil.fetch(testRepo, r.getPatchSet().getRefName() + ":ps");
+ GitUtil.fetch(testRepo, r.getPatchSet().refName() + ":ps");
testRepo.reset("ps");
r = amendChange(r.getChangeId(), "refs/for/master%ready", user, testRepo);
r.assertOkStatus();
@@ -875,7 +877,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(r.getChange().change().isWorkInProgress()).isFalse();
// Admin user trying to move from ready to WIP should succeed.
- GitUtil.fetch(testRepo, r.getPatchSet().getRefName() + ":ps");
+ GitUtil.fetch(testRepo, r.getPatchSet().refName() + ":ps");
testRepo.reset("ps");
r = amendChange(r.getChangeId(), "refs/for/master%wip", admin, testRepo);
r.assertOkStatus();
@@ -886,16 +888,26 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Non owner, non admin and non project owner cannot flip wip bit:
TestAccount user2 = accountCreator.user2();
- grant(
- project, "refs/*", Permission.FORGE_COMMITTER, false, SystemGroupBackend.REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allow(Permission.FORGE_COMMITTER)
+ .ref("refs/*")
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
TestRepository<?> user2Repo = cloneProject(project, user2);
- GitUtil.fetch(user2Repo, r.getPatchSet().getRefName() + ":ps");
+ GitUtil.fetch(user2Repo, r.getPatchSet().refName() + ":ps");
user2Repo.reset("ps");
r = amendChange(r.getChangeId(), "refs/for/master%ready", user2, user2Repo);
r.assertErrorStatus(ReceiveConstants.ONLY_CHANGE_OWNER_OR_PROJECT_OWNER_CAN_MODIFY_WIP);
// Project owner trying to move from WIP to ready should succeed.
- allow("refs/*", Permission.OWNER, SystemGroupBackend.REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
r = amendChange(r.getChangeId(), "refs/for/master%ready", user2, user2Repo);
r.assertOkStatus();
}
@@ -1196,14 +1208,17 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void pushWithMultipleApprovals() throws Exception {
LabelType Q =
- category("Custom-Label", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
- AccountGroup.UUID anon = systemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID();
+ label("Custom-Label", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
String heads = "refs/heads/*";
try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(u.getConfig(), Permission.forLabel("Custom-Label"), -1, 1, anon, heads);
u.getConfig().getLabelSections().put(Q.getName(), Q);
u.save();
}
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel("Custom-Label").ref(heads).group(ANONYMOUS_USERS).range(-1, 1))
+ .update();
RevCommit c =
commitBuilder()
@@ -1225,40 +1240,6 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
}
@Test
- @GerritConfig(name = "receive.allowPushToRefsChanges", value = "true")
- public void pushToRefsChangesAllowed() throws Exception {
- PushOneCommit.Result r = pushOneCommitToRefsChanges();
- r.assertOkStatus();
- }
-
- @Test
- public void pushNewPatchsetToRefsChanges() throws Exception {
- PushOneCommit.Result r = pushOneCommitToRefsChanges();
- r.assertErrorStatus("upload to refs/changes not allowed");
- }
-
- @Test
- @GerritConfig(name = "receive.allowPushToRefsChanges", value = "false")
- public void pushToRefsChangesNotAllowed() throws Exception {
- PushOneCommit.Result r = pushOneCommitToRefsChanges();
- r.assertErrorStatus("upload to refs/changes not allowed");
- }
-
- private PushOneCommit.Result pushOneCommitToRefsChanges() throws Exception {
- PushOneCommit.Result r = pushTo("refs/for/master");
- r.assertOkStatus();
- PushOneCommit push =
- pushFactory.create(
- admin.newIdent(),
- testRepo,
- PushOneCommit.SUBJECT,
- "b.txt",
- "anotherContent",
- r.getChangeId());
- return push.to("refs/changes/" + r.getChange().change().getId().get());
- }
-
- @Test
public void pushNewPatchsetToPatchSetLockedChange() throws Exception {
PushOneCommit.Result r = pushTo("refs/for/master");
r.assertOkStatus();
@@ -1364,7 +1345,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r.assertOkStatus();
setUseSignedOffBy(InheritableBoolean.TRUE);
- block(project, "refs/heads/master", Permission.FORGE_COMMITTER, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.FORGE_COMMITTER).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
push =
pushFactory.create(
@@ -1420,7 +1405,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// create a second change as user (depends on the change from admin)
TestRepository<?> userRepo = cloneProject(project, user);
- GitUtil.fetch(userRepo, r.getPatchSet().getRefName() + ":change");
+ GitUtil.fetch(userRepo, r.getPatchSet().refName() + ":change");
userRepo.reset("change");
push =
pushFactory.create(
@@ -1434,7 +1419,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void pushSameCommitTwiceUsingMagicBranchBaseOption() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/master").group(adminGroupUuid()))
+ .update();
PushOneCommit.Result rBase = pushTo("refs/heads/master");
rBase.assertOkStatus();
@@ -1530,8 +1519,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Check that a change was created for each.
for (RevCommit c : commits) {
- assertThat(byCommit(c).change().getSubject())
- .named("change for " + c.name())
+ assertWithMessage("change for " + c.name())
+ .that(byCommit(c).change().getSubject())
.isEqualTo(c.getShortMessage());
}
@@ -1543,9 +1532,9 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
RevCommit c2 = commits2.get(i);
String name = "change for " + c2.name();
ChangeData cd = byCommit(c);
- assertThat(cd.change().getSubject()).named(name).isEqualTo(c2.getShortMessage());
- assertThat(getPatchSetRevisions(cd))
- .named(name)
+ assertWithMessage(name).that(cd.change().getSubject()).isEqualTo(c2.getShortMessage());
+ assertWithMessage(name)
+ .that(getPatchSetRevisions(cd))
.containsExactlyEntriesIn(ImmutableMap.of(1, c.name(), 2, c2.name()));
}
@@ -1608,37 +1597,13 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
RemoteRefUpdate refUpdate = r.getRemoteUpdate(ref);
assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
String reason =
- String.format(
- "commit %s: missing Change-Id in message footer", c.toObjectId().abbreviate(7).name());
+ String.format("commit %s: missing Change-Id in message footer", abbreviateName(c));
assertThat(refUpdate.getMessage()).isEqualTo(reason);
assertThat(r.getMessages()).contains("\nERROR: " + reason);
}
@Test
- @GerritConfig(name = "receive.allowPushToRefsChanges", value = "true")
- public void testPushWithChangedChangeId() throws Exception {
- PushOneCommit.Result r = pushTo("refs/for/master");
- r.assertOkStatus();
- PushOneCommit push =
- pushFactory.create(
- admin.newIdent(),
- testRepo,
- PushOneCommit.SUBJECT
- + "\n\n"
- + "Change-Id: I55eab7c7a76e95005fa9cc469aa8f9fc16da9eba\n",
- "b.txt",
- "anotherContent",
- r.getChangeId());
- r = push.to("refs/changes/" + r.getChange().change().getId().get());
- r.assertErrorStatus(
- String.format(
- "commit %s: %s",
- r.getCommit().abbreviate(RevId.ABBREV_LEN).name(),
- ChangeIdValidator.CHANGE_ID_MISMATCH_MSG));
- }
-
- @Test
public void pushWithMultipleChangeIds() throws Exception {
testPushWithMultipleChangeIds();
}
@@ -1814,30 +1779,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
}
@Test
- @GerritConfig(name = "receive.allowPushToRefsChanges", value = "true")
- public void accidentallyPushNewPatchSetDirectlyToBranchAndRecoverByPushingToRefsChanges()
- throws Exception {
- Change.Id id = accidentallyPushNewPatchSetDirectlyToBranch();
- ChangeData cd = byChangeId(id);
- String ps1Rev = Iterables.getOnlyElement(cd.patchSets()).getRevision().get();
-
- String r = "refs/changes/" + id;
- assertPushOk(pushHead(testRepo, r, false), r);
-
- // Added a new patch set and auto-closed the change.
- cd = byChangeId(id);
- assertThat(cd.change().isMerged()).isTrue();
- assertThat(getPatchSetRevisions(cd))
- .containsExactlyEntriesIn(
- ImmutableMap.of(1, ps1Rev, 2, testRepo.getRepository().resolve("HEAD").name()));
- }
-
- @Test
public void accidentallyPushNewPatchSetDirectlyToBranchAndCantRecoverByPushingToRefsFor()
throws Exception {
Change.Id id = accidentallyPushNewPatchSetDirectlyToBranch();
ChangeData cd = byChangeId(id);
- String ps1Rev = Iterables.getOnlyElement(cd.patchSets()).getRevision().get();
+ String ps1Rev = Iterables.getOnlyElement(cd.patchSets()).commitId().name();
String r = "refs/for/master";
assertPushRejected(pushHead(testRepo, r, false), r, "no new changes");
@@ -1850,7 +1796,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void forcePushAbandonedChange() throws Exception {
- grant(project, "refs/*", Permission.PUSH, true);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/*").group(adminGroupUuid()).force(true))
+ .update();
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r = push1.to("refs/for/master");
@@ -1923,7 +1873,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void pushNewPatchsetOverridingStickyLabel() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
- LabelType codeReview = Util.codeReview();
+ LabelType codeReview = TestLabels.codeReview();
codeReview.setCopyMaxScore(true);
u.getConfig().getLabelSections().put(codeReview.getName(), codeReview);
u.save();
@@ -1946,7 +1896,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void createChangeForMergedCommit() throws Exception {
String master = "refs/heads/master";
- grant(project, master, Permission.PUSH, true);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref(master).group(adminGroupUuid()).force(true))
+ .update();
// Update master with a direct push.
RevCommit c1 = testRepo.commit().message("Non-change 1").create();
@@ -2045,7 +1999,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void mergedOptionWithExistingChangeInsertsPatchSet() throws Exception {
String master = "refs/heads/master";
- grant(project, master, Permission.PUSH, true);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref(master).group(adminGroupUuid()).force(true))
+ .update();
PushOneCommit.Result r = pushTo("refs/for/master");
r.assertOkStatus();
@@ -2238,53 +2196,6 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
amendChanges(unChanged.toObjectId(), commits, "refs/for/master%publish-comments");
}
- @Test
- public void pushWithDraftOptionIsDisabledPerDefault() throws Exception {
- for (String ref : ImmutableSet.of("refs/drafts/master", "refs/for/master%draft")) {
- PushOneCommit.Result r = pushTo(ref);
- r.assertErrorStatus();
- r.assertMessage("draft workflow is disabled");
- }
- }
-
- @GerritConfig(name = "change.allowDrafts", value = "true")
- @Test
- public void pushDraftGetsPrivateChange() throws Exception {
- String changeId1 = createChange("refs/drafts/master").getChangeId();
- String changeId2 = createChange("refs/for/master%draft").getChangeId();
-
- ChangeInfo info1 = gApi.changes().id(changeId1).get();
- ChangeInfo info2 = gApi.changes().id(changeId2).get();
-
- assertThat(info1.status).isEqualTo(ChangeStatus.NEW);
- assertThat(info2.status).isEqualTo(ChangeStatus.NEW);
- assertThat(info1.isPrivate).isTrue();
- assertThat(info2.isPrivate).isTrue();
- assertThat(info1.revisions).hasSize(1);
- assertThat(info2.revisions).hasSize(1);
- }
-
- @GerritConfig(name = "change.allowDrafts", value = "true")
- @Sandboxed
- @Test
- public void pushWithDraftOptionToExistingNewChangeGetsChangeEdit() throws Exception {
- String changeId = createChange().getChangeId();
- EditInfoSubject.assertThat(getEdit(changeId)).isAbsent();
-
- ChangeInfo changeInfo = gApi.changes().id(changeId).get();
- ChangeStatus originalChangeStatus = changeInfo.status;
-
- PushOneCommit.Result result = amendChange(changeId, "refs/drafts/master");
- result.assertOkStatus();
-
- changeInfo = gApi.changes().id(changeId).get();
- assertThat(changeInfo.status).isEqualTo(originalChangeStatus);
- assertThat(changeInfo.isPrivate).isNull();
- assertThat(changeInfo.revisions).hasSize(1);
-
- EditInfoSubject.assertThat(getEdit(changeId)).isPresent();
- }
-
@GerritConfig(name = "receive.maxBatchCommits", value = "2")
@Test
public void maxBatchCommits() throws Exception {
@@ -2294,24 +2205,16 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@GerritConfig(name = "receive.maxBatchCommits", value = "2")
@Test
public void maxBatchCommitsWithDefaultValidator() throws Exception {
- TestValidator validator = new TestValidator();
- RegistrationHandle handle = commitValidators.add("test-validator", validator);
- try {
+ try (Registration registration = extensionRegistry.newRegistration().add(new TestValidator())) {
testMaxBatchCommits();
- } finally {
- handle.remove();
}
}
@GerritConfig(name = "receive.maxBatchCommits", value = "2")
@Test
public void maxBatchCommitsWithValidateAllCommitsValidator() throws Exception {
- TestValidator validator = new TestValidator(true);
- RegistrationHandle handle = commitValidators.add("test-validator", validator);
- try {
+ try (Registration registration = extensionRegistry.newRegistration().add(new TestValidator())) {
testMaxBatchCommits();
- } finally {
- handle.remove();
}
}
@@ -2369,10 +2272,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
public void skipValidation() throws Exception {
String master = "refs/heads/master";
TestValidator validator = new TestValidator();
- RegistrationHandle handle = commitValidators.add("test-validator", validator);
- RegistrationHandle handle2 = null;
-
- try {
+ try (Registration registration = extensionRegistry.newRegistration().add(validator)) {
// Validation listener is called on normal push
PushOneCommit push =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
@@ -2401,20 +2301,16 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Validation listener that needs to validate all commits gets called even
// when the skip option is used.
TestValidator validator2 = new TestValidator(true);
- handle2 = commitValidators.add("test-validator-2", validator2);
- PushOneCommit push4 =
- pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
- push4.setPushOptions(ImmutableList.of(PUSH_OPTION_SKIP_VALIDATION));
- r = push4.to(master);
- r.assertOkStatus();
- // First listener was not called; its count remains the same.
- assertThat(validator.count()).isEqualTo(1);
- // Second listener was called.
- assertThat(validator2.count()).isEqualTo(1);
- } finally {
- handle.remove();
- if (handle2 != null) {
- handle2.remove();
+ try (Registration registration2 = extensionRegistry.newRegistration().add(validator2)) {
+ PushOneCommit push4 =
+ pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
+ push4.setPushOptions(ImmutableList.of(PUSH_OPTION_SKIP_VALIDATION));
+ r = push4.to(master);
+ r.assertOkStatus();
+ // First listener was not called; its count remains the same.
+ assertThat(validator.count()).isEqualTo(1);
+ // Second listener was called.
+ assertThat(validator2.count()).isEqualTo(1);
}
}
}
@@ -2435,12 +2331,19 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
pr = pushOne(testRepo, c.name(), ref, false, false, opts);
assertPushRejected(pr, ref, "NoteDb update requires access database permission");
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
pr = pushOne(testRepo, c.name(), ref, false, false, opts);
assertPushRejected(pr, ref, "prohibited by Gerrit: not permitted: create");
- grant(project, "refs/changes/*", Permission.CREATE);
- grant(project, "refs/changes/*", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref("refs/changes/*").group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref("refs/changes/*").group(adminGroupUuid()))
+ .update();
grantSkipValidation(project, "refs/changes/*", REGISTERED_USERS);
pr = pushOne(testRepo, c.name(), ref, false, false, opts);
assertPushOk(pr, ref);
@@ -2489,9 +2392,9 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(pr.getMessages())
.contains(
"warning: no changes between prior commit "
- + c.abbreviate(7).name()
+ + abbreviateName(c)
+ " and new commit "
- + amended.abbreviate(7).name());
+ + abbreviateName(amended));
}
@Test
@@ -2517,8 +2420,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
pr = pushHead(testRepo, r, false);
assertPushOk(pr, r);
assertThat(pr.getMessages())
- .contains(
- "warning: " + amended.abbreviate(7).name() + ": no files changed, message updated");
+ .contains("warning: " + abbreviateName(amended) + ": no files changed, message updated");
}
@Test
@@ -2542,8 +2444,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
pr = pushHead(testRepo, r, false);
assertPushOk(pr, r);
assertThat(pr.getMessages())
- .contains(
- "warning: " + amended.abbreviate(7).name() + ": no files changed, author changed");
+ .contains("warning: " + abbreviateName(amended) + ": no files changed, author changed");
}
@Test
@@ -2570,7 +2471,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
pr = pushHead(testRepo, r, false);
assertPushOk(pr, r);
assertThat(pr.getMessages())
- .contains("warning: " + amended.abbreviate(7).name() + ": no files changed, was rebased");
+ .contains("warning: " + abbreviateName(amended) + ": no files changed, was rebased");
}
@Test
@@ -2641,6 +2542,76 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
+ "\n");
}
+ @Test
+ public void cannotPushTheSameCommitTwiceForReviewToTheSameBranch() throws Exception {
+ testCannotPushTheSameCommitTwiceForReviewToTheSameBranch();
+ }
+
+ @Test
+ public void cannotPushTheSameCommitTwiceForReviewToTheSameBranchCreateNewChangeForAllNotInTarget()
+ throws Exception {
+ enableCreateNewChangeForAllNotInTarget();
+ testCannotPushTheSameCommitTwiceForReviewToTheSameBranch();
+ }
+
+ private void testCannotPushTheSameCommitTwiceForReviewToTheSameBranch() throws Exception {
+ setRequireChangeId(InheritableBoolean.FALSE);
+
+ // create a commit without Change-Id
+ testRepo
+ .branch("HEAD")
+ .commit()
+ .author(user.newIdent())
+ .committer(user.newIdent())
+ .add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
+ .message(PushOneCommit.SUBJECT)
+ .create();
+
+ // push the commit for review to create a change
+ PushResult r = pushHead(testRepo, "refs/for/master");
+ assertPushOk(r, "refs/for/master");
+
+ // try to push the same commit for review again to create another change on the same branch,
+ // it's expected that this is rejected with "no new changes"
+ r = pushHead(testRepo, "refs/for/master");
+ assertPushRejected(r, "refs/for/master", "no new changes");
+ }
+
+ @Test
+ public void pushTheSameCommitTwiceForReviewToDifferentBranches() throws Exception {
+ setRequireChangeId(InheritableBoolean.FALSE);
+
+ // create a commit without Change-Id
+ testRepo
+ .branch("HEAD")
+ .commit()
+ .author(user.newIdent())
+ .committer(user.newIdent())
+ .add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
+ .message(PushOneCommit.SUBJECT)
+ .create();
+
+ // push the commit for review to create a change
+ PushResult r = pushHead(testRepo, "refs/for/master");
+ assertPushOk(r, "refs/for/master");
+
+ // create another branch
+ gApi.projects().name(project.get()).branch("otherBranch").create(new BranchInput());
+
+ // try to push the same commit for review again to create a change on another branch,
+ // it's expected that this is rejected with "no new changes" since
+ // CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET is false
+ r = pushHead(testRepo, "refs/for/otherBranch");
+ assertPushRejected(r, "refs/for/otherBranch", "no new changes");
+
+ enableCreateNewChangeForAllNotInTarget();
+
+ // try to push the same commit for review again to create a change on another branch,
+ // now it should succeed since CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET is true
+ r = pushHead(testRepo, "refs/for/otherBranch");
+ assertPushOk(r, "refs/for/otherBranch");
+ }
+
private DraftInput newDraft(String path, int line, String message) {
DraftInput d = new DraftInput();
d.path = path;
@@ -2685,11 +2656,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
ChangeData cd = byCommit(c);
String name = "reviewers for " + (i + 1);
if (expectedReviewer != null) {
- assertThat(cd.reviewers().all()).named(name).containsExactly(expectedReviewer.id());
+ assertWithMessage(name).that(cd.reviewers().all()).containsExactly(expectedReviewer.id());
// Remove reviewer from PS1 so we can test adding this same reviewer on PS2 below.
gApi.changes().id(cd.getId().get()).reviewer(expectedReviewer.id().toString()).remove();
}
- assertThat(byCommit(c).reviewers().all()).named(name).isEmpty();
+ assertWithMessage(name).that(byCommit(c).reviewers().all()).isEmpty();
}
List<RevCommit> commits2 = amendChanges(initialHead, commits, r);
@@ -2698,9 +2669,9 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
ChangeData cd = byCommit(c);
String name = "reviewers for " + (i + 1);
if (expectedReviewer != null) {
- assertThat(cd.reviewers().all()).named(name).containsExactly(expectedReviewer.id());
+ assertWithMessage(name).that(cd.reviewers().all()).containsExactly(expectedReviewer.id());
} else {
- assertThat(byCommit(c).reviewers().all()).named(name).isEmpty();
+ assertWithMessage(name).that(byCommit(c).reviewers().all()).isEmpty();
}
}
}
@@ -2767,20 +2738,20 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
private static Map<Integer, String> getPatchSetRevisions(ChangeData cd) throws Exception {
Map<Integer, String> revisions = new HashMap<>();
for (PatchSet ps : cd.patchSets()) {
- revisions.put(ps.getPatchSetId(), ps.getRevision().get());
+ revisions.put(ps.number(), ps.commitId().name());
}
return revisions;
}
private ChangeData byCommit(ObjectId id) throws Exception {
List<ChangeData> cds = queryProvider.get().byCommit(id);
- assertThat(cds).named("change for " + id.name()).hasSize(1);
+ assertWithMessage("change for " + id.name()).that(cds).hasSize(1);
return cds.get(0);
}
private ChangeData byChangeId(Change.Id id) throws Exception {
List<ChangeData> cds = queryProvider.get().byLegacyChangeId(id);
- assertThat(cds).named("change " + id).hasSize(1);
+ assertWithMessage("change " + id).that(cds).hasSize(1);
return cds.get(0);
}
@@ -2808,13 +2779,14 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
private void grantSkipValidation(Project.NameKey project, String ref, AccountGroup.UUID groupUuid)
throws Exception {
// See SKIP_VALIDATION implementation in default permission backend.
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(u.getConfig(), Permission.FORGE_AUTHOR, groupUuid, ref);
- Util.allow(u.getConfig(), Permission.FORGE_COMMITTER, groupUuid, ref);
- Util.allow(u.getConfig(), Permission.FORGE_SERVER, groupUuid, ref);
- Util.allow(u.getConfig(), Permission.PUSH_MERGE, groupUuid, "refs/for/" + ref);
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.FORGE_AUTHOR).ref(ref).group(groupUuid))
+ .add(allow(Permission.FORGE_COMMITTER).ref(ref).group(groupUuid))
+ .add(allow(Permission.FORGE_SERVER).ref(ref).group(groupUuid))
+ .add(allow(Permission.PUSH_MERGE).ref("refs/for/" + ref).group(groupUuid))
+ .update();
}
private PushOneCommit.Result amendChange(String changeId, String ref) throws Exception {
@@ -2833,4 +2805,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
? infos.stream().map(a -> a.email).collect(toImmutableList())
: ImmutableList.of();
}
+
+ private String abbreviateName(AnyObjectId id) throws Exception {
+ return ObjectIds.abbreviateName(id, testRepo.getRevWalk().getObjectReader());
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java b/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
index c02a5edc87..7cfb0f2855 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
@@ -17,21 +17,30 @@ package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
+import com.google.gerrit.extensions.api.changes.RecipientType;
+import com.google.gerrit.mail.Address;
+import com.google.gerrit.mail.EmailHeader;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.testing.FakeEmailSender.Message;
import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -48,6 +57,7 @@ import org.junit.Test;
public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
@Inject private ApprovalsUtil approvalsUtil;
+ @Inject private ProjectOperations projectOperations;
@Before
public void blockAnonymous() throws Exception {
@@ -56,7 +66,11 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
@Test
public void submitOnPush() throws Exception {
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/master").group(adminGroupUuid()))
+ .update();
PushOneCommit.Result r = pushTo("refs/for/master%submit");
r.assertOkStatus();
r.assertChange(Change.Status.MERGED, null, admin);
@@ -66,7 +80,11 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
@Test
public void submitOnPushToRefsMetaConfig() throws Exception {
- grant(project, "refs/for/refs/meta/config", Permission.SUBMIT);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/meta/config").group(adminGroupUuid()))
+ .update();
git().fetch().setRefSpecs(new RefSpec("refs/meta/config:refs/meta/config")).call();
testRepo.reset(RefNames.REFS_CONFIG);
@@ -84,7 +102,11 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
push("refs/heads/master", "one change", "a.txt", "some content");
testRepo.reset(objectId);
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/master").group(adminGroupUuid()))
+ .update();
PushOneCommit.Result r =
push("refs/for/master%submit", "other change", "a.txt", "other content");
r.assertErrorStatus();
@@ -100,7 +122,11 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
push(master, "one change", "a.txt", "some content");
testRepo.reset(objectId);
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/master").group(adminGroupUuid()))
+ .update();
PushOneCommit.Result r =
push("refs/for/master%submit", "other change", "b.txt", "other content");
r.assertOkStatus();
@@ -113,7 +139,11 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
PushOneCommit.Result r =
push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content");
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/master").group(adminGroupUuid()))
+ .update();
r =
push(
"refs/for/master%submit",
@@ -153,7 +183,11 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
@Test
public void mergeOnPushToBranch() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/master").group(adminGroupUuid()))
+ .update();
PushOneCommit.Result r =
push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content");
r.assertOkStatus();
@@ -162,28 +196,32 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
assertCommit(project, "refs/heads/master");
ChangeData cd =
- Iterables.getOnlyElement(queryProvider.get().byKey(new Change.Key(r.getChangeId())));
+ Iterables.getOnlyElement(queryProvider.get().byKey(Change.key(r.getChangeId())));
RevCommit c = r.getCommit();
- PatchSet.Id psId = cd.currentPatchSet().getId();
+ PatchSet.Id psId = cd.currentPatchSet().id();
assertThat(psId.get()).isEqualTo(1);
assertThat(cd.change().isMerged()).isTrue();
assertSubmitApproval(psId);
assertThat(cd.patchSets()).hasSize(1);
- assertThat(cd.patchSet(psId).getRevision().get()).isEqualTo(c.name());
+ assertThat(cd.patchSet(psId).commitId()).isEqualTo(c);
}
@Test
public void correctNewRevOnMergeByPushToBranch() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/master").group(adminGroupUuid()))
+ .update();
push("refs/for/master", PushOneCommit.SUBJECT, "one.txt", "One");
PushOneCommit.Result r = push("refs/for/master", PushOneCommit.SUBJECT, "two.txt", "Two");
startEventRecorder();
git().push().setRefSpecs(new RefSpec(r.getCommit().name() + ":refs/heads/master")).call();
List<ChangeMergedEvent> changeMergedEvents =
eventRecorder.getChangeMergedEvents(project.get(), "refs/heads/master", 2);
- assertThat(changeMergedEvents.get(0).newRev).isEqualTo(r.getPatchSet().getRevision().get());
- assertThat(changeMergedEvents.get(1).newRev).isEqualTo(r.getPatchSet().getRevision().get());
+ assertThat(changeMergedEvents.get(0).newRev).isEqualTo(r.getPatchSet().commitId().name());
+ assertThat(changeMergedEvents.get(1).newRev).isEqualTo(r.getPatchSet().commitId().name());
}
@Test
@@ -191,10 +229,14 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
enableCreateNewChangeForAllNotInTarget();
String master = "refs/heads/master";
String other = "refs/heads/other";
- grant(project, master, Permission.PUSH);
- grant(project, other, Permission.CREATE);
- grant(project, other, Permission.PUSH);
- RevCommit masterRev = getRemoteHead();
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref(master).group(adminGroupUuid()))
+ .add(allow(Permission.CREATE).ref(other).group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref(other).group(adminGroupUuid()))
+ .update();
+ RevCommit masterRev = projectOperations.project(project).getHead("master");
pushCommitTo(masterRev, other);
PushOneCommit.Result r = createChange();
r.assertOkStatus();
@@ -202,7 +244,7 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
pushCommitTo(commit, master);
assertCommit(project, master);
ChangeData cd =
- Iterables.getOnlyElement(queryProvider.get().byKey(new Change.Key(r.getChangeId())));
+ Iterables.getOnlyElement(queryProvider.get().byKey(Change.key(r.getChangeId())));
assertThat(cd.change().isMerged()).isTrue();
RemoteRefUpdate.Status status = pushCommitTo(commit, "refs/for/other");
@@ -211,8 +253,8 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
pushCommitTo(commit, other);
assertCommit(project, other);
- for (ChangeData c : queryProvider.get().byKey(new Change.Key(r.getChangeId()))) {
- if (c.change().getDest().get().equals(other)) {
+ for (ChangeData c : queryProvider.get().byKey(Change.key(r.getChangeId()))) {
+ if (c.change().getDest().branch().equals(other)) {
assertThat(c.change().isMerged()).isTrue();
}
}
@@ -228,7 +270,11 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
@Test
public void mergeOnPushToBranchWithNewPatchset() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/master").group(adminGroupUuid()))
+ .update();
PushOneCommit.Result r = pushTo("refs/for/master");
r.assertOkStatus();
RevCommit c1 = r.getCommit();
@@ -256,13 +302,17 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
assertSubmitApproval(psId2);
assertThat(cd.patchSets()).hasSize(2);
- assertThat(cd.patchSet(psId1).getRevision().get()).isEqualTo(c1.name());
- assertThat(cd.patchSet(psId2).getRevision().get()).isEqualTo(c2.name());
+ assertThat(cd.patchSet(psId1).commitId()).isEqualTo(c1);
+ assertThat(cd.patchSet(psId2).commitId()).isEqualTo(c2);
}
@Test
public void mergeOnPushToBranchWithOldPatchset() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/master").group(adminGroupUuid()))
+ .update();
PushOneCommit.Result r = pushTo("refs/for/master");
r.assertOkStatus();
RevCommit c1 = r.getCommit();
@@ -273,26 +323,30 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
r = amendChange(changeId);
ChangeData cd = r.getChange();
PatchSet.Id psId2 = cd.change().currentPatchSetId();
- assertThat(psId2.getParentKey()).isEqualTo(psId1.getParentKey());
+ assertThat(psId2.changeId()).isEqualTo(psId1.changeId());
assertThat(psId2.get()).isEqualTo(2);
testRepo.reset(c1);
assertPushOk(pushHead(testRepo, "refs/heads/master", false), "refs/heads/master");
- cd = changeDataFactory.create(project, psId1.getParentKey());
+ cd = changeDataFactory.create(project, psId1.changeId());
Change c = cd.change();
assertThat(c.isMerged()).isTrue();
assertThat(c.currentPatchSetId()).isEqualTo(psId1);
- assertThat(cd.patchSets().stream().map(PatchSet::getId).collect(toList()))
+ assertThat(cd.patchSets().stream().map(PatchSet::id).collect(toList()))
.containsExactly(psId1, psId2);
}
@Test
public void mergeMultipleOnPushToBranchWithNewPatchset() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/master").group(adminGroupUuid()))
+ .update();
// Create 2 changes.
- ObjectId initialHead = getRemoteHead();
+ ObjectId initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result r1 = createChange("Change 1", "a", "a");
r1.assertOkStatus();
PushOneCommit.Result r2 = createChange("Change 2", "b", "b");
@@ -323,27 +377,96 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
assertThat(cd2.change().isMerged()).isTrue();
PatchSet.Id psId2_2 = cd2.change().currentPatchSetId();
assertThat(psId2_2.get()).isEqualTo(2);
- assertThat(cd2.patchSet(psId2_1).getRevision().get()).isEqualTo(c2_1.name());
- assertThat(cd2.patchSet(psId2_2).getRevision().get()).isEqualTo(c2_2.name());
+ assertThat(cd2.patchSet(psId2_1).commitId()).isEqualTo(c2_1);
+ assertThat(cd2.patchSet(psId2_2).commitId()).isEqualTo(c2_2);
ChangeData cd1 = r1.getChange();
assertThat(cd1.change().isMerged()).isTrue();
PatchSet.Id psId1_2 = cd1.change().currentPatchSetId();
assertThat(psId1_2.get()).isEqualTo(2);
- assertThat(cd1.patchSet(psId1_1).getRevision().get()).isEqualTo(c1_1.name());
- assertThat(cd1.patchSet(psId1_2).getRevision().get()).isEqualTo(c1_2.name());
+ assertThat(cd1.patchSet(psId1_1).commitId()).isEqualTo(c1_1);
+ assertThat(cd1.patchSet(psId1_2).commitId()).isEqualTo(c1_2);
+ }
+
+ @Test
+ public void pushForSubmitWithNotifyOption() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/master").group(adminGroupUuid()))
+ .update();
+
+ TestAccount user = accountCreator.user();
+ String pushSpec = "refs/for/master%reviewer=" + user.email();
+ sender.clear();
+
+ PushOneCommit.Result result = pushTo(pushSpec + ",submit,notify=" + NotifyHandling.NONE);
+ result.assertOkStatus();
+ assertThat(sender.getMessages()).isEmpty();
+
+ sender.clear();
+ result = pushTo(pushSpec + ",submit,notify=" + NotifyHandling.OWNER);
+ result.assertOkStatus();
+ assertThat(sender.getMessages()).isEmpty();
+
+ sender.clear();
+ result = pushTo(pushSpec + ",submit,notify=" + NotifyHandling.OWNER_REVIEWERS);
+ result.assertOkStatus();
+ assertThatEmailsForChangeCreationAndSubmitWereSent(user, null);
+
+ sender.clear();
+ result = pushTo(pushSpec + ",submit,notify=" + NotifyHandling.ALL);
+ result.assertOkStatus();
+ assertThatEmailsForChangeCreationAndSubmitWereSent(user, null);
+
+ sender.clear();
+ result = pushTo(pushSpec + ",submit"); // default is notify = ALL
+ result.assertOkStatus();
+ assertThatEmailsForChangeCreationAndSubmitWereSent(user, null);
+ }
+
+ @Test
+ public void pushForSubmitWithNotifyingUsersExplicitly() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/master").group(adminGroupUuid()))
+ .update();
+
+ TestAccount user = accountCreator.user();
+ String pushSpec = "refs/for/master%reviewer=" + user.email() + ",cc=" + user.email();
+
+ TestAccount user2 = accountCreator.user2();
+
+ sender.clear();
+ PushOneCommit.Result result =
+ pushTo(pushSpec + ",submit,notify=" + NotifyHandling.NONE + ",notify-to=" + user2.email());
+ result.assertOkStatus();
+ assertThatEmailsForChangeCreationAndSubmitWereSent(user2, RecipientType.TO);
+
+ sender.clear();
+ result =
+ pushTo(pushSpec + ",submit,notify=" + NotifyHandling.NONE + ",notify-cc=" + user2.email());
+ result.assertOkStatus();
+ assertThatEmailsForChangeCreationAndSubmitWereSent(user2, RecipientType.CC);
+
+ sender.clear();
+ result =
+ pushTo(pushSpec + ",submit,notify=" + NotifyHandling.NONE + ",notify-bcc=" + user2.email());
+ result.assertOkStatus();
+ assertThatEmailsForChangeCreationAndSubmitWereSent(user2, RecipientType.BCC);
}
private PatchSetApproval getSubmitter(PatchSet.Id patchSetId) throws Exception {
- ChangeNotes notes = notesFactory.createChecked(project, patchSetId.getParentKey()).load();
+ ChangeNotes notes = notesFactory.createChecked(project, patchSetId.changeId()).load();
return approvalsUtil.getSubmitter(notes, patchSetId);
}
private void assertSubmitApproval(PatchSet.Id patchSetId) throws Exception {
PatchSetApproval a = getSubmitter(patchSetId);
assertThat(a.isLegacySubmit()).isTrue();
- assertThat(a.getValue()).isEqualTo((short) 1);
- assertThat(a.getAccountId()).isEqualTo(admin.id());
+ assertThat(a.value()).isEqualTo((short) 1);
+ assertThat(a.accountId()).isEqualTo(admin.id());
}
private void assertCommit(Project.NameKey project, String branch) throws Exception {
@@ -381,4 +504,45 @@ public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content, changeId);
return push.to(ref);
}
+
+ /**
+ * Makes sure that two emails are sent: one for the change creation, and one for the submit.
+ *
+ * @param expected The account expected to receive message.
+ * @param expectedRecipientType The notification's type: To/Cc/Bcc. if {@code null} then it is not
+ * needed to check the recipientType. It is meant for -notify without other flags like
+ * notify-cc, notify-to, and notify-bcc. With the -notify flag, the message can sometimes be
+ * sent as "To" and sometimes can be sent as "Cc".
+ */
+ private void assertThatEmailsForChangeCreationAndSubmitWereSent(
+ TestAccount expected, @Nullable RecipientType expectedRecipientType) {
+ String expectedEmail = expected.email();
+ String expectedFullName = expected.fullName();
+ Address expectedAddress = new Address(expectedFullName, expectedEmail);
+ assertThat(sender.getMessages()).hasSize(2);
+ Message message = sender.getMessages().get(0);
+ assertThat(message.body().contains("review")).isTrue();
+ assertAddress(message, expectedAddress, expectedRecipientType);
+ message = sender.getMessages().get(1);
+ assertThat(message.rcpt()).containsExactly(expectedAddress);
+ assertAddress(message, expectedAddress, expectedRecipientType);
+ assertThat(message.body().contains("submitted")).isTrue();
+ }
+
+ private void assertAddress(
+ Message message, Address expectedAddress, @Nullable RecipientType expectedRecipientType) {
+ assertThat(message.rcpt()).containsExactly(expectedAddress);
+ if (expectedRecipientType != null
+ && expectedRecipientType
+ != RecipientType.BCC) { // When Bcc, it does not appear in the header.
+ String expectedRecipientTypeString = "To";
+ if (expectedRecipientType == RecipientType.CC) {
+ expectedRecipientTypeString = "Cc";
+ }
+ assertThat(
+ ((EmailHeader.AddressList) message.headers().get(expectedRecipientTypeString))
+ .getAddressList())
+ .containsExactly(expectedAddress);
+ }
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java b/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
index d1349d073a..01323a08a9 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
@@ -15,6 +15,8 @@
package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.Iterables;
@@ -22,8 +24,8 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.inject.Inject;
@@ -58,11 +60,12 @@ import org.junit.Before;
public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+
protected TestRepository<?> superRepo;
protected Project.NameKey superKey;
protected TestRepository<?> subRepo;
protected Project.NameKey subKey;
- @Inject protected ProjectOperations projectOperations;
protected SubmitType getSubmitType() {
return cfg.getEnum("project", null, "submitType", SubmitType.MERGE_IF_NECESSARY);
@@ -104,8 +107,12 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
}
protected void grantPush(Project.NameKey project) throws Exception {
- grant(project, "refs/heads/*", Permission.PUSH);
- grant(project, "refs/for/refs/heads/*", Permission.SUBMIT);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/*").group(adminGroupUuid()))
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/*").group(adminGroupUuid()))
+ .update();
}
protected Project.NameKey createProjectForPush(SubmitType submitType) throws Exception {
@@ -472,7 +479,7 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
ObjectInserter ins = serverRepo.newObjectInserter();
RevWalk rw = new RevWalk(serverRepo)) {
Ref ref = serverRepo.exactRef(refName);
- assertThat(ref).named(refName).isNotNull();
+ assertWithMessage(refName).that(ref).isNotNull();
ObjectId oldCommitId = ref.getObjectId();
DirCache dc = DirCache.newInCore();
diff --git a/javatests/com/google/gerrit/acceptance/git/BUILD b/javatests/com/google/gerrit/acceptance/git/BUILD
index 24a83e0b33..ef54c92639 100644
--- a/javatests/com/google/gerrit/acceptance/git/BUILD
+++ b/javatests/com/google/gerrit/acceptance/git/BUILD
@@ -8,6 +8,8 @@ load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
deps = [
":push_for_review",
":submodule_util",
+ "//java/com/google/gerrit/git",
+ "//java/com/google/gerrit/server/git/receive/testing",
"//lib/commons:lang",
],
) for f in glob(["*IT.java"])]
@@ -18,6 +20,7 @@ java_library(
srcs = glob(["Abstract*.java"]),
deps = [
"//java/com/google/gerrit/acceptance:lib",
+ "//java/com/google/gerrit/git",
"//java/com/google/gerrit/mail",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java b/javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java
index ac0cbd83e3..e2aa666275 100644
--- a/javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java
@@ -14,6 +14,9 @@
package com.google.gerrit.acceptance.git;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
import com.google.gerrit.acceptance.AbstractDaemonTest;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.junit.TestRepository;
@@ -50,8 +53,15 @@ public class GitmodulesIT extends AbstractDaemonTest {
.add(".gitmodules", config.toText())
.create();
- exception.expectMessage(expectedErrorMessage);
- exception.expect(TransportException.class);
- repo.git().push().setRemote("origin").setRefSpecs(new RefSpec("HEAD:refs/for/master")).call();
+ TransportException thrown =
+ assertThrows(
+ TransportException.class,
+ () ->
+ repo.git()
+ .push()
+ .setRemote("origin")
+ .setRefSpecs(new RefSpec("HEAD:refs/for/master"))
+ .call());
+ assertThat(thrown).hasMessageThat().contains(expectedErrorMessage);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java b/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
index 6516b32eb5..b51263e596 100644
--- a/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
@@ -19,8 +19,9 @@ import static com.google.gerrit.acceptance.GitUtil.pushHead;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
+import com.google.gerrit.git.ObjectIds;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
@@ -76,8 +77,9 @@ public class ImplicitMergeCheckIT extends AbstractDaemonTest {
assertThat(c.getMessage().toLowerCase()).doesNotContain(implicitMergeOf(m.getCommit()));
}
- private static String implicitMergeOf(ObjectId commit) {
- return "implicit merge of " + commit.abbreviate(7).name();
+ private String implicitMergeOf(ObjectId commit) throws Exception {
+ return "implicit merge of "
+ + ObjectIds.abbreviateName(commit, testRepo.getRevWalk().getObjectReader());
}
private void setRejectImplicitMerges() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/git/PushAccountIT.java b/javatests/com/google/gerrit/acceptance/git/PushAccountIT.java
new file mode 100644
index 0000000000..2f677e2fd0
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/git/PushAccountIT.java
@@ -0,0 +1,778 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.git;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.fetch;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountIndexedCounter;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.account.AccountProperties;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.ProjectWatches;
+import com.google.gerrit.server.account.ProjectWatches.NotifyType;
+import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.server.util.MagicBranch;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.util.EnumSet;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Test;
+
+/** Tests account behavior when users push to accounts refs. */
+public class PushAccountIT extends AbstractDaemonTest {
+
+ @ConfigSuite.Default
+ public static Config enableSignedPushConfig() {
+ return defaultConfig();
+ }
+
+ @ConfigSuite.Config
+ public static Config disableInMemoryRefCache() {
+ // Run these tests for both enabled and disabled in-memory ref caches. This is an implementation
+ // detail of ReceiveCommits that makes the logic either base it's computation on previously
+ // advertised refs or a make it query a ref database.
+ Config cfg = defaultConfig();
+ cfg.setBoolean("receive", null, "enableInMemoryRefCache", false);
+ return cfg;
+ }
+
+ private static Config defaultConfig() {
+ Config cfg = new Config();
+ cfg.setBoolean("receive", null, "enableSignedPush", true);
+
+ // Disable the staleness checker so that tests that verify the number of expected index events
+ // are stable.
+ cfg.setBoolean("index", null, "autoReindexIfStale", false);
+
+ return cfg;
+ }
+
+ @Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
+ @Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private Sequences seq;
+
+ @Test
+ public void pushToUserBranch() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
+ allUsersRepo.reset("userRef");
+ PushOneCommit push = pushFactory.create(admin.newIdent(), allUsersRepo);
+ push.to(RefNames.refsUsers(admin.id())).assertOkStatus();
+ accountIndexedCounter.assertReindexOf(admin);
+
+ push = pushFactory.create(admin.newIdent(), allUsersRepo);
+ push.to(RefNames.REFS_USERS_SELF).assertOkStatus();
+ accountIndexedCounter.assertReindexOf(admin);
+ }
+ }
+
+ @Test
+ public void pushToUserBranchForReview() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String userRefName = RefNames.refsUsers(admin.id());
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, userRefName + ":userRef");
+ allUsersRepo.reset("userRef");
+ PushOneCommit push = pushFactory.create(admin.newIdent(), allUsersRepo);
+ PushOneCommit.Result r = push.to(MagicBranch.NEW_CHANGE + userRefName);
+ r.assertOkStatus();
+ accountIndexedCounter.assertNoReindex();
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(userRefName);
+ gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).current().submit();
+ accountIndexedCounter.assertReindexOf(admin);
+
+ push = pushFactory.create(admin.newIdent(), allUsersRepo);
+ r = push.to(MagicBranch.NEW_CHANGE + RefNames.REFS_USERS_SELF);
+ r.assertOkStatus();
+ accountIndexedCounter.assertNoReindex();
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(userRefName);
+ gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).current().submit();
+ accountIndexedCounter.assertReindexOf(admin);
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchForReviewAndSubmit() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String userRef = RefNames.refsUsers(admin.id());
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_STATUS, "out-of-office");
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(MagicBranch.NEW_CHANGE + userRef);
+ r.assertOkStatus();
+ accountIndexedCounter.assertNoReindex();
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(userRef);
+
+ gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).current().submit();
+ accountIndexedCounter.assertReindexOf(admin);
+
+ AccountInfo info = gApi.accounts().self().get();
+ assertThat(info.email).isEqualTo(admin.email());
+ assertThat(info.name).isEqualTo(admin.fullName());
+ assertThat(info.status).isEqualTo("out-of-office");
+ }
+ }
+
+ @Test
+ public void pushAccountConfigWithPrefEmailThatDoesNotExistAsExtIdToUserBranchForReviewAndSubmit()
+ throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
+ String userRef = RefNames.refsUsers(foo.id());
+ accountIndexedCounter.clear();
+
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ String email = "some.email@example.com";
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_PREFERRED_EMAIL, email);
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ foo.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(MagicBranch.NEW_CHANGE + userRef);
+ r.assertOkStatus();
+ accountIndexedCounter.assertNoReindex();
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(userRef);
+
+ requestScopeOperations.setApiUser(foo.id());
+ gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).current().submit();
+
+ accountIndexedCounter.assertReindexOf(foo);
+
+ AccountInfo info = gApi.accounts().self().get();
+ assertThat(info.email).isEqualTo(email);
+ assertThat(info.name).isEqualTo(foo.fullName());
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfConfigIsInvalid()
+ throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String userRef = RefNames.refsUsers(admin.id());
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ "invalid config")
+ .to(MagicBranch.NEW_CHANGE + userRef);
+ r.assertOkStatus();
+ accountIndexedCounter.assertNoReindex();
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(userRef);
+
+ gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r.getChangeId()).current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format(
+ "invalid account configuration: commit '%s' has an invalid '%s' file for account"
+ + " '%s': Invalid config file %s in commit %s",
+ r.getCommit().name(),
+ AccountProperties.ACCOUNT_CONFIG,
+ admin.id(),
+ AccountProperties.ACCOUNT_CONFIG,
+ r.getCommit().name()));
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfPreferredEmailIsInvalid()
+ throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String userRef = RefNames.refsUsers(admin.id());
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ String noEmail = "no.email";
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_PREFERRED_EMAIL, noEmail);
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(MagicBranch.NEW_CHANGE + userRef);
+ r.assertOkStatus();
+ accountIndexedCounter.assertNoReindex();
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(userRef);
+
+ gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r.getChangeId()).current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format(
+ "invalid account configuration: invalid preferred email '%s' for account '%s'",
+ noEmail, admin.id()));
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfOwnAccountIsDeactivated()
+ throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ String userRef = RefNames.refsUsers(admin.id());
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setBoolean(AccountProperties.ACCOUNT, null, AccountProperties.KEY_ACTIVE, false);
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(MagicBranch.NEW_CHANGE + userRef);
+ r.assertOkStatus();
+ accountIndexedCounter.assertNoReindex();
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(userRef);
+
+ gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.changes().id(r.getChangeId()).current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("invalid account configuration: cannot deactivate own account");
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchForReviewDeactivateOtherAccount() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
+
+ TestAccount foo = accountCreator.create(name("foo"));
+ assertThat(gApi.accounts().id(foo.id().get()).getActive()).isTrue();
+ String userRef = RefNames.refsUsers(foo.id());
+ accountIndexedCounter.clear();
+
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref(userRef).group(adminGroupUuid()))
+ .add(allowLabel("Code-Review").ref(userRef).group(adminGroupUuid()).range(-2, 2))
+ .add(allow(Permission.SUBMIT).ref(userRef).group(adminGroupUuid()))
+ .update();
+
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setBoolean(AccountProperties.ACCOUNT, null, AccountProperties.KEY_ACTIVE, false);
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(MagicBranch.NEW_CHANGE + userRef);
+ r.assertOkStatus();
+ accountIndexedCounter.assertNoReindex();
+ assertThat(r.getChange().change().getDest().branch()).isEqualTo(userRef);
+
+ gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(r.getChangeId()).current().submit();
+ accountIndexedCounter.assertReindexOf(foo);
+
+ assertThat(gApi.accounts().id(foo.id().get()).getActive()).isFalse();
+ }
+ }
+
+ @Test
+ public void pushWatchConfigToUserBranch() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ Config wc = new Config();
+ wc.setString(
+ ProjectWatches.PROJECT,
+ project.get(),
+ ProjectWatches.KEY_NOTIFY,
+ ProjectWatches.NotifyValue.create(null, EnumSet.of(NotifyType.ALL_COMMENTS)).toString());
+ PushOneCommit push =
+ pushFactory.create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Add project watch",
+ ProjectWatches.WATCH_CONFIG,
+ wc.toText());
+ push.to(RefNames.REFS_USERS_SELF).assertOkStatus();
+ accountIndexedCounter.assertReindexOf(admin);
+
+ String invalidNotifyValue = "]invalid[";
+ wc.setString(
+ ProjectWatches.PROJECT, project.get(), ProjectWatches.KEY_NOTIFY, invalidNotifyValue);
+ push =
+ pushFactory.create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Add invalid project watch",
+ ProjectWatches.WATCH_CONFIG,
+ wc.toText());
+ PushOneCommit.Result r = push.to(RefNames.REFS_USERS_SELF);
+ r.assertErrorStatus("invalid account configuration");
+ r.assertMessage(
+ String.format(
+ "%s: Invalid project watch of account %d for project %s: %s",
+ ProjectWatches.WATCH_CONFIG, admin.id().get(), project.get(), invalidNotifyValue));
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranch() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestAccount oooUser = accountCreator.create("away", "away@mail.invalid", "Ambrose Way");
+ requestScopeOperations.setApiUser(oooUser.id());
+
+ // Must clone as oooUser to ensure the push is allowed.
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, oooUser);
+ fetch(allUsersRepo, RefNames.refsUsers(oooUser.id()) + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_STATUS, "out-of-office");
+
+ accountIndexedCounter.clear();
+ pushFactory
+ .create(
+ oooUser.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(RefNames.refsUsers(oooUser.id()))
+ .assertOkStatus();
+
+ accountIndexedCounter.assertReindexOf(oooUser);
+
+ AccountInfo info = gApi.accounts().self().get();
+ assertThat(info.email).isEqualTo(oooUser.email());
+ assertThat(info.name).isEqualTo(oooUser.fullName());
+ assertThat(info.status).isEqualTo("out-of-office");
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchIsRejectedIfConfigIsInvalid() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ "invalid config")
+ .to(RefNames.REFS_USERS_SELF);
+ r.assertErrorStatus("invalid account configuration");
+ r.assertMessage(
+ String.format(
+ "commit '%s' has an invalid '%s' file for account '%s':"
+ + " Invalid config file %s in commit %s",
+ r.getCommit().name(),
+ AccountProperties.ACCOUNT_CONFIG,
+ admin.id(),
+ AccountProperties.ACCOUNT_CONFIG,
+ r.getCommit().name()));
+ accountIndexedCounter.assertNoReindex();
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchIsRejectedIfPreferredEmailIsInvalid() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ String noEmail = "no.email";
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_PREFERRED_EMAIL, noEmail);
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(RefNames.REFS_USERS_SELF);
+ r.assertErrorStatus("invalid account configuration");
+ r.assertMessage(
+ String.format("invalid preferred email '%s' for account '%s'", noEmail, admin.id()));
+ accountIndexedCounter.assertNoReindex();
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchInvalidPreferredEmailButNotChanged() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
+ String userRef = RefNames.refsUsers(foo.id());
+
+ String noEmail = "no.email";
+ accountsUpdateProvider
+ .get()
+ .update("Set Preferred Email", foo.id(), u -> u.setPreferredEmail(noEmail));
+ accountIndexedCounter.clear();
+
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref(userRef).group(REGISTERED_USERS))
+ .update();
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ String status = "in vacation";
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_STATUS, status);
+
+ pushFactory
+ .create(
+ foo.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(userRef)
+ .assertOkStatus();
+ accountIndexedCounter.assertReindexOf(foo);
+
+ AccountInfo info = gApi.accounts().id(foo.id().get()).get();
+ assertThat(info.email).isEqualTo(noEmail);
+ assertThat(info.name).isEqualTo(foo.fullName());
+ assertThat(info.status).isEqualTo(status);
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchIfPreferredEmailDoesNotExistAsExtId() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
+ String userRef = RefNames.refsUsers(foo.id());
+ accountIndexedCounter.clear();
+
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref(userRef).group(adminGroupUuid()))
+ .update();
+
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ String email = "some.email@example.com";
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setString(AccountProperties.ACCOUNT, null, AccountProperties.KEY_PREFERRED_EMAIL, email);
+
+ pushFactory
+ .create(
+ foo.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(userRef)
+ .assertOkStatus();
+ accountIndexedCounter.assertReindexOf(foo);
+
+ AccountInfo info = gApi.accounts().id(foo.id().get()).get();
+ assertThat(info.email).isEqualTo(email);
+ assertThat(info.name).isEqualTo(foo.fullName());
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchIsRejectedIfOwnAccountIsDeactivated() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setBoolean(AccountProperties.ACCOUNT, null, AccountProperties.KEY_ACTIVE, false);
+
+ PushOneCommit.Result r =
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(RefNames.REFS_USERS_SELF);
+ r.assertErrorStatus("invalid account configuration");
+ r.assertMessage("cannot deactivate own account");
+ accountIndexedCounter.assertNoReindex();
+ }
+ }
+
+ @Test
+ public void pushAccountConfigToUserBranchDeactivateOtherAccount() throws Exception {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
+
+ TestAccount foo = accountCreator.create(name("foo"));
+ assertThat(gApi.accounts().id(foo.id().get()).getActive()).isTrue();
+ String userRef = RefNames.refsUsers(foo.id());
+ accountIndexedCounter.clear();
+
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref(userRef).group(adminGroupUuid()))
+ .update();
+
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ fetch(allUsersRepo, userRef + ":userRef");
+ allUsersRepo.reset("userRef");
+
+ Config ac = getAccountConfig(allUsersRepo);
+ ac.setBoolean(AccountProperties.ACCOUNT, null, AccountProperties.KEY_ACTIVE, false);
+
+ pushFactory
+ .create(
+ admin.newIdent(),
+ allUsersRepo,
+ "Update account config",
+ AccountProperties.ACCOUNT_CONFIG,
+ ac.toText())
+ .to(userRef)
+ .assertOkStatus();
+ accountIndexedCounter.assertReindexOf(foo);
+
+ assertThat(gApi.accounts().id(foo.id().get()).getActive()).isFalse();
+ }
+ }
+
+ @Test
+ public void cannotCreateNonUserBranchUnderRefsUsersWithAccessDatabaseCapability()
+ throws Exception {
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_USERS + "*").group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_USERS + "*").group(adminGroupUuid()))
+ .update();
+
+ String userRef = RefNames.REFS_USERS + "foo";
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef);
+ r.assertErrorStatus();
+ assertThat(r.getMessage()).contains("Not allowed to create non-user branch under refs/users/.");
+
+ try (Repository repo = repoManager.openRepository(allUsers)) {
+ assertThat(repo.exactRef(userRef)).isNull();
+ }
+ }
+
+ @Test
+ public void cannotCreateUserBranch() throws Exception {
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_USERS + "*").group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_USERS + "*").group(adminGroupUuid()))
+ .update();
+
+ String userRef = RefNames.refsUsers(Account.id(seq.nextAccountId()));
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef);
+ r.assertErrorStatus();
+ assertThat(r.getMessage()).contains("Not allowed to create user branch.");
+
+ try (Repository repo = repoManager.openRepository(allUsers)) {
+ assertThat(repo.exactRef(userRef)).isNull();
+ }
+ }
+
+ @Test
+ public void createUserBranchWithAccessDatabaseCapability() throws Exception {
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_USERS + "*").group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_USERS + "*").group(adminGroupUuid()))
+ .update();
+
+ String userRef = RefNames.refsUsers(Account.id(seq.nextAccountId()));
+ TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
+ pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef).assertOkStatus();
+
+ try (Repository repo = repoManager.openRepository(allUsers)) {
+ assertThat(repo.exactRef(userRef)).isNotNull();
+ }
+ }
+
+ private Config getAccountConfig(TestRepository<?> allUsersRepo) throws Exception {
+ Config ac = new Config();
+ try (TreeWalk tw =
+ TreeWalk.forPath(
+ allUsersRepo.getRepository(),
+ AccountProperties.ACCOUNT_CONFIG,
+ getHead(allUsersRepo.getRepository(), "HEAD").getTree())) {
+ assertThat(tw).isNotNull();
+ ac.fromText(
+ new String(
+ allUsersRepo
+ .getRevWalk()
+ .getObjectReader()
+ .open(tw.getObjectId(0), OBJ_BLOB)
+ .getBytes(),
+ UTF_8));
+ }
+ return ac;
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
index cc19ad2a65..f9c751fe7c 100644
--- a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
@@ -16,7 +16,7 @@ package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.git.testing.PushResultSubject.assertThat;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static java.util.stream.Collectors.toList;
@@ -24,19 +24,17 @@ import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.ReviewInput;
-import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.extensions.common.ChangeInput;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.testing.Util;
import com.google.inject.Inject;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -54,14 +52,13 @@ import org.junit.Before;
import org.junit.Test;
public class PushPermissionsIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Before
public void setUp() throws Exception {
try (ProjectConfigUpdate u = updateProject(allProjects)) {
ProjectConfig cfg = u.getConfig();
- cfg.getProject()
- .setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, InheritableBoolean.FALSE);
// Remove push-related permissions, so they can be added back individually by test methods.
removeAllBranchPermissions(
@@ -73,13 +70,15 @@ public class PushPermissionsIT extends AbstractDaemonTest {
Permission.PUSH_MERGE,
Permission.SUBMIT);
removeAllGlobalCapabilities(cfg, GlobalCapability.ADMINISTRATE_SERVER);
-
- // Include some auxiliary permissions.
- Util.allow(cfg, Permission.FORGE_AUTHOR, REGISTERED_USERS, "refs/*");
- Util.allow(cfg, Permission.FORGE_COMMITTER, REGISTERED_USERS, "refs/*");
-
u.save();
}
+
+ // Include some auxiliary permissions.
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allow(Permission.FORGE_AUTHOR).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.FORGE_COMMITTER).ref("refs/*").group(REGISTERED_USERS))
+ .update();
}
@Test
@@ -94,17 +93,6 @@ public class PushPermissionsIT extends AbstractDaemonTest {
}
@Test
- public void mixingDirectChangesAndRegularPush() throws Exception {
- testRepo.branch("HEAD").commit().create();
- PushResult r = push("HEAD:refs/heads/master", "HEAD:refs/changes/01/101");
-
- String msg = "cannot combine normal pushes and magic pushes";
- assertThat(r.getRemoteUpdate("refs/heads/master")).isNotEqualTo(Status.OK);
- assertThat(r.getRemoteUpdate("refs/changes/01/101")).isNotEqualTo(Status.OK);
- assertThat(r.getRemoteUpdate("refs/heads/master").getMessage()).isEqualTo(msg);
- }
-
- @Test
public void fastForwardUpdateDenied() throws Exception {
testRepo.branch("HEAD").commit().create();
PushResult r = push("HEAD:refs/heads/master");
@@ -202,7 +190,11 @@ public class PushPermissionsIT extends AbstractDaemonTest {
@Test
public void refsMetaConfigUpdateRequiresProjectOwner() throws Exception {
- grant(project, "refs/meta/config", Permission.PUSH, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/meta/config").group(REGISTERED_USERS))
+ .update();
forceFetch("refs/meta/config");
ObjectId commit = testRepo.branch("refs/meta/config").commit().create();
@@ -222,7 +214,11 @@ public class PushPermissionsIT extends AbstractDaemonTest {
"Contact an administrator to fix the permissions");
assertThat(r).hasProcessed(ImmutableMap.of("refs", 1));
- grant(project, "refs/*", Permission.OWNER, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// Re-fetch refs/meta/config from the server because the grant changed it, and we want a
// fast-forward.
@@ -249,9 +245,14 @@ public class PushPermissionsIT extends AbstractDaemonTest {
@Test
public void updateBySubmitDenied() throws Exception {
- grant(project, "refs/for/refs/heads/*", Permission.PUSH, false, REGISTERED_USERS);
-
- ObjectId commit = testRepo.branch("HEAD").commit().create();
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/for/refs/heads/*").group(REGISTERED_USERS))
+ .update();
+
+ ObjectId commit =
+ testRepo.branch("HEAD").commit().message("test commit").insertChangeId().create();
assertThat(push("HEAD:refs/for/master")).onlyRef("refs/for/master").isOk();
gApi.changes().id(commit.name()).current().review(ReviewInput.approve());
@@ -267,18 +268,23 @@ public class PushPermissionsIT extends AbstractDaemonTest {
@Test
public void addPatchSetDenied() throws Exception {
- grant(project, "refs/for/refs/heads/*", Permission.PUSH, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/for/refs/heads/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
ChangeInput ci = new ChangeInput();
ci.project = project.get();
ci.branch = "master";
ci.subject = "A change";
- Change.Id id = new Change.Id(gApi.changes().create(ci).get()._number);
+ Change.Id id = Change.id(gApi.changes().create(ci).get()._number);
requestScopeOperations.setApiUser(admin.id());
- ObjectId ps1Id = forceFetch(new PatchSet.Id(id, 1).toRefName());
+ ObjectId ps1Id = forceFetch(PatchSet.id(id, 1).toRefName());
ObjectId ps2Id = testRepo.amend(ps1Id).add("file", "content").create();
PushResult r = push(ps2Id.name() + ":refs/for/master");
+ // Admin had ADD_PATCH_SET removed in setup.
assertThat(r)
.onlyRef("refs/for/master")
.isRejected("cannot add patch set to " + id.get() + ".");
@@ -288,7 +294,11 @@ public class PushPermissionsIT extends AbstractDaemonTest {
@Test
public void skipValidationDenied() throws Exception {
- grant(project, "refs/heads/*", Permission.PUSH, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
testRepo.branch("HEAD").commit().create();
PushResult r =
@@ -305,7 +315,11 @@ public class PushPermissionsIT extends AbstractDaemonTest {
@Test
public void accessDatabaseForNoteDbDenied() throws Exception {
- grant(project, "refs/heads/*", Permission.PUSH, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
testRepo.branch("HEAD").commit().create();
PushResult r =
@@ -322,8 +336,12 @@ public class PushPermissionsIT extends AbstractDaemonTest {
@Test
public void administrateServerForUpdateParentDenied() throws Exception {
- grant(project, "refs/meta/config", Permission.PUSH, false, REGISTERED_USERS);
- grant(project, "refs/*", Permission.OWNER, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/meta/config").group(REGISTERED_USERS))
+ .add(allow(Permission.OWNER).ref("refs/*").group(REGISTERED_USERS))
+ .update();
String project2 = name("project2");
gApi.projects().create(project2);
@@ -398,7 +416,7 @@ public class PushPermissionsIT extends AbstractDaemonTest {
case REJECTED_OTHER_REASON:
case RENAMED:
default:
- assert_().fail("fetch failed to update local %s: %s", ref, u.getResult());
+ assertWithMessage("fetch failed to update local %s: %s", ref, u.getResult()).fail();
break;
}
return u.getNewObjectId();
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index 5bed77c41f..ea9f48eb2c 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -18,11 +18,14 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.acceptance.GitUtil.fetch;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
-import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -30,28 +33,29 @@ import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.receive.ReceiveCommitsAdvertiseRefsHook;
+import com.google.gerrit.server.git.receive.ReceiveCommitsAdvertiseRefsHookChain;
+import com.google.gerrit.server.git.receive.testing.TestRefAdvertiser;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Inject;
@@ -71,6 +75,9 @@ import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.AdvertiseRefsHook;
+import org.eclipse.jgit.transport.ReceivePack;
import org.junit.Before;
import org.junit.Test;
@@ -79,11 +86,15 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Inject private AllUsersName allUsersName;
@Inject private ChangeNoteUtil noteUtil;
@Inject private PermissionBackend permissionBackend;
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
private AccountGroup.UUID admins;
private AccountGroup.UUID nonInteractiveUsers;
+ private RevCommit rcMaster;
+ private RevCommit rcBranch;
+
private ChangeData cd1;
private String psRef1;
private String metaRef1;
@@ -122,9 +133,12 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
for (AccessSection sec : u.getConfig().getAccessSections()) {
sec.removePermission(Permission.READ);
}
- Util.allow(u.getConfig(), Permission.READ, admins, "refs/*");
u.save();
}
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(admins))
+ .update();
// Remove all read permissions on All-Users.
try (ProjectConfigUpdate u = updateProject(allUsers)) {
@@ -135,60 +149,96 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
}
+ // Building the following:
+ // rcMaster (c1 master master-tag) <-- rcBranch (c2 branch branch-tag)
+ // \ \
+ // (c3_open) (c4_open)
+ //
private void setUpChanges() throws Exception {
gApi.projects().name(project.get()).branch("branch").create(new BranchInput());
// First 2 changes are merged, which means the tags pointing to them are
// visible.
- allow("refs/for/refs/heads/*", Permission.SUBMIT, admins);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/*").group(admins))
+ .update();
+
+ // rcMaster (c1 master)
PushOneCommit.Result mr =
pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%submit");
mr.assertOkStatus();
cd1 = mr.getChange();
- psRef1 = cd1.currentPatchSet().getId().toRefName();
+ rcMaster = mr.getCommit();
+ psRef1 = cd1.currentPatchSet().id().toRefName();
metaRef1 = RefNames.changeMetaRef(cd1.getId());
+
+ // rcMaster (c1 master) <-- rcBranch (c2 branch)
PushOneCommit.Result br =
pushFactory.create(admin.newIdent(), testRepo).to("refs/for/branch%submit");
br.assertOkStatus();
cd2 = br.getChange();
- psRef2 = cd2.currentPatchSet().getId().toRefName();
+ rcBranch = br.getCommit();
+ psRef2 = cd2.currentPatchSet().id().toRefName();
metaRef2 = RefNames.changeMetaRef(cd2.getId());
// Second 2 changes are unmerged.
+ // rcMaster (c1 master) <-- rcBranch (c2 branch)
+ // \
+ // (c3_open)
+ //
mr = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
mr.assertOkStatus();
cd3 = mr.getChange();
- psRef3 = cd3.currentPatchSet().getId().toRefName();
+ psRef3 = cd3.currentPatchSet().id().toRefName();
metaRef3 = RefNames.changeMetaRef(cd3.getId());
+
+ // rcMaster (c1 master) <-- rcBranch (c2 branch)
+ // \ \
+ // (c3_open) (c4_open)
br = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/branch");
br.assertOkStatus();
cd4 = br.getChange();
- psRef4 = cd4.currentPatchSet().getId().toRefName();
+ psRef4 = cd4.currentPatchSet().id().toRefName();
metaRef4 = RefNames.changeMetaRef(cd4.getId());
try (Repository repo = repoManager.openRepository(project)) {
- // master-tag -> master
+ // rcMaster (c1 master master-tag) <-- rcBranch (c2 branch)
+ // \ \
+ // (c3_open) (c4_open)
RefUpdate mtu = repo.updateRef("refs/tags/master-tag");
mtu.setExpectedOldObjectId(ObjectId.zeroId());
mtu.setNewObjectId(repo.exactRef("refs/heads/master").getObjectId());
assertThat(mtu.update()).isEqualTo(RefUpdate.Result.NEW);
- // branch-tag -> branch
+ // rcMaster (c1 master master-tag) <-- rcBranch (c2 branch branch-tag)
+ // \ \
+ // (c3_open) (c4_open)
RefUpdate btu = repo.updateRef("refs/tags/branch-tag");
btu.setExpectedOldObjectId(ObjectId.zeroId());
btu.setNewObjectId(repo.exactRef("refs/heads/branch").getObjectId());
assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+
+ // Create a tag for the tree of the commit on 'master'
+ // tree-tag -> master.tree
+ RefUpdate ttu = repo.updateRef("refs/tags/tree-tag");
+ ttu.setExpectedOldObjectId(ObjectId.zeroId());
+ ttu.setNewObjectId(rcMaster.getTree().toObjectId());
+ assertThat(ttu.update()).isEqualTo(RefUpdate.Result.NEW);
}
}
@Test
+ @GerritConfig(name = "auth.skipFullRefEvaluationIfAllRefsAreVisible", value = "false")
public void uploadPackAllRefsVisibleNoRefsMetaConfig() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(u.getConfig(), Permission.READ, REGISTERED_USERS, "refs/*");
- Util.allow(u.getConfig(), Permission.READ, admins, RefNames.REFS_CONFIG);
- Util.doNotInherit(u.getConfig(), Permission.READ, RefNames.REFS_CONFIG);
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(admins))
+ .setExclusiveGroup(permissionKey(Permission.READ).ref(RefNames.REFS_CONFIG), true)
+ .update();
requestScopeOperations.setApiUser(user.id());
assertUploadPackRefs(
@@ -205,12 +255,46 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
"refs/heads/master",
"refs/tags/branch-tag",
"refs/tags/master-tag");
+ // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead.
+ }
+
+ @Test
+ @GerritConfig(name = "auth.skipFullRefEvaluationIfAllRefsAreVisible", value = "true")
+ public void uploadPackAllRefsVisibleNoRefsMetaConfigSkipFullRefEval() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(admins))
+ .setExclusiveGroup(permissionKey(Permission.READ).ref(RefNames.REFS_CONFIG), true)
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+ assertUploadPackRefs(
+ "HEAD",
+ psRef1,
+ metaRef1,
+ psRef2,
+ metaRef2,
+ psRef3,
+ metaRef3,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/heads/master",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag",
+ "refs/tags/tree-tag");
}
@Test
public void uploadPackAllRefsVisibleWithRefsMetaConfig() throws Exception {
- allow("refs/*", Permission.READ, REGISTERED_USERS);
- allow(RefNames.REFS_CONFIG, Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(REGISTERED_USERS))
+ .update();
assertUploadPackRefs(
"HEAD",
@@ -226,23 +310,46 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
"refs/heads/master",
RefNames.REFS_CONFIG,
"refs/tags/branch-tag",
- "refs/tags/master-tag");
+ "refs/tags/master-tag",
+ "refs/tags/tree-tag");
+ }
+
+ @Test
+ public void grantReadOnRefsTagsIsNoOp() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/tags/*").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+ assertUploadPackRefs(); // We expect no refs returned
}
@Test
public void uploadPackSubsetOfBranchesVisibleIncludingHead() throws Exception {
- allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
- deny("refs/heads/branch", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .add(deny(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
assertUploadPackRefs(
"HEAD", psRef1, metaRef1, psRef3, metaRef3, "refs/heads/master", "refs/tags/master-tag");
+ // tree-tag is not visible because we don't look at trees reachable from
+ // refs
}
@Test
public void uploadPackSubsetOfBranchesVisibleNotIncludingHead() throws Exception {
- deny("refs/heads/master", Permission.READ, REGISTERED_USERS);
- allow("refs/heads/branch", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(deny(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
assertUploadPackRefs(
@@ -255,11 +362,16 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
// master branch is not visible but master-tag is reachable from branch
// (since PushOneCommit always bases changes on each other).
"refs/tags/master-tag");
+ // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead.
}
@Test
public void uploadPackSubsetOfBranchesVisibleWithEdit() throws Exception {
- allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
// Admin's edit is not visible.
requestScopeOperations.setApiUser(admin.id());
@@ -278,12 +390,17 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
"refs/heads/master",
"refs/tags/master-tag",
"refs/users/01/1000001/edit-" + cd3.getId() + "/1");
+ // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead.
}
@Test
public void uploadPackSubsetOfBranchesAndEditsVisibleWithViewPrivateChanges() throws Exception {
- allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
- allow("refs/*", Permission.VIEW_PRIVATE_CHANGES, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .add(allow(Permission.VIEW_PRIVATE_CHANGES).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// Admin's edit on change3 is visible.
requestScopeOperations.setApiUser(admin.id());
@@ -306,13 +423,21 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
"refs/tags/master-tag",
"refs/users/00/1000000/edit-" + cd3.getId() + "/1",
"refs/users/01/1000001/edit-" + cd3.getId() + "/1");
+ // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead.
}
@Test
public void uploadPackSubsetOfRefsVisibleWithAccessDatabase() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- deny("refs/heads/master", Permission.READ, REGISTERED_USERS);
- allow("refs/heads/branch", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(deny(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(cd3.getId().get()).edit().create();
@@ -335,6 +460,7 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
"refs/tags/master-tag",
// All edits are visible due to accessDatabase capability.
"refs/users/00/1000000/edit-" + cd3.getId() + "/1");
+ // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead.
}
@Test
@@ -349,7 +475,11 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
private void uploadPackNoSearchingChangeCacheImpl() throws Exception {
- allow("refs/heads/*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
assertRefs(
@@ -370,21 +500,29 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
"refs/heads/master",
"refs/tags/branch-tag",
"refs/tags/master-tag");
+ // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead.
}
@Test
public void uploadPackSequencesWithAccessDatabase() throws Exception {
assertRefs(allProjects, user, true);
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
assertRefs(allProjects, user, true, "refs/sequences/changes");
}
@Test
public void uploadPackAllRefsAreVisibleOrphanedTag() throws Exception {
- allow("refs/*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// Delete the pending change on 'branch' and 'branch' itself so that the tag gets orphaned
- gApi.changes().id(cd4.getId().id).delete();
+ gApi.changes().id(cd4.getId().get()).delete();
gApi.projects().name(project.get()).branch("refs/heads/branch").delete();
requestScopeOperations.setApiUser(user.id());
@@ -399,28 +537,517 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
metaRef3,
"refs/heads/master",
"refs/tags/branch-tag",
+ "refs/tags/master-tag",
+ "refs/tags/tree-tag");
+ }
+
+ @Test
+ public void uploadPackSubsetRefsVisibleOrphanedTagInvisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
+ // Create a tag for the pending change on 'branch' so that the tag is orphaned
+ try (Repository repo = repoManager.openRepository(project)) {
+ // change4-tag -> psRef4
+ RefUpdate ctu = repo.updateRef("refs/tags/change4-tag");
+ ctu.setExpectedOldObjectId(ObjectId.zeroId());
+ ctu.setNewObjectId(repo.exactRef(psRef4).getObjectId());
+ assertThat(ctu.update()).isEqualTo(RefUpdate.Result.NEW);
+ }
+
+ requestScopeOperations.setApiUser(user.id());
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+ }
+
+ // first ls-remote: rcMaster (c1 master)
+ // second ls-remote: rcMaster (c1 master) <- newchange1 (master-newtag)
+ @Test
+ public void uploadPackNewCommitOrphanTagInvisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ // rcMaster (c1 master)
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ PushOneCommit.Result r =
+ pushFactory.create(admin.newIdent(), testRepo).setParent(rcMaster).to("refs/for/master");
+ r.assertOkStatus();
+
+ // rcMaster (c1 master) <- newchange1 (master-newtag)
+ RefUpdate btu = repo.updateRef("refs/tags/master-newtag");
+ btu.setExpectedOldObjectId(ObjectId.zeroId());
+ btu.setNewObjectId(r.getCommit());
+ assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ }
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+ }
+
+ // first ls-remote: rcBranch (c2) <- newcommit1 <- newcommit2 (branch)
+ // second ls-remote: rcBranch (c2) <- newcommit1 (branch-newtag) <- newcommit2 (branch)
+ @Test
+ public void uploadPackNewReachableTagVisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ // c2 <- newcommit1 (branch)
+ PushOneCommit.Result r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(rcBranch)
+ .to("refs/heads/branch");
+ r.assertOkStatus();
+ RevCommit tagRc = r.getCommit();
+
+ // c2 <- newcommit1 <- newcommit2 (branch)
+ r = pushFactory.create(admin.newIdent(), testRepo).setParent(tagRc).to("refs/heads/branch");
+ r.assertOkStatus();
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+
+ // c2 <- newcommit1 (branch-newtag) <- newcommit2 (branch)
+ RefUpdate btu = repo.updateRef("refs/tags/branch-newtag");
+ btu.setExpectedOldObjectId(ObjectId.zeroId());
+ btu.setNewObjectId(tagRc);
+ assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ }
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/tags/branch-newtag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+ }
+
+ // first ls-remote: rcBranch (c2) <- newcommit1 (branch)
+ // second ls-remote: rcBranch (c2) <- newcommit1 <- newcommit2 (branch)
+ // third ls-remote: rcBranch (c2) <- newcommit1 (branch-newtag) <- newcommit2 (branch)
+ @Test
+ public void uploadPackBranchFFNewTagOldBranchVisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ // rcBranch (c2) <- newcommit1 (branch)
+ PushOneCommit.Result r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(rcBranch)
+ .to("refs/heads/branch");
+ r.assertOkStatus();
+ RevCommit tagRc = r.getCommit();
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+
+ // rcBranch (c2) <- newcommit1 <- newcommit2 (branch)
+ r = pushFactory.create(admin.newIdent(), testRepo).setParent(tagRc).to("refs/heads/branch");
+ r.assertOkStatus();
+
+ // rcBranch (c2) <- newcommit1 (branch-newtag) <- newcommit2 (branch)
+ RefUpdate btu = repo.updateRef("refs/tags/branch-newtag");
+ btu.setExpectedOldObjectId(ObjectId.zeroId());
+ btu.setNewObjectId(tagRc);
+ assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ }
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/tags/branch-newtag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+ }
+
+ // first ls-remote: rcBranch (c2) <- newcommit1 (branch-oldtag) <- newcommit2 (branch)
+ // second ls-remote: rcBranch (c2 branch) <- newcommit1 (branch-oldtag)
+ @Test
+ public void uploadPackBranchRewindMakeTagUnreachableInVisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ // rcBranch (c2) <- newcommit1 (branch)
+ PushOneCommit.Result r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(rcBranch)
+ .to("refs/heads/branch");
+ r.assertOkStatus();
+ RevCommit tagRc = r.getCommit();
+
+ // rcBranch (c2) <- newcommit1 <- newcommit2 (branch)
+ r = pushFactory.create(admin.newIdent(), testRepo).setParent(tagRc).to("refs/heads/branch");
+ r.assertOkStatus();
+ RevCommit bRc = r.getCommit();
+
+ // rcBranch (c2) <- newcommit1 (branch-oldtag) <- newcommit2 (branch)
+ RefUpdate btu = repo.updateRef("refs/tags/branch-oldtag");
+ btu.setExpectedOldObjectId(ObjectId.zeroId());
+ btu.setNewObjectId(tagRc);
+ assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/tags/branch-oldtag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+
+ // rcBranch (c2 branch) <- newcommit1 (branch-oldtag) <- newcommit2
+ btu = repo.updateRef("refs/heads/branch");
+ btu.setExpectedOldObjectId(bRc);
+ btu.setNewObjectId(rcBranch);
+ btu.setForceUpdate(true);
+ assertThat(btu.update()).isEqualTo(RefUpdate.Result.FORCED);
+ }
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ // See comment in subsetOfBranchesVisibleNotIncludingHead.
+ "refs/tags/master-tag");
+ }
+
+ // first ls-remote: rcBranch (c2 branch) <- newcommit1 (new-tag)
+ // second ls-remote: rcBranch (c2 branch) <- newcommit1 (new-tag) <- newcommit2 (new-branch)
+ @Test
+ public void uploadPackCreateBranchTagReachableVisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/new-branch").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ // rcBranch (c2 branch) <- newcommit1 (branch-newtag)
+ PushOneCommit.Result r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(rcBranch)
+ .to("refs/tags/new-tag");
+ r.assertOkStatus();
+ RevCommit tagRc = r.getCommit();
+
+ assertUploadPackRefs();
+
+ // rcBranch (c2) <- newcommit1 (branch-newtag) <- newcommit2 (branch)
+ r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(tagRc)
+ .to("refs/heads/new-branch");
+ r.assertOkStatus();
+ }
+
+ assertUploadPackRefs(
+ "refs/heads/new-branch",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag",
+ "refs/tags/new-tag");
+ }
+
+ // first ls-remote: rcBranch (c2 branch) <- newcommit1 (updated-tag)
+ // second ls-remote: rcBranch (c2 branch updated-tag)
+ @Test
+ public void uploadPackTagUpdatedReachableVisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ // rcBranch (c2 branch) <- newcommit1 (updated-tag)
+ PushOneCommit.Result r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(rcBranch)
+ .to("refs/tags/updated-tag");
+ r.assertOkStatus();
+ RevCommit tagRc = r.getCommit();
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag");
+
+ // rcBranch (c2 branch updated-tag)
+ RefUpdate btu = repo.updateRef("refs/tags/updated-tag");
+ btu.setExpectedOldObjectId(tagRc);
+ btu.setNewObjectId(rcBranch);
+ btu.setForceUpdate(true);
+ assertThat(btu.update()).isEqualTo(RefUpdate.Result.FORCED);
+ }
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag",
+ "refs/tags/updated-tag");
+ }
+
+ // first ls-remote: rcBranch (c2 branch updated-tag)
+ // second ls-remote: rcBranch (c2 branch) <- newcommit1 (updated-tag)
+ @Test
+ public void uploadPackTagUpdatedUnreachableInvisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ // rcBranch (c2 branch updated-tag)
+ RefUpdate btu = repo.updateRef("refs/tags/updated-tag");
+ btu.setExpectedOldObjectId(ObjectId.zeroId());
+ btu.setNewObjectId(rcBranch);
+ assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag",
+ "refs/tags/updated-tag");
+
+ // rcBranch (c2 branch) <- newcommit1 (updated-tag)
+ PushOneCommit.Result r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(rcBranch)
+ .to("refs/tags/updated-tag");
+ r.assertOkStatus();
+ }
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag");
+ }
+
+ // first ls-remote: rcBranch (c2 branch branch-tag)
+ // second ls-remote: rcBranch (c2 branch)
+ @Test
+ public void uploadPackTagDeleted() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .add(allow(Permission.DELETE).ref("refs/tags/branch-tag").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref("refs/tags/branch-tag").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ // rcBranch (c2 branch branch-tag)
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag");
+
+ // rcBranch (c2 branch)
+ try (Repository repo = repoManager.openRepository(project)) {
+ RefUpdate btu = repo.updateRef("refs/tags/branch-tag");
+ btu.setExpectedOldObjectId(rcBranch);
+ btu.setNewObjectId(ObjectId.zeroId());
+ btu.setForceUpdate(true);
+ assertThat(btu.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ }
+
+ assertUploadPackRefs(
+ psRef2, metaRef2, psRef4, metaRef4, "refs/heads/branch", "refs/tags/master-tag");
+ }
+
+ // first ls-remote: rcBranch (c2 branch) <- newcommit1 (new-tag) <- newcommit2 (new-branch)
+ // second ls-remote: rcBranch (c2 branch) <- newcommit1 (new-tag)
+ @Test
+ public void uploadPackBranchDeleteTagUnreachableInvisible() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .add(allow(Permission.READ).ref("refs/heads/new-branch").group(REGISTERED_USERS))
+ .add(allow(Permission.DELETE).ref("refs/heads/new-branch").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS))
+ .update();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ // rcBranch (branch) <- newcommit1 (new-tag)
+ PushOneCommit.Result r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(rcBranch)
+ .to("refs/tags/new-tag");
+ r.assertOkStatus();
+ RevCommit tagRc = r.getCommit();
+
+ // rcBranch (c2 branch) <- newcommit1 (new-tag) <- newcommit2 (new-branch)
+ r =
+ pushFactory
+ .create(admin.newIdent(), testRepo)
+ .setParent(tagRc)
+ .to("refs/heads/new-branch");
+ r.assertOkStatus();
+ }
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
+ "refs/heads/new-branch",
+ "refs/tags/new-tag",
+ "refs/tags/master-tag");
+
+ // rcBranch (c2 branch) <- newcommit1 (new-tag)
+ gApi.projects().name(project.get()).branch("refs/heads/new-branch").delete();
+
+ assertUploadPackRefs(
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/tags/branch-tag",
"refs/tags/master-tag");
}
@Test
public void receivePackListsOpenChangesAsAdditionalHaves() throws Exception {
- ReceiveCommitsAdvertiseRefsHook.Result r = getReceivePackRefs();
+ TestRefAdvertiser.Result r = getReceivePackRefs();
assertThat(r.allRefs().keySet())
.containsExactly(
// meta refs are excluded
- "HEAD",
"refs/heads/branch",
"refs/heads/master",
"refs/meta/config",
"refs/tags/branch-tag",
- "refs/tags/master-tag");
+ "refs/tags/master-tag",
+ "refs/tags/tree-tag");
assertThat(r.additionalHaves()).containsExactly(obj(cd3, 1), obj(cd4, 1));
}
@Test
public void receivePackRespectsVisibilityOfOpenChanges() throws Exception {
- allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
- deny("refs/heads/branch", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .add(deny(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(cd3, 1));
@@ -442,7 +1069,7 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
TestRepository<Repository> tr = new TestRepository<>(repo)) {
String subject = "Subject for missing commit";
Change c = new Change(cd3.change());
- PatchSet.Id psId = new PatchSet.Id(cd3.getId(), 2);
+ PatchSet.Id psId = PatchSet.id(cd3.getId(), 2);
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
PersonIdent committer = serverIdent.get();
@@ -483,7 +1110,11 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesOmitUserBranchesOfOtherUsers() throws Exception {
- allow(allUsersName, RefNames.REFS_USERS + "*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(allUsersName)
+ .forUpdate()
+ .add(allow(Permission.READ).ref(RefNames.REFS_USERS + "*").group(REGISTERED_USERS))
+ .update();
TestRepository<?> userTestRepository = cloneProject(allUsers, user);
try (Git git = userTestRepository.git()) {
assertThat(getUserRefs(git))
@@ -493,7 +1124,10 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesIncludeAllUserBranchesWithAccessDatabase() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
TestRepository<?> userTestRepository = cloneProject(allUsers, user);
try (Git git = userTestRepository.git()) {
assertThat(getUserRefs(git))
@@ -515,7 +1149,11 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesOmitGroupBranchesOfNonOwnedGroups() throws Exception {
- allow(allUsersName, RefNames.REFS_GROUPS + "*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(allUsersName)
+ .forUpdate()
+ .add(allow(Permission.READ).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .update();
AccountGroup.UUID users = createGroup("Users", admins, user);
AccountGroup.UUID foos = createGroup("Foos", users);
AccountGroup.UUID bars = createSelfOwnedGroup("Bars", user);
@@ -528,7 +1166,10 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesIncludeAllGroupBranchesWithAccessDatabase() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
AccountGroup.UUID users = createGroup("Users", admins);
TestRepository<?> userTestRepository = cloneProject(allUsers, user);
try (Git git = userTestRepository.git()) {
@@ -542,8 +1183,15 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesIncludeAllGroupBranchesForAdmins() throws Exception {
- allow(allUsersName, RefNames.REFS_GROUPS + "*", Permission.READ, REGISTERED_USERS);
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ADMINISTRATE_SERVER);
+ projectOperations
+ .project(allUsersName)
+ .forUpdate()
+ .add(allow(Permission.READ).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ADMINISTRATE_SERVER).group(REGISTERED_USERS))
+ .update();
AccountGroup.UUID users = createGroup("Users", admins);
TestRepository<?> userTestRepository = cloneProject(allUsers, user);
try (Git git = userTestRepository.git()) {
@@ -557,7 +1205,11 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesOmitNoteDbNotesBranches() throws Exception {
- allow(allUsersName, RefNames.REFS + "*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(allUsersName)
+ .forUpdate()
+ .add(allow(Permission.READ).ref(RefNames.REFS + "*").group(REGISTERED_USERS))
+ .update();
TestRepository<?> userTestRepository = cloneProject(allUsers, user);
try (Git git = userTestRepository.git()) {
assertThat(getRefs(git)).containsNoneOf(RefNames.REFS_EXTERNAL_IDS, RefNames.REFS_GROUPNAMES);
@@ -566,11 +1218,15 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesOmitPrivateChangesOfOtherUsers() throws Exception {
- allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
- String change3RefName = cd3.currentPatchSet().getRefName();
+ String change3RefName = cd3.currentPatchSet().refName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
gApi.changes().id(cd3.getId().get()).setPrivate(true, null);
@@ -583,11 +1239,15 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
assume()
.that(baseConfig.getBoolean("auth", "skipFullRefEvaluationIfAllRefsAreVisible", true))
.isTrue();
- allow("refs/*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
- String change3RefName = cd3.currentPatchSet().getRefName();
+ String change3RefName = cd3.currentPatchSet().refName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
gApi.changes().id(cd3.getId().get()).setPrivate(true, null);
@@ -599,11 +1259,15 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@GerritConfig(name = "auth.skipFullRefEvaluationIfAllRefsAreVisible", value = "false")
public void advertisedReferencesOmitPrivateChangesOfOtherUsersWhenShortcutDisabled()
throws Exception {
- allow("refs/*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
- String change3RefName = cd3.currentPatchSet().getRefName();
+ String change3RefName = cd3.currentPatchSet().refName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
gApi.changes().id(cd3.getId().get()).setPrivate(true, null);
@@ -613,8 +1277,16 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesOmitDraftCommentRefsOfOtherUsers() throws Exception {
- allow(project, "refs/*", Permission.READ, REGISTERED_USERS);
- allow(allUsersName, "refs/*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(allUsersName)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
DraftInput draftInput = new DraftInput();
@@ -633,8 +1305,16 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesOmitStarredChangesRefsOfOtherUsers() throws Exception {
- allow(project, "refs/*", Permission.READ, REGISTERED_USERS);
- allow(allUsersName, "refs/*", Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(allUsersName)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
gApi.accounts().self().starChange(cd3.getId().toString());
@@ -649,7 +1329,10 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void hideMetadata() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
// create change
TestRepository<?> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.REFS_USERS_SELF + ":userRef");
@@ -662,7 +1345,6 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
List<String> expectedNonMetaRefs =
ImmutableList.of(
- RefNames.REFS_USERS_SELF,
RefNames.refsUsers(admin.id()),
RefNames.refsUsers(user.id()),
RefNames.REFS_EXTERNAL_IDS,
@@ -719,7 +1401,7 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
private List<String> getRefs(Git git) throws Exception {
- return getRefs(git, Predicates.alwaysTrue());
+ return getRefs(git, x -> true);
}
private List<String> getUserRefs(Git git) throws Exception {
@@ -760,11 +1442,16 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
}
- private ReceiveCommitsAdvertiseRefsHook.Result getReceivePackRefs() throws Exception {
- ReceiveCommitsAdvertiseRefsHook hook =
- new ReceiveCommitsAdvertiseRefsHook(queryProvider, project);
+ private TestRefAdvertiser.Result getReceivePackRefs() throws Exception {
try (Repository repo = repoManager.openRepository(project)) {
- return hook.advertiseRefs(getAllRefs(repo));
+ AdvertiseRefsHook adv =
+ ReceiveCommitsAdvertiseRefsHookChain.createForTest(
+ queryProvider, project, identifiedUserFactory.create(admin.id()));
+ ReceivePack rp = new ReceivePack(repo);
+ rp.setAdvertiseRefsHook(adv);
+ TestRefAdvertiser advertiser = new TestRefAdvertiser(repo);
+ rp.sendAdvertisedRefs(advertiser);
+ return advertiser.result();
}
}
@@ -773,10 +1460,10 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
private static ObjectId obj(ChangeData cd, int psNum) throws Exception {
- PatchSet.Id psId = new PatchSet.Id(cd.getId(), psNum);
+ PatchSet.Id psId = PatchSet.id(cd.getId(), psNum);
PatchSet ps = cd.patchSet(psId);
assertWithMessage("%s not found in %s", psId, cd.patchSets()).that(ps).isNotNull();
- return ObjectId.fromString(ps.getRevision().get());
+ return ps.commitId();
}
private AccountGroup.UUID createSelfOwnedGroup(String name, TestAccount... members)
@@ -792,7 +1479,7 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
groupInput.ownerId = ownerGroup != null ? ownerGroup.get() : null;
groupInput.members =
Arrays.stream(members).map(m -> String.valueOf(m.id().get())).collect(toList());
- return new AccountGroup.UUID(gApi.groups().create(groupInput).get().id);
+ return AccountGroup.uuid(gApi.groups().create(groupInput).get().id);
}
private static Map<String, Ref> getAllRefs(Repository repo) throws IOException {
diff --git a/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java b/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
index 9b823b7ad2..876e34264b 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
@@ -15,8 +15,10 @@
package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.acceptance.GitUtil.deleteRef;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.transport.ReceiveCommand.Type.CREATE;
import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE;
@@ -24,11 +26,12 @@ import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.BranchInput;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.events.RefReceivedEvent;
import com.google.gerrit.server.git.validators.RefOperationValidationListener;
@@ -47,17 +50,16 @@ import org.junit.Test;
public class RefOperationValidationIT extends AbstractDaemonTest {
private static final String TEST_REF = "refs/heads/protected";
- @Inject DynamicSet<RefOperationValidationListener> validators;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
- private class TestRefValidator implements RefOperationValidationListener, AutoCloseable {
+ private static class TestRefValidator implements RefOperationValidationListener {
private final ReceiveCommand.Type rejectType;
private final String rejectRef;
- private final RegistrationHandle handle;
public TestRefValidator(ReceiveCommand.Type rejectType) {
this.rejectType = rejectType;
this.rejectRef = TEST_REF;
- this.handle = validators.add("test-" + rejectType.name(), this);
}
@Override
@@ -69,27 +71,35 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
}
return Collections.emptyList();
}
+ }
- @Override
- public void close() throws Exception {
- handle.remove();
- }
+ private Registration testValidator(ReceiveCommand.Type rejectType) {
+ return extensionRegistry.newRegistration().add(new TestRefValidator(rejectType));
}
@Test
public void rejectRefCreation() throws Exception {
- try (TestRefValidator validator = new TestRefValidator(CREATE)) {
- gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
- assert_().fail("expected exception");
- } catch (RestApiException expected) {
+ try (Registration registration = testValidator(CREATE)) {
+ RestApiException expected =
+ assertThrows(
+ RestApiException.class,
+ () -> gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput()));
assertThat(expected).hasMessageThat().contains(CREATE.name());
}
}
+ private void grant(String permission) {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(permission).ref("refs/*").group(REGISTERED_USERS).force(true))
+ .update();
+ }
+
@Test
public void rejectRefCreationByPush() throws Exception {
- try (TestRefValidator validator = new TestRefValidator(CREATE)) {
- grant(project, "refs/*", Permission.PUSH, true);
+ try (Registration registration = testValidator(CREATE)) {
+ grant(Permission.PUSH);
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
@@ -102,10 +112,11 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
@Test
public void rejectRefDeletion() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
- try (TestRefValidator validator = new TestRefValidator(DELETE)) {
- gApi.projects().name(project.get()).branch(TEST_REF).delete();
- assert_().fail("expected exception");
- } catch (RestApiException expected) {
+ try (Registration registration = testValidator(DELETE)) {
+ RestApiException expected =
+ assertThrows(
+ RestApiException.class,
+ () -> gApi.projects().name(project.get()).branch(TEST_REF).delete());
assertThat(expected).hasMessageThat().contains(DELETE.name());
}
}
@@ -113,8 +124,8 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
@Test
public void rejectRefDeletionByPush() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
- grant(project, "refs/*", Permission.DELETE, true);
- try (TestRefValidator validator = new TestRefValidator(DELETE)) {
+ grant(Permission.DELETE);
+ try (Registration registration = testValidator(DELETE)) {
PushResult result = deleteRef(testRepo, TEST_REF);
RemoteRefUpdate refUpdate = result.getRemoteUpdate(TEST_REF);
assertThat(refUpdate.getMessage()).contains(DELETE.name());
@@ -124,8 +135,8 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
@Test
public void rejectRefUpdateFastForward() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
- try (TestRefValidator validator = new TestRefValidator(UPDATE)) {
- grant(project, "refs/*", Permission.PUSH, true);
+ try (Registration registration = testValidator(UPDATE)) {
+ grant(Permission.PUSH);
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to(TEST_REF);
@@ -136,9 +147,9 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
@Test
public void rejectRefUpdateNonFastForward() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
- try (TestRefValidator validator = new TestRefValidator(UPDATE_NONFASTFORWARD)) {
+ try (Registration registration = testValidator(UPDATE_NONFASTFORWARD)) {
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
- grant(project, "refs/*", Permission.PUSH, true);
+ grant(Permission.PUSH);
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to(TEST_REF);
@@ -161,8 +172,8 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
public void rejectRefUpdateNonFastForwardToExistingCommit() throws Exception {
gApi.projects().name(project.get()).branch(TEST_REF).create(new BranchInput());
- try (TestRefValidator validator = new TestRefValidator(UPDATE_NONFASTFORWARD)) {
- grant(project, "refs/*", Permission.PUSH, true);
+ try (Registration registration = testValidator(UPDATE_NONFASTFORWARD)) {
+ grant(Permission.PUSH);
PushOneCommit push1 =
pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
index e090fae228..09da6284c7 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
@@ -16,15 +16,16 @@ package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
-import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.TestTimeUtil;
+import com.google.inject.Inject;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
@@ -45,6 +46,8 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
return submitWholeTopicEnabledConfig();
}
+ @Inject private ProjectOperations projectOperations;
+
@Test
@GerritConfig(name = "submodule.enableSuperProjectSubscriptions", value = "false")
public void testSubscriptionWithoutGlobalServerSetting() throws Exception {
@@ -108,7 +111,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
public void subscriptionWildcardACLForMissingProject() throws Exception {
allowMatchingSubmoduleSubscription(
- subKey, "refs/heads/*", new Project.NameKey("not-existing-super-project"), "refs/heads/*");
+ subKey, "refs/heads/*", Project.nameKey("not-existing-super-project"), "refs/heads/*");
pushChangeTo(subRepo, "master");
}
@@ -379,10 +382,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
@Test
public void subscriptionFailOnWrongProjectACL() throws Exception {
allowMatchingSubmoduleSubscription(
- subKey,
- "refs/heads/master",
- new Project.NameKey("wrong-super-project"),
- "refs/heads/master");
+ subKey, "refs/heads/master", Project.nameKey("wrong-super-project"), "refs/heads/master");
pushChangeTo(subRepo, "master");
createSubmoduleSubscription(superRepo, "master", subKey, "master");
@@ -479,127 +479,107 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
}
@Test
+ @UseClockStep
public void superRepoCommitHasSameAuthorAsSubmoduleCommit() throws Exception {
// Make sure that the commit is created at an earlier timestamp than the submit timestamp.
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- try {
- allowMatchingSubmoduleSubscription(
- subKey, "refs/heads/master", superKey, "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", subKey, "master");
-
- PushOneCommit.Result pushResult =
- createChange(subRepo, "refs/heads/master", "Change", "a.txt", "some content", null);
- approve(pushResult.getChangeId());
- gApi.changes().id(pushResult.getChangeId()).current().submit();
-
- // Expect that the author name/email is preserved for the superRepo commit, but a new author
- // timestamp is used.
- PersonIdent authorIdent = getAuthor(superRepo, "master");
- assertThat(authorIdent.getName()).isEqualTo(admin.fullName());
- assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email());
- assertThat(authorIdent.getWhen())
- .isGreaterThan(pushResult.getCommit().getAuthorIdent().getWhen());
- } finally {
- TestTimeUtil.useSystemTime();
- }
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
+
+ PushOneCommit.Result pushResult =
+ createChange(subRepo, "refs/heads/master", "Change", "a.txt", "some content", null);
+ approve(pushResult.getChangeId());
+ gApi.changes().id(pushResult.getChangeId()).current().submit();
+
+ // Expect that the author name/email is preserved for the superRepo commit, but a new author
+ // timestamp is used.
+ PersonIdent authorIdent = getAuthor(superRepo, "master");
+ assertThat(authorIdent.getName()).isEqualTo(admin.fullName());
+ assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email());
+ assertThat(authorIdent.getWhen())
+ .isGreaterThan(pushResult.getCommit().getAuthorIdent().getWhen());
}
@Test
+ @UseClockStep
public void superRepoCommitHasSameAuthorAsSubmoduleCommits() throws Exception {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
- // Make sure that the commits are created at different timestamps and that the submit timestamp
- // is afterwards.
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- try {
-
- Project.NameKey proj2 = createProjectForPush(getSubmitType());
-
- TestRepository<?> subRepo2 = cloneProject(proj2);
- allowMatchingSubmoduleSubscription(
- subKey, "refs/heads/master", superKey, "refs/heads/master");
- allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master");
-
- Config config = new Config();
- prepareSubmoduleConfigEntry(config, subKey, subKey, "master");
- prepareSubmoduleConfigEntry(config, proj2, proj2, "master");
- pushSubmoduleConfig(superRepo, "master", config);
-
- String topic = "foo";
-
- PushOneCommit.Result pushResult1 =
- createChange(subRepo, "refs/heads/master", "Change 1", "a.txt", "some content", topic);
- approve(pushResult1.getChangeId());
-
- PushOneCommit.Result pushResult2 =
- createChange(subRepo2, "refs/heads/master", "Change 2", "b.txt", "other content", topic);
- approve(pushResult2.getChangeId());
-
- // Submit the topic, 2 changes with the same author.
- gApi.changes().id(pushResult1.getChangeId()).current().submit();
-
- // Expect that the author name/email is preserved for the superRepo commit, but a new author
- // timestamp is used.
- PersonIdent authorIdent = getAuthor(superRepo, "master");
- assertThat(authorIdent.getName()).isEqualTo(admin.fullName());
- assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email());
- assertThat(authorIdent.getWhen())
- .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen());
- assertThat(authorIdent.getWhen())
- .isGreaterThan(pushResult2.getCommit().getAuthorIdent().getWhen());
- } finally {
- TestTimeUtil.useSystemTime();
- }
+ Project.NameKey proj2 = createProjectForPush(getSubmitType());
+
+ TestRepository<?> subRepo2 = cloneProject(proj2);
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master");
+
+ Config config = new Config();
+ prepareSubmoduleConfigEntry(config, subKey, subKey, "master");
+ prepareSubmoduleConfigEntry(config, proj2, proj2, "master");
+ pushSubmoduleConfig(superRepo, "master", config);
+
+ String topic = "foo";
+
+ PushOneCommit.Result pushResult1 =
+ createChange(subRepo, "refs/heads/master", "Change 1", "a.txt", "some content", topic);
+ approve(pushResult1.getChangeId());
+
+ PushOneCommit.Result pushResult2 =
+ createChange(subRepo2, "refs/heads/master", "Change 2", "b.txt", "other content", topic);
+ approve(pushResult2.getChangeId());
+
+ // Submit the topic, 2 changes with the same author.
+ gApi.changes().id(pushResult1.getChangeId()).current().submit();
+
+ // Expect that the author name/email is preserved for the superRepo commit, but a new author
+ // timestamp is used.
+ PersonIdent authorIdent = getAuthor(superRepo, "master");
+ assertThat(authorIdent.getName()).isEqualTo(admin.fullName());
+ assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email());
+ assertThat(authorIdent.getWhen())
+ .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen());
+ assertThat(authorIdent.getWhen())
+ .isGreaterThan(pushResult2.getCommit().getAuthorIdent().getWhen());
}
@Test
+ @UseClockStep
public void superRepoCommitHasGerritAsAuthorIfAuthorsOfSubmoduleCommitsDiffer() throws Exception {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
- // Make sure that the commits are created at different timestamps and that the submit timestamp
- // is afterwards.
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- try {
- Project.NameKey proj2 = createProjectForPush(getSubmitType());
- TestRepository<InMemoryRepository> repo2 = cloneProject(proj2, user);
-
- allowMatchingSubmoduleSubscription(
- subKey, "refs/heads/master", superKey, "refs/heads/master");
- allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master");
-
- Config config = new Config();
- prepareSubmoduleConfigEntry(config, subKey, subKey, "master");
- prepareSubmoduleConfigEntry(config, proj2, proj2, "master");
- pushSubmoduleConfig(superRepo, "master", config);
-
- String topic = "foo";
-
- // Create change as admin.
- PushOneCommit.Result pushResult1 =
- createChange(subRepo, "refs/heads/master", "Change 1", "a.txt", "some content", topic);
- approve(pushResult1.getChangeId());
-
- // Create change as user.
- PushOneCommit push =
- pushFactory.create(user.newIdent(), repo2, "Change 2", "b.txt", "other content");
- PushOneCommit.Result pushResult2 = push.to("refs/for/master/" + name(topic));
- approve(pushResult2.getChangeId());
-
- // Submit the topic, 2 changes with the different author.
- gApi.changes().id(pushResult1.getChangeId()).current().submit();
-
- // Expect that the Gerrit server identity is chosen as author for the superRepo commit and a
- // new author timestamp is used.
- PersonIdent authorIdent = getAuthor(superRepo, "master");
- assertThat(authorIdent.getName()).isEqualTo(serverIdent.get().getName());
- assertThat(authorIdent.getEmailAddress()).isEqualTo(serverIdent.get().getEmailAddress());
- assertThat(authorIdent.getWhen())
- .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen());
- assertThat(authorIdent.getWhen())
- .isGreaterThan(pushResult2.getCommit().getAuthorIdent().getWhen());
- } finally {
- TestTimeUtil.useSystemTime();
- }
+ Project.NameKey proj2 = createProjectForPush(getSubmitType());
+ TestRepository<InMemoryRepository> repo2 = cloneProject(proj2, user);
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master");
+
+ Config config = new Config();
+ prepareSubmoduleConfigEntry(config, subKey, subKey, "master");
+ prepareSubmoduleConfigEntry(config, proj2, proj2, "master");
+ pushSubmoduleConfig(superRepo, "master", config);
+
+ String topic = "foo";
+
+ // Create change as admin.
+ PushOneCommit.Result pushResult1 =
+ createChange(subRepo, "refs/heads/master", "Change 1", "a.txt", "some content", topic);
+ approve(pushResult1.getChangeId());
+
+ // Create change as user.
+ PushOneCommit push =
+ pushFactory.create(user.newIdent(), repo2, "Change 2", "b.txt", "other content");
+ PushOneCommit.Result pushResult2 = push.to("refs/for/master/" + name(topic));
+ approve(pushResult2.getChangeId());
+
+ // Submit the topic, 2 changes with the different author.
+ gApi.changes().id(pushResult1.getChangeId()).current().submit();
+
+ // Expect that the Gerrit server identity is chosen as author for the superRepo commit and a
+ // new author timestamp is used.
+ PersonIdent authorIdent = getAuthor(superRepo, "master");
+ assertThat(authorIdent.getName()).isEqualTo(serverIdent.get().getName());
+ assertThat(authorIdent.getEmailAddress()).isEqualTo(serverIdent.get().getEmailAddress());
+ assertThat(authorIdent.getWhen())
+ .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen());
+ assertThat(authorIdent.getWhen())
+ .isGreaterThan(pushResult2.getCommit().getAuthorIdent().getWhen());
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
index dc84d13fc6..283c95fd6c 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
@@ -17,17 +17,23 @@ package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.GitUtil.getChangeId;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.testsuite.ThrowingConsumer;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.server.change.TestSubmitInput;
import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Inject;
import java.util.ArrayDeque;
import java.util.Map;
import org.apache.commons.lang.RandomStringUtils;
@@ -67,6 +73,8 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
return submitByRebaseIfNecessaryConfig();
}
+ @Inject private ProjectOperations projectOperations;
+
@Test
public void subscriptionUpdateOfManyChanges() throws Exception {
allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
@@ -136,7 +144,7 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
gApi.changes().id(id2).current().review(ReviewInput.approve());
gApi.changes().id(id3).current().review(ReviewInput.approve());
- Map<Branch.NameKey, ObjectId> preview = fetchFromSubmitPreview(id1);
+ Map<BranchNameKey, ObjectId> preview = fetchFromSubmitPreview(id1);
gApi.changes().id(id1).current().submit();
ObjectId subRepoId =
subRepo
@@ -152,8 +160,8 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
// As the submodules have changed commits, the superproject tree will be
// different, so we cannot directly compare the trees here, so make
// assumptions only about the changed branches:
- assertThat(preview).containsKey(new Branch.NameKey(superKey, "refs/heads/master"));
- assertThat(preview).containsKey(new Branch.NameKey(subKey, "refs/heads/master"));
+ assertThat(preview).containsKey(BranchNameKey.create(superKey, "refs/heads/master"));
+ assertThat(preview).containsKey(BranchNameKey.create(subKey, "refs/heads/master"));
if ((getSubmitType() == SubmitType.CHERRY_PICK)
|| (getSubmitType() == SubmitType.REBASE_ALWAYS)) {
@@ -282,8 +290,12 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
.name(prefix + "sub" + i)
.submitType(getSubmitType())
.create();
- grant(subKey[i], "refs/heads/*", Permission.PUSH);
- grant(subKey[i], "refs/for/refs/heads/*", Permission.SUBMIT);
+ projectOperations
+ .project(subKey[i])
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/*").group(adminGroupUuid()))
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/*").group(adminGroupUuid()))
+ .update();
sub[i] = cloneProject(subKey[i]);
}
@@ -393,7 +405,7 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
gApi.changes().id(subChangeId).current().submit();
expectToHaveSubmoduleState(superRepo, "master", subKey, subRepo, "master");
- RevCommit superHead = getRemoteHead(superKey, "master");
+ RevCommit superHead = projectOperations.project(superKey).getHead("master");
assertThat(superHead.getShortMessage()).contains("some message");
assertThat(superHead.getId()).isNotEqualTo(superId);
}
@@ -427,7 +439,7 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
gApi.changes().id(subChangeId).current().submit();
- RevCommit superHead = getRemoteHead(superKey, "master");
+ RevCommit superHead = projectOperations.project(superKey).getHead("master");
assertThat(superHead.getShortMessage()).isEqualTo("some message");
assertThat(superHead.getId()).isEqualTo(superId);
}
@@ -614,7 +626,7 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
expectToHaveSubmoduleState(topRepo, "master", botKey, bottomRepo, "master");
}
- private String prepareBranchCircularSubscription() throws Exception {
+ private void testBranchCircularSubscription(ThrowingConsumer<String> apiCall) throws Exception {
Project.NameKey topKey = createProjectForPush(getSubmitType());
Project.NameKey midKey = createProjectForPush(getSubmitType());
Project.NameKey botKey = createProjectForPush(getSubmitType());
@@ -634,23 +646,24 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
String changeId = getChangeId(bottomRepo, bottomMasterHead).get();
approve(changeId);
- exception.expectMessage("Branch level circular subscriptions detected");
- exception.expectMessage(topKey.get() + ",refs/heads/master");
- exception.expectMessage(midKey.get() + ",refs/heads/master");
- exception.expectMessage(botKey.get() + ",refs/heads/master");
- return changeId;
+
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> apiCall.accept(changeId));
+ assertThat(thrown).hasMessageThat().contains("Branch level circular subscriptions detected");
+ assertThat(thrown).hasMessageThat().contains(topKey.get() + ",refs/heads/master");
+ assertThat(thrown).hasMessageThat().contains(midKey.get() + ",refs/heads/master");
+ assertThat(thrown).hasMessageThat().contains(botKey.get() + ",refs/heads/master");
}
@Test
public void branchCircularSubscription() throws Exception {
- String changeId = prepareBranchCircularSubscription();
- gApi.changes().id(changeId).current().submit();
+ testBranchCircularSubscription(changeId -> gApi.changes().id(changeId).current().submit());
}
@Test
public void branchCircularSubscriptionPreview() throws Exception {
- String changeId = prepareBranchCircularSubscription();
- gApi.changes().id(changeId).current().submitPreview();
+ testBranchCircularSubscription(
+ changeId -> gApi.changes().id(changeId).current().submitPreview());
}
@Test
@@ -672,10 +685,13 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
approve(getChangeId(subRepo, subMasterHead).get());
approve(getChangeId(superRepo, superDevHead).get());
- exception.expectMessage("Project level circular subscriptions detected");
- exception.expectMessage(subKey.get());
- exception.expectMessage(superKey.get());
- gApi.changes().id(getChangeId(subRepo, subMasterHead).get()).current().submit();
+ Throwable thrown =
+ assertThrows(
+ Throwable.class,
+ () -> gApi.changes().id(getChangeId(subRepo, subMasterHead).get()).current().submit());
+ assertThat(thrown).hasMessageThat().contains("Project level circular subscriptions detected");
+ assertThat(thrown).hasMessageThat().contains(subKey.get());
+ assertThat(thrown).hasMessageThat().contains(superKey.get());
}
@Test
@@ -739,13 +755,13 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
approve(getChangeId(repoB, bDevHead).get());
gApi.changes().id(getChangeId(repoA, aDevHead).get()).current().submit();
- assertThat(getRemoteHead(keyA, "refs/heads/master").getShortMessage())
+ assertThat(projectOperations.project(keyA).getHead("refs/heads/master").getShortMessage())
.contains("some message in a master.txt");
- assertThat(getRemoteHead(keyA, "refs/heads/dev").getShortMessage())
+ assertThat(projectOperations.project(keyA).getHead("refs/heads/dev").getShortMessage())
.contains("some message in a dev.txt");
- assertThat(getRemoteHead(keyB, "refs/heads/master").getShortMessage())
+ assertThat(projectOperations.project(keyB).getHead("refs/heads/master").getShortMessage())
.contains("some message in b master.txt");
- assertThat(getRemoteHead(keyB, "refs/heads/dev").getShortMessage())
+ assertThat(projectOperations.project(keyB).getHead("refs/heads/dev").getShortMessage())
.contains("some message in b dev.txt");
}
@@ -838,13 +854,13 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
sub1.git().fetch().call();
RevWalk rw1 = sub1.getRevWalk();
- RevCommit master1 = rw1.parseCommit(getRemoteHead(subKey1, "master"));
+ RevCommit master1 = rw1.parseCommit(projectOperations.project(subKey1).getHead("master"));
RevCommit change1Ps = parseCurrentRevision(rw1, changeId1);
assertThat(rw1.isMergedInto(change1Ps, master1)).isTrue();
sub2.git().fetch().call();
RevWalk rw2 = sub2.getRevWalk();
- RevCommit master2 = rw2.parseCommit(getRemoteHead(subKey2, "master"));
+ RevCommit master2 = rw2.parseCommit(projectOperations.project(subKey2).getHead("master"));
RevCommit change2Ps = parseCurrentRevision(rw2, changeId2);
assertThat(rw2.isMergedInto(change2Ps, master2)).isTrue();
@@ -898,6 +914,6 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
}
private Project.NameKey nameKey(String s) {
- return new Project.NameKey(name(s));
+ return Project.nameKey(name(s));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java b/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
index c07d512946..cad0b83942 100644
--- a/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
+++ b/javatests/com/google/gerrit/acceptance/pgm/AbstractReindexTests.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.pgm;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.truth.StreamSubject.streams;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
@@ -26,11 +27,11 @@ import com.google.common.io.RecursiveDeleteOption;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.StandaloneSiteTest;
import com.google.gerrit.acceptance.pgm.IndexUpgradeController.UpgradeAttempt;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.launcher.GerritLauncher;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.index.GerritIndexStatus;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
@@ -73,13 +74,13 @@ public abstract class AbstractReindexTests extends StandaloneSiteTest {
.containsExactly(changeId);
// Query account index
assertThat(gApi.accounts().query("admin").get().stream().map(a -> a._accountId))
- .containsExactly(adminId.get());
+ .containsExactly(admin.id().get());
// Query group index
assertThat(
gApi.groups().query("Group").withOption(MEMBERS).get().stream()
.flatMap(g -> g.members.stream())
.map(a -> a._accountId))
- .containsExactly(adminId.get());
+ .containsExactly(admin.id().get());
// Query project index
assertThat(gApi.projects().query(project.get()).get().stream().map(p -> p.name))
.containsExactly(project.get());
@@ -195,7 +196,7 @@ public abstract class AbstractReindexTests extends StandaloneSiteTest {
// Updating and searching old schema version works.
Provider<InternalChangeQuery> queryProvider =
ctx.getInjector().getProvider(InternalChangeQuery.class);
- assertThat(queryProvider.get().byKey(new Change.Key(changeId))).hasSize(1);
+ assertThat(queryProvider.get().byKey(Change.key(changeId))).hasSize(1);
assertThat(queryProvider.get().byTopicOpen("topic1")).isEmpty();
GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
@@ -223,7 +224,7 @@ public abstract class AbstractReindexTests extends StandaloneSiteTest {
}
private void setUpChange() throws Exception {
- project = new Project.NameKey("reindex-project-test");
+ project = Project.nameKey("reindex-project-test");
try (ServerContext ctx = startServer()) {
configureIndex(ctx.getInjector());
GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
@@ -240,7 +241,7 @@ public abstract class AbstractReindexTests extends StandaloneSiteTest {
}
private void enableSlaveMode() throws Exception {
- updateConfig(config -> config.setBoolean("container", null, "slave", true));
+ updateConfig(config -> config.setBoolean("container", null, "replica", true));
}
private void updateConfig(Consumer<Config> configConsumer) throws Exception {
@@ -255,30 +256,31 @@ public abstract class AbstractReindexTests extends StandaloneSiteTest {
}
private void assertSearchVersion(ServerContext ctx, int expected) {
- assertThat(
+ assertWithMessage("search version")
+ .that(
ctx.getInjector()
.getInstance(ChangeIndexCollection.class)
.getSearchIndex()
.getSchema()
.getVersion())
- .named("search version")
.isEqualTo(expected);
}
private void assertWriteVersions(ServerContext ctx, Integer... expected) {
- assertThat(
+ assertWithMessage("write versions")
+ .about(streams())
+ .that(
ctx.getInjector().getInstance(ChangeIndexCollection.class).getWriteIndexes().stream()
.map(i -> i.getSchema().getVersion()))
- .named("write versions")
.containsExactlyElementsIn(ImmutableSet.copyOf(expected));
}
private void assertReady(int expectedReady) throws Exception {
Set<Integer> allVersions = ChangeSchemaDefinitions.INSTANCE.getSchemas().keySet();
GerritIndexStatus status = new GerritIndexStatus(sitePaths);
- assertThat(
+ assertWithMessage("ready state for index versions")
+ .that(
allVersions.stream().collect(toImmutableMap(v -> v, v -> status.getReady(CHANGES, v))))
- .named("ready state for index versions")
.isEqualTo(allVersions.stream().collect(toImmutableMap(v -> v, v -> v == expectedReady)));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/pgm/InitIT.java b/javatests/com/google/gerrit/acceptance/pgm/InitIT.java
index 7cc47c3fa8..4caee6482c 100644
--- a/javatests/com/google/gerrit/acceptance/pgm/InitIT.java
+++ b/javatests/com/google/gerrit/acceptance/pgm/InitIT.java
@@ -14,16 +14,16 @@
package com.google.gerrit.acceptance.pgm;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.StandaloneSiteTest;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectIndexCollection;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import java.io.IOException;
@@ -52,9 +52,9 @@ public class InitIT extends StandaloneSiteTest {
QueryOptions opts =
QueryOptions.create(IndexConfig.createDefault(), 0, 1, ImmutableSet.of("name"));
Optional<ProjectData> allProjectsData = projectIndex.getSearchIndex().get(allProjects, opts);
- assertThat(allProjectsData.isPresent()).isTrue();
+ assertThat(allProjectsData).isPresent();
Optional<ProjectData> allUsersData = projectIndex.getSearchIndex().get(allUsers, opts);
- assertThat(allUsersData.isPresent()).isTrue();
+ assertThat(allUsersData).isPresent();
}
}
@@ -65,7 +65,7 @@ public class InitIT extends StandaloneSiteTest {
// Simulate a projects indexes files modified in the past by 3 seconds
Optional<Instant> projectsLastModified =
getProjectsIndexLastModified(sitePaths.index_dir).map(t -> t.minusSeconds(3));
- assertThat((projectsLastModified).isPresent()).isTrue();
+ assertThat(projectsLastModified).isPresent();
setProjectsIndexLastModifiedInThePast(sitePaths.index_dir, projectsLastModified.get());
initSite();
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index b30dc41290..52de5adf43 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -16,215 +16,320 @@ package com.google.gerrit.acceptance.rest;
import static com.google.common.truth.Truth.assertThat;
import static org.apache.http.HttpStatus.SC_CREATED;
+import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
+import static org.apache.http.HttpStatus.SC_OK;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.truth.Expect;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
+import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.events.ChangeIndexedListener;
import com.google.gerrit.httpd.restapi.ParameterParser;
import com.google.gerrit.httpd.restapi.RestApiServlet;
+import com.google.gerrit.server.ExceptionHook;
import com.google.gerrit.server.events.CommitReceivedEvent;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.PerformanceLogger;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.project.CreateProjectArgs;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.rules.SubmitRule;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.SortedMap;
import java.util.SortedSet;
import org.apache.http.message.BasicHeader;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+/**
+ * This test tests the tracing of requests.
+ *
+ * <p>To verify that tracing is working we do:
+ *
+ * <ul>
+ * <li>Register a plugin extension that we know is invoked when the request is done. Within the
+ * implementation of this plugin extension we access the status of the thread local state in
+ * the {@link LoggingContext} and store it locally in the plugin extension class.
+ * <li>Do a request (e.g. REST) that triggers the plugin extension.
+ * <li>When the plugin extension is invoked it records the current logging context.
+ * <li>After the request is done the test verifies that logging context that was recorded by the
+ * plugin extension has the expected state.
+ * </ul>
+ */
public class TraceIT extends AbstractDaemonTest {
@Rule public final Expect expect = Expect.create();
- @Inject private DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
- @Inject private DynamicSet<CommitValidationListener> commitValidationListeners;
+ @Inject private ExtensionRegistry extensionRegistry;
@Inject private WorkQueue workQueue;
- private TraceValidatingProjectCreationValidationListener projectCreationListener;
- private RegistrationHandle projectCreationListenerRegistrationHandle;
- private TraceValidatingCommitValidationListener commitValidationListener;
- private RegistrationHandle commitValidationRegistrationHandle;
-
- @Before
- public void setup() {
- projectCreationListener = new TraceValidatingProjectCreationValidationListener();
- projectCreationListenerRegistrationHandle =
- projectCreationValidationListeners.add("gerrit", projectCreationListener);
- commitValidationListener = new TraceValidatingCommitValidationListener();
- commitValidationRegistrationHandle =
- commitValidationListeners.add("gerrit", commitValidationListener);
- }
+ @Test
+ public void restCallWithoutTrace() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new1");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
- @After
- public void cleanup() {
- projectCreationListenerRegistrationHandle.remove();
- commitValidationRegistrationHandle.remove();
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new1");
+ }
}
@Test
- public void restCallWithoutTrace() throws Exception {
- RestResponse response = adminRestSession.put("/projects/new1");
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
- assertThat(projectCreationListener.traceId).isNull();
- assertThat(projectCreationListener.isLoggingForced).isFalse();
+ public void restCallForChangeSetsProjectTag() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ TraceChangeIndexedListener changeIndexedListener = new TraceChangeIndexedListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedListener)) {
+ RestResponse response =
+ adminRestSession.post(
+ "/changes/" + changeId + "/revisions/current/review", ReviewInput.approve());
+ assertThat(response.getStatusCode()).isEqualTo(SC_OK);
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(changeIndexedListener.tags.get("project")).containsExactly(project.get());
+ }
}
@Test
public void restCallWithTraceRequestParam() throws Exception {
- RestResponse response =
- adminRestSession.put("/projects/new2?" + ParameterParser.TRACE_PARAMETER);
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
- assertThat(projectCreationListener.traceId).isNotNull();
- assertThat(projectCreationListener.isLoggingForced).isTrue();
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response =
+ adminRestSession.put("/projects/new2?" + ParameterParser.TRACE_PARAMETER);
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
+ assertThat(projectCreationListener.traceId).isNotNull();
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new2");
+ }
}
@Test
public void restCallWithTraceRequestParamAndProvidedTraceId() throws Exception {
- RestResponse response =
- adminRestSession.put("/projects/new3?" + ParameterParser.TRACE_PARAMETER + "=issue/123");
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
- assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
- assertThat(projectCreationListener.isLoggingForced).isTrue();
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response =
+ adminRestSession.put("/projects/new3?" + ParameterParser.TRACE_PARAMETER + "=issue/123");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new3");
+ }
}
@Test
public void restCallWithTraceHeader() throws Exception {
- RestResponse response =
- adminRestSession.putWithHeader(
- "/projects/new4", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, null));
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
- assertThat(projectCreationListener.traceId).isNotNull();
- assertThat(projectCreationListener.isLoggingForced).isTrue();
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response =
+ adminRestSession.putWithHeader(
+ "/projects/new4", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, null));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
+ assertThat(projectCreationListener.traceId).isNotNull();
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new4");
+ }
}
@Test
public void restCallWithTraceHeaderAndProvidedTraceId() throws Exception {
- RestResponse response =
- adminRestSession.putWithHeader(
- "/projects/new5", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
- assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
- assertThat(projectCreationListener.isLoggingForced).isTrue();
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response =
+ adminRestSession.putWithHeader(
+ "/projects/new5", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new5");
+ }
}
@Test
public void restCallWithTraceRequestParamAndTraceHeader() throws Exception {
- // trace ID only specified by trace header
- RestResponse response =
- adminRestSession.putWithHeader(
- "/projects/new6?trace", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
- assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
- assertThat(projectCreationListener.isLoggingForced).isTrue();
-
- // trace ID only specified by trace request parameter
- response =
- adminRestSession.putWithHeader(
- "/projects/new7?trace=issue/123", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, null));
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
- assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
- assertThat(projectCreationListener.isLoggingForced).isTrue();
-
- // same trace ID specified by trace header and trace request parameter
- response =
- adminRestSession.putWithHeader(
- "/projects/new8?trace=issue/123",
- new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
- assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
- assertThat(projectCreationListener.isLoggingForced).isTrue();
-
- // different trace IDs specified by trace header and trace request parameter
- response =
- adminRestSession.putWithHeader(
- "/projects/new9?trace=issue/123",
- new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/456"));
- assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeaders(RestApiServlet.X_GERRIT_TRACE))
- .containsExactly("issue/123", "issue/456");
- assertThat(projectCreationListener.traceIds).containsExactly("issue/123", "issue/456");
- assertThat(projectCreationListener.isLoggingForced).isTrue();
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ // trace ID only specified by trace header
+ RestResponse response =
+ adminRestSession.putWithHeader(
+ "/projects/new6?trace", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new6");
+
+ // trace ID only specified by trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new7?trace=issue/123",
+ new BasicHeader(RestApiServlet.X_GERRIT_TRACE, null));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new7");
+
+ // same trace ID specified by trace header and trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new8?trace=issue/123",
+ new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new8");
+
+ // different trace IDs specified by trace header and trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new9?trace=issue/123",
+ new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/456"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeaders(RestApiServlet.X_GERRIT_TRACE))
+ .containsExactly("issue/123", "issue/456");
+ assertThat(projectCreationListener.traceIds).containsExactly("issue/123", "issue/456");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new9");
+ }
}
@Test
public void pushWithoutTrace() throws Exception {
- PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
- PushOneCommit.Result r = push.to("refs/heads/master");
- r.assertOkStatus();
- assertThat(commitValidationListener.traceId).isNull();
- assertThat(commitValidationListener.isLoggingForced).isFalse();
+ TraceValidatingCommitValidationListener commitValidationListener =
+ new TraceValidatingCommitValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(commitValidationListener)) {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ PushOneCommit.Result r = push.to("refs/heads/master");
+ r.assertOkStatus();
+ assertThat(commitValidationListener.traceId).isNull();
+ assertThat(commitValidationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(commitValidationListener.tags.get("project")).containsExactly(project.get());
+ }
}
@Test
public void pushWithTrace() throws Exception {
- PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
- push.setPushOptions(ImmutableList.of("trace"));
- PushOneCommit.Result r = push.to("refs/heads/master");
- r.assertOkStatus();
- assertThat(commitValidationListener.traceId).isNotNull();
- assertThat(commitValidationListener.isLoggingForced).isTrue();
+ TraceValidatingCommitValidationListener commitValidationListener =
+ new TraceValidatingCommitValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(commitValidationListener)) {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ push.setPushOptions(ImmutableList.of("trace"));
+ PushOneCommit.Result r = push.to("refs/heads/master");
+ r.assertOkStatus();
+ assertThat(commitValidationListener.traceId).isNotNull();
+ assertThat(commitValidationListener.isLoggingForced).isTrue();
+ assertThat(commitValidationListener.tags.get("project")).containsExactly(project.get());
+ }
}
@Test
public void pushWithTraceAndProvidedTraceId() throws Exception {
- PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
- push.setPushOptions(ImmutableList.of("trace=issue/123"));
- PushOneCommit.Result r = push.to("refs/heads/master");
- r.assertOkStatus();
- assertThat(commitValidationListener.traceId).isEqualTo("issue/123");
- assertThat(commitValidationListener.isLoggingForced).isTrue();
+ TraceValidatingCommitValidationListener commitValidationListener =
+ new TraceValidatingCommitValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(commitValidationListener)) {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ push.setPushOptions(ImmutableList.of("trace=issue/123"));
+ PushOneCommit.Result r = push.to("refs/heads/master");
+ r.assertOkStatus();
+ assertThat(commitValidationListener.traceId).isEqualTo("issue/123");
+ assertThat(commitValidationListener.isLoggingForced).isTrue();
+ assertThat(commitValidationListener.tags.get("project")).containsExactly(project.get());
+ }
}
@Test
public void pushForReviewWithoutTrace() throws Exception {
- PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
- PushOneCommit.Result r = push.to("refs/for/master");
- r.assertOkStatus();
- assertThat(commitValidationListener.traceId).isNull();
- assertThat(commitValidationListener.isLoggingForced).isFalse();
+ TraceValidatingCommitValidationListener commitValidationListener =
+ new TraceValidatingCommitValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(commitValidationListener)) {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ PushOneCommit.Result r = push.to("refs/for/master");
+ r.assertOkStatus();
+ assertThat(commitValidationListener.traceId).isNull();
+ assertThat(commitValidationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(commitValidationListener.tags.get("project")).containsExactly(project.get());
+ }
}
@Test
public void pushForReviewWithTrace() throws Exception {
- PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
- push.setPushOptions(ImmutableList.of("trace"));
- PushOneCommit.Result r = push.to("refs/for/master");
- r.assertOkStatus();
- assertThat(commitValidationListener.traceId).isNotNull();
- assertThat(commitValidationListener.isLoggingForced).isTrue();
+ TraceValidatingCommitValidationListener commitValidationListener =
+ new TraceValidatingCommitValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(commitValidationListener)) {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ push.setPushOptions(ImmutableList.of("trace"));
+ PushOneCommit.Result r = push.to("refs/for/master");
+ r.assertOkStatus();
+ assertThat(commitValidationListener.traceId).isNotNull();
+ assertThat(commitValidationListener.isLoggingForced).isTrue();
+ assertThat(commitValidationListener.tags.get("project")).containsExactly(project.get());
+ }
}
@Test
public void pushForReviewWithTraceAndProvidedTraceId() throws Exception {
- PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
- push.setPushOptions(ImmutableList.of("trace=issue/123"));
- PushOneCommit.Result r = push.to("refs/for/master");
- r.assertOkStatus();
- assertThat(commitValidationListener.traceId).isEqualTo("issue/123");
- assertThat(commitValidationListener.isLoggingForced).isTrue();
+ TraceValidatingCommitValidationListener commitValidationListener =
+ new TraceValidatingCommitValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(commitValidationListener)) {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ push.setPushOptions(ImmutableList.of("trace=issue/123"));
+ PushOneCommit.Result r = push.to("refs/for/master");
+ r.assertOkStatus();
+ assertThat(commitValidationListener.traceId).isEqualTo("issue/123");
+ assertThat(commitValidationListener.isLoggingForced).isTrue();
+ assertThat(commitValidationListener.tags.get("project")).containsExactly(project.get());
+ }
}
@Test
@@ -263,6 +368,409 @@ public class TraceIT extends AbstractDaemonTest {
assertForceLogging(false);
}
+ @Test
+ public void performanceLoggingForRestCall() throws Exception {
+ TestPerformanceLogger testPerformanceLogger = new TestPerformanceLogger();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(testPerformanceLogger)) {
+ RestResponse response = adminRestSession.put("/projects/new10");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+
+ // This assertion assumes that the server invokes the PerformanceLogger plugins before it
+ // sends
+ // the response to the client. If this assertion gets flaky it's likely that this got changed
+ // on
+ // server-side.
+ assertThat(testPerformanceLogger.logEntries()).isNotEmpty();
+ }
+ }
+
+ @Test
+ public void performanceLoggingForPush() throws Exception {
+ TestPerformanceLogger testPerformanceLogger = new TestPerformanceLogger();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(testPerformanceLogger)) {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ PushOneCommit.Result r = push.to("refs/heads/master");
+ r.assertOkStatus();
+ assertThat(testPerformanceLogger.logEntries()).isNotEmpty();
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.performanceLogging", value = "false")
+ public void noPerformanceLoggingIfDisabled() throws Exception {
+ TestPerformanceLogger testPerformanceLogger = new TestPerformanceLogger();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(testPerformanceLogger)) {
+ RestResponse response = adminRestSession.put("/projects/new11");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ PushOneCommit.Result r = push.to("refs/heads/master");
+ r.assertOkStatus();
+
+ assertThat(testPerformanceLogger.logEntries()).isEmpty();
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.projectPattern", value = "new12")
+ public void traceProject() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new12");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isEqualTo("issue123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new12");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.projectPattern", value = "new.*")
+ public void traceProjectMatchRegEx() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new13");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isEqualTo("issue123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new13");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.projectPattern", value = "foo.*")
+ public void traceProjectNoMatch() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new13");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new13");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.projectPattern", value = "][")
+ public void traceProjectInvalidRegEx() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new14");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new14");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.account", value = "1000000")
+ public void traceAccount() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new15");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isEqualTo("issue123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new15");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.account", value = "1000001")
+ public void traceAccountNoMatch() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new16");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new16");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.account", value = "999")
+ public void traceAccountNotFound() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new17");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new17");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.account", value = "invalid")
+ public void traceAccountInvalidId() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new18");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new18");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.requestType", value = "REST")
+ public void traceRequestType() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new19");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isEqualTo("issue123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new19");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.requestType", value = "SSH")
+ public void traceRequestTypeNoMatch() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new20");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new20");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.requestType", value = "FOO")
+ public void traceProjectInvalidRequestType() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new21");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new21");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.account", value = "1000000")
+ @GerritConfig(name = "tracing.issue123.projectPattern", value = "new.*")
+ public void traceProjectForAccount() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new22");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isEqualTo("issue123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new22");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.account", value = "1000000")
+ @GerritConfig(name = "tracing.issue123.projectPattern", value = "foo.*")
+ public void traceProjectForAccountNoProjectMatch() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new23");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new23");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.account", value = "1000001")
+ @GerritConfig(name = "tracing.issue123.projectPattern", value = "new.*")
+ public void traceProjectForAccountNoAccountMatch() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new24");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new24");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.requestUriPattern", value = "/projects/.*")
+ public void traceRequestUri() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new23");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isEqualTo("issue123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new23");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.requestUriPattern", value = "/projects/.*/foo")
+ public void traceRequestUriNoMatch() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new23");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new23");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.requestUriPattern", value = "][")
+ public void traceRequestUriInvalidRegEx() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/new24");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("new24");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "retry.retryWithTraceOnFailure", value = "true")
+ public void autoRetryWithTrace() throws Exception {
+ String changeId = createChange().getChangeId();
+ approve(changeId);
+
+ TraceSubmitRule traceSubmitRule = new TraceSubmitRule();
+ traceSubmitRule.failAlways = true;
+ try (Registration registration = extensionRegistry.newRegistration().add(traceSubmitRule)) {
+ RestResponse response = adminRestSession.post("/changes/" + changeId + "/submit");
+ assertThat(response.getStatusCode()).isEqualTo(SC_INTERNAL_SERVER_ERROR);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).startsWith("retry-on-failure-");
+ assertThat(traceSubmitRule.traceId).startsWith("retry-on-failure-");
+ assertThat(traceSubmitRule.isLoggingForced).isTrue();
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "retry.retryWithTraceOnFailure", value = "true")
+ public void noAutoRetryIfExceptionCausesNormalRetrying() throws Exception {
+ String changeId = createChange().getChangeId();
+ approve(changeId);
+
+ TraceSubmitRule traceSubmitRule = new TraceSubmitRule();
+ traceSubmitRule.failAlways = true;
+ try (Registration registration =
+ extensionRegistry
+ .newRegistration()
+ .add(traceSubmitRule)
+ .add(
+ new ExceptionHook() {
+ @Override
+ public boolean shouldRetry(Throwable t) {
+ return true;
+ }
+ })) {
+ RestResponse response = adminRestSession.post("/changes/" + changeId + "/submit");
+ assertThat(response.getStatusCode()).isEqualTo(SC_INTERNAL_SERVER_ERROR);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(traceSubmitRule.traceId).isNull();
+ assertThat(traceSubmitRule.isLoggingForced).isFalse();
+ }
+ }
+
+ @Test
+ public void noAutoRetryWithTraceIfDisabled() throws Exception {
+ String changeId = createChange().getChangeId();
+ approve(changeId);
+
+ TraceSubmitRule traceSubmitRule = new TraceSubmitRule();
+ traceSubmitRule.failOnce = true;
+ try (Registration registration = extensionRegistry.newRegistration().add(traceSubmitRule)) {
+ RestResponse response = adminRestSession.post("/changes/" + changeId + "/submit");
+ assertThat(response.getStatusCode()).isEqualTo(SC_INTERNAL_SERVER_ERROR);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(traceSubmitRule.traceId).isNull();
+ assertThat(traceSubmitRule.isLoggingForced).isFalse();
+ }
+ }
+
private void assertForceLogging(boolean expected) {
assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
.isEqualTo(expected);
@@ -273,6 +781,7 @@ public class TraceIT extends AbstractDaemonTest {
String traceId;
ImmutableSet<String> traceIds;
Boolean isLoggingForced;
+ ImmutableSetMultimap<String, String> tags;
@Override
public void validateNewProject(CreateProjectArgs args) throws ValidationException {
@@ -280,12 +789,14 @@ public class TraceIT extends AbstractDaemonTest {
Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null);
this.traceIds = LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID");
this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
}
}
private static class TraceValidatingCommitValidationListener implements CommitValidationListener {
String traceId;
Boolean isLoggingForced;
+ ImmutableSetMultimap<String, String> tags;
@Override
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
@@ -293,7 +804,67 @@ public class TraceIT extends AbstractDaemonTest {
this.traceId =
Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null);
this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
return ImmutableList.of();
}
}
+
+ private static class TraceChangeIndexedListener implements ChangeIndexedListener {
+ ImmutableSetMultimap<String, String> tags;
+
+ @Override
+ public void onChangeIndexed(String projectName, int id) {
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
+ }
+
+ @Override
+ public void onChangeDeleted(int id) {}
+ }
+
+ private static class TraceSubmitRule implements SubmitRule {
+ String traceId;
+ Boolean isLoggingForced;
+ boolean failOnce;
+ boolean failAlways;
+
+ @Override
+ public Optional<SubmitRecord> evaluate(ChangeData changeData) {
+ this.traceId =
+ Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null);
+ this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
+
+ if (failOnce || failAlways) {
+ failOnce = false;
+ throw new IllegalStateException("forced failure from test");
+ }
+
+ SubmitRecord submitRecord = new SubmitRecord();
+ submitRecord.status = SubmitRecord.Status.OK;
+ return Optional.of(submitRecord);
+ }
+ }
+
+ private static class TestPerformanceLogger implements PerformanceLogger {
+ private List<PerformanceLogEntry> logEntries = new ArrayList<>();
+
+ @Override
+ public void log(String operation, long durationMs, Metadata metadata) {
+ logEntries.add(PerformanceLogEntry.create(operation, metadata));
+ }
+
+ ImmutableList<PerformanceLogEntry> logEntries() {
+ return ImmutableList.copyOf(logEntries);
+ }
+ }
+
+ @AutoValue
+ abstract static class PerformanceLogEntry {
+ static PerformanceLogEntry create(String operation, Metadata metadata) {
+ return new AutoValue_TraceIT_PerformanceLogEntry(operation, metadata);
+ }
+
+ abstract String operation();
+
+ abstract Metadata metadata();
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
index 5e652c0c18..03202f2002 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
@@ -18,21 +18,33 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.reviewdb.client.Account;
import java.util.List;
public class AccountAssert {
-
- public static void assertAccountInfo(TestAccount a, AccountInfo ai) {
- assertThat(a.id().get()).isEqualTo(ai._accountId);
- assertThat(a.fullName()).isEqualTo(ai.name);
- assertThat(a.email()).isEqualTo(ai.email);
+ /**
+ * Asserts an AccountInfo for an active account.
+ *
+ * @param testAccount the TestAccount which the provided AccountInfo is expected to match
+ * @param accountInfo the AccountInfo that should be asserted
+ */
+ public static void assertAccountInfo(TestAccount testAccount, AccountInfo accountInfo) {
+ assertThat(accountInfo._accountId).isEqualTo(testAccount.id().get());
+ assertThat(accountInfo.name).isEqualTo(testAccount.fullName());
+ assertThat(accountInfo.email).isEqualTo(testAccount.email());
+ assertThat(accountInfo.inactive).isNull();
}
+ /**
+ * Asserts an AccountInfos for active accounts.
+ *
+ * @param expected the TestAccounts which the provided AccountInfos are expected to match
+ * @param actual the AccountInfos that should be asserted
+ */
public static void assertAccountInfos(List<TestAccount> expected, List<AccountInfo> actual) {
Iterable<Account.Id> expectedIds = TestAccount.ids(expected);
- Iterable<Account.Id> actualIds = Iterables.transform(actual, a -> new Account.Id(a._accountId));
+ Iterable<Account.Id> actualIds = Iterables.transform(actual, a -> Account.id(a._accountId));
assertThat(actualIds).containsExactlyElementsIn(expectedIds).inOrder();
for (int i = 0; i < expected.size(); i++) {
AccountAssert.assertAccountInfo(expected.get(i), actual.get(i));
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/BUILD b/javatests/com/google/gerrit/acceptance/rest/account/BUILD
index 66ea6f33c3..a00a8b2886 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/account/BUILD
@@ -5,7 +5,10 @@ acceptance_tests(
srcs = glob(["*IT.java"]),
group = "rest_account",
labels = ["rest"],
- deps = [":util"],
+ deps = [
+ ":util",
+ "//java/com/google/gerrit/server/account/externalids/testing",
+ ],
)
java_library(
@@ -18,7 +21,7 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/acceptance:lib",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//lib:junit",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java b/javatests/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java
index e7ce43f69c..be4fde02c2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/CapabilitiesIT.java
@@ -14,8 +14,11 @@
package com.google.gerrit.acceptance.rest.account;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.capabilityKey;
import static com.google.gerrit.common.data.GlobalCapability.ACCESS_DATABASE;
import static com.google.gerrit.common.data.GlobalCapability.ADMINISTRATE_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.BATCH_CHANGES_LIMIT;
@@ -26,24 +29,30 @@ import static com.google.gerrit.common.data.GlobalCapability.QUERY_LIMIT;
import static com.google.gerrit.common.data.GlobalCapability.RUN_AS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import com.google.common.collect.Iterables;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
+import com.google.inject.Inject;
import org.junit.Test;
public class CapabilitiesIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+
@Test
public void capabilitiesUser() throws Exception {
- Iterable<String> all =
- Iterables.filter(
- GlobalCapability.getAllNames(),
- c -> !ADMINISTRATE_SERVER.equals(c) && !PRIORITY.equals(c));
-
- allowGlobalCapabilities(REGISTERED_USERS, all);
+ ImmutableList<String> all =
+ GlobalCapability.getAllNames().stream()
+ .filter(c -> !ADMINISTRATE_SERVER.equals(c) && !PRIORITY.equals(c))
+ .collect(toImmutableList());
+ TestProjectUpdate.Builder allowBuilder = projectOperations.allProjectsForUpdate();
+ all.forEach(c -> allowBuilder.add(allowCapability(c).group(REGISTERED_USERS)));
+ allowBuilder.update();
try {
RestResponse r = userRestSession.get("/accounts/self/capabilities");
r.assertOK();
@@ -67,7 +76,9 @@ public class CapabilitiesIT extends AbstractDaemonTest {
}
}
} finally {
- removeGlobalCapabilities(REGISTERED_USERS, all);
+ TestProjectUpdate.Builder removeBuilder = projectOperations.allProjectsForUpdate();
+ all.forEach(c -> removeBuilder.remove(capabilityKey(c).group(REGISTERED_USERS)));
+ removeBuilder.update();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
index 84f218d091..aaeed0214b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
@@ -16,6 +16,7 @@ package com.google.gerrit.acceptance.rest.account;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableMultimap;
@@ -24,13 +25,13 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.accounts.EmailApi;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.common.EmailInfo;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
@@ -149,16 +150,20 @@ public class EmailIT extends AbstractDaemonTest {
@Test
public void setPreferredEmailToNonExistingEmail() throws Exception {
String email = "non-existing@example.com";
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Not found: " + email);
- gApi.accounts().self().email(email).setPreferred();
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.accounts().self().email(email).setPreferred());
+ assertThat(thrown).hasMessageThat().contains("Not found: " + email);
}
@Test
public void setPreferredEmailToEmailOfOtherAccount() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Not found: " + user.email());
- gApi.accounts().self().email(user.email()).setPreferred();
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.accounts().self().email(user.email()).setPreferred());
+ assertThat(thrown).hasMessageThat().contains("Not found: " + user.email());
}
@Test
@@ -201,9 +206,11 @@ public class EmailIT extends AbstractDaemonTest {
Context oldCtx =
createContextWithCustomRealm(new RealmWithAdditionalEmails(admin.id(), user.email()));
try {
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("email in use by another account");
- gApi.accounts().self().email(user.email()).setPreferred();
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> gApi.accounts().self().email(user.email()).setPreferred());
+ assertThat(thrown).hasMessageThat().contains("email in use by another account");
} finally {
atrScope.set(oldCtx);
}
@@ -248,9 +255,7 @@ public class EmailIT extends AbstractDaemonTest {
// Now the email is no longer found
requestScopeOperations.resetCurrentApiUser();
- emailApi = gApi.accounts().self().email(email);
- exception.expect(ResourceNotFoundException.class);
- emailApi.get();
+ assertThrows(ResourceNotFoundException.class, () -> gApi.accounts().self().email(email).get());
}
private Set<String> getEmails() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index f08fa447b0..6e164356f5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -16,16 +16,21 @@ package com.google.gerrit.acceptance.rest.account;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.acceptance.GitUtil.fetch;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_UUID;
+import static com.google.gerrit.server.account.externalids.testing.ExternalIdTestUtil.insertExternalIdWithEmptyNote;
+import static com.google.gerrit.server.account.externalids.testing.ExternalIdTestUtil.insertExternalIdWithInvalidConfig;
+import static com.google.gerrit.server.account.externalids.testing.ExternalIdTestUtil.insertExternalIdWithKeyThatDoesntMatchNoteId;
+import static com.google.gerrit.server.account.externalids.testing.ExternalIdTestUtil.insertExternalIdWithoutAccountId;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -33,9 +38,12 @@ import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
@@ -44,8 +52,6 @@ import com.google.gerrit.extensions.api.config.ConsistencyCheckInput.CheckAccoun
import com.google.gerrit.extensions.common.AccountExternalIdInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.externalids.ExternalId;
@@ -54,6 +60,7 @@ import com.google.gerrit.server.account.externalids.ExternalIdReader;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.testing.ConfigSuite;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -70,13 +77,8 @@ import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.FooterLine;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -91,11 +93,26 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Inject private ExternalIds externalIds;
@Inject private ExternalIdReader externalIdReader;
@Inject private ExternalIdNotes.Factory externalIdNotesFactory;
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
+ @ConfigSuite.Default
+ public static Config partialCacheReloadingEnabled() {
+ Config cfg = new Config();
+ cfg.setBoolean("cache", "external_ids_map", "enablePartialReloads", true);
+ return cfg;
+ }
+
+ @ConfigSuite.Config
+ public static Config partialCacheReloadingDisabled() {
+ Config cfg = new Config();
+ cfg.setBoolean("cache", "external_ids_map", "enablePartialReloads", false);
+ return cfg;
+ }
+
@Test
public void getExternalIds() throws Exception {
- Collection<ExternalId> expectedIds = getAccountState(user.id()).getExternalIds();
+ Collection<ExternalId> expectedIds = getAccountState(user.id()).externalIds();
List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
RestResponse response = userRestSession.get("/accounts/self/external.ids");
@@ -112,16 +129,20 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void getExternalIdsOfOtherUserNotAllowed() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("access database not permitted");
- gApi.accounts().id(admin.id().get()).getExternalIds();
+ AuthException thrown =
+ assertThrows(
+ AuthException.class, () -> gApi.accounts().id(admin.id().get()).getExternalIds());
+ assertThat(thrown).hasMessageThat().contains("access database not permitted");
}
@Test
public void getExternalIdsOfOtherUserWithAccessDatabase() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
- Collection<ExternalId> expectedIds = getAccountState(admin.id()).getExternalIds();
+ Collection<ExternalId> expectedIds = getAccountState(admin.id()).externalIds();
List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
RestResponse response = userRestSession.get("/accounts/" + admin.id() + "/external.ids");
@@ -165,27 +186,38 @@ public class ExternalIdIT extends AbstractDaemonTest {
public void deleteExternalIdsOfOtherUserNotAllowed() throws Exception {
List<AccountExternalIdInfo> extIds = gApi.accounts().self().getExternalIds();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("access database not permitted");
- gApi.accounts()
- .id(admin.id().get())
- .deleteExternalIds(extIds.stream().map(e -> e.identity).collect(toList()));
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () ->
+ gApi.accounts()
+ .id(admin.id().get())
+ .deleteExternalIds(extIds.stream().map(e -> e.identity).collect(toList())));
+ assertThat(thrown).hasMessageThat().contains("access database not permitted");
}
@Test
public void deleteExternalIdOfOtherUserUnderOwnAccount_UnprocessableEntity() throws Exception {
List<AccountExternalIdInfo> extIds = gApi.accounts().self().getExternalIds();
requestScopeOperations.setApiUser(user.id());
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage(String.format("External id %s does not exist", extIds.get(0).identity));
- gApi.accounts()
- .self()
- .deleteExternalIds(extIds.stream().map(e -> e.identity).collect(toList()));
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () ->
+ gApi.accounts()
+ .self()
+ .deleteExternalIds(extIds.stream().map(e -> e.identity).collect(toList())));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("External id %s does not exist", extIds.get(0).identity));
}
@Test
public void deleteExternalIdsOfOtherUserWithAccessDatabase() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
List<AccountExternalIdInfo> externalIds = gApi.accounts().self().getExternalIds();
@@ -248,29 +280,33 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void fetchExternalIdsBranch() throws Exception {
- TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user);
+ final TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user);
// refs/meta/external-ids is only visible to users with the 'Access Database' capability
- try {
- fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS);
- fail("expected TransportException");
- } catch (TransportException e) {
- assertThat(e.getMessage())
- .isEqualTo(
- "Remote does not have " + RefNames.REFS_EXTERNAL_IDS + " available for fetch.");
- }
-
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ TransportException thrown =
+ assertThrows(
+ TransportException.class, () -> fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo("Remote does not have " + RefNames.REFS_EXTERNAL_IDS + " available for fetch.");
+
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
// re-clone to get new request context, otherwise the old global capabilities are still cached
// in the IdentifiedUser object
- allUsersRepo = cloneProject(allUsers, user);
- fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS);
+ TestRepository<InMemoryRepository> allUsersRepo2 = cloneProject(allUsers, user);
+ fetch(allUsersRepo2, RefNames.REFS_EXTERNAL_IDS);
}
@Test
public void pushToExternalIdsBranch() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS);
@@ -295,14 +331,21 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void pushToExternalIdsBranchRejectsExternalIdWithoutAccountId() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS);
allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS);
insertExternalIdWithoutAccountId(
- allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), "foo:bar");
+ allUsersRepo.getRepository(),
+ allUsersRepo.getRevWalk(),
+ admin.newIdent(),
+ admin.id(),
+ "foo:bar");
allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS);
allowPushOfExternalIds();
@@ -313,14 +356,21 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void pushToExternalIdsBranchRejectsExternalIdWithKeyThatDoesntMatchTheNoteId()
throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS);
allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS);
insertExternalIdWithKeyThatDoesntMatchNoteId(
- allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), "foo:bar");
+ allUsersRepo.getRepository(),
+ allUsersRepo.getRevWalk(),
+ admin.newIdent(),
+ admin.id(),
+ "foo:bar");
allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS);
allowPushOfExternalIds();
@@ -330,14 +380,17 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void pushToExternalIdsBranchRejectsExternalIdWithInvalidConfig() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS);
allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS);
insertExternalIdWithInvalidConfig(
- allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), "foo:bar");
+ allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), admin.newIdent(), "foo:bar");
allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS);
allowPushOfExternalIds();
@@ -347,14 +400,17 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void pushToExternalIdsBranchRejectsExternalIdWithEmptyNote() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS);
allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS);
insertExternalIdWithEmptyNote(
- allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), "foo:bar");
+ allUsersRepo.getRepository(), allUsersRepo.getRevWalk(), admin.newIdent(), "foo:bar");
allUsersRepo.reset(RefNames.REFS_EXTERNAL_IDS);
allowPushOfExternalIds();
@@ -387,7 +443,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
private void testPushToExternalIdsBranchRejectsInvalidExternalId(ExternalId invalidExtId)
throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.REFS_EXTERNAL_IDS + ":" + RefNames.REFS_EXTERNAL_IDS);
@@ -403,7 +462,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void readExternalIdsWhenInvalidExternalIdsExist() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
requestScopeOperations.resetCurrentApiUser();
insertValidExternalIds();
@@ -424,7 +486,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void checkConsistency() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
requestScopeOperations.resetCurrentApiUser();
insertValidExternalIds();
@@ -446,9 +511,11 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void checkConsistencyNotAllowed() throws Exception {
- exception.expect(AuthException.class);
- exception.expectMessage("access database not permitted");
- gApi.config().server().checkConsistency(new ConsistencyCheckInput());
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.config().server().checkConsistency(new ConsistencyCheckInput()));
+ assertThat(thrown).hasMessageThat().contains("access database not permitted");
}
private ConsistencyProblemInfo consistencyError(String message) {
@@ -525,7 +592,8 @@ public class ExternalIdIT extends AbstractDaemonTest {
try (Repository repo = repoManager.openRepository(allUsers);
RevWalk rw = new RevWalk(repo)) {
String externalId = nextId(scheme, i);
- String noteId = insertExternalIdWithoutAccountId(repo, rw, externalId);
+ String noteId =
+ insertExternalIdWithoutAccountId(repo, rw, admin.newIdent(), admin.id(), externalId);
expectedProblems.add(
consistencyError(
"Invalid external ID config for note '"
@@ -535,7 +603,9 @@ public class ExternalIdIT extends AbstractDaemonTest {
+ ".accountId' is missing, expected account ID"));
externalId = nextId(scheme, i);
- noteId = insertExternalIdWithKeyThatDoesntMatchNoteId(repo, rw, externalId);
+ noteId =
+ insertExternalIdWithKeyThatDoesntMatchNoteId(
+ repo, rw, admin.newIdent(), admin.id(), externalId);
expectedProblems.add(
consistencyError(
"Invalid external ID config for note '"
@@ -546,12 +616,12 @@ public class ExternalIdIT extends AbstractDaemonTest {
+ noteId
+ "'"));
- noteId = insertExternalIdWithInvalidConfig(repo, rw, nextId(scheme, i));
+ noteId = insertExternalIdWithInvalidConfig(repo, rw, admin.newIdent(), nextId(scheme, i));
expectedProblems.add(
consistencyError(
"Invalid external ID config for note '" + noteId + "': Invalid line in config file"));
- noteId = insertExternalIdWithEmptyNote(repo, rw, nextId(scheme, i));
+ noteId = insertExternalIdWithEmptyNote(repo, rw, admin.newIdent(), nextId(scheme, i));
expectedProblems.add(
consistencyError(
"Invalid external ID config for note '"
@@ -570,125 +640,8 @@ public class ExternalIdIT extends AbstractDaemonTest {
"password");
}
- private String insertExternalIdWithoutAccountId(Repository repo, RevWalk rw, String externalId)
- throws IOException {
- return insertExternalId(
- repo,
- rw,
- (ins, noteMap) -> {
- ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id());
- ObjectId noteId = extId.key().sha1();
- Config c = new Config();
- extId.writeToConfig(c);
- c.unset("externalId", extId.key().get(), "accountId");
- byte[] raw = c.toText().getBytes(UTF_8);
- ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
- noteMap.set(noteId, dataBlob);
- return noteId;
- });
- }
-
- private String insertExternalIdWithKeyThatDoesntMatchNoteId(
- Repository repo, RevWalk rw, String externalId) throws IOException {
- return insertExternalId(
- repo,
- rw,
- (ins, noteMap) -> {
- ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id());
- ObjectId noteId = ExternalId.Key.parse(externalId + "x").sha1();
- Config c = new Config();
- extId.writeToConfig(c);
- byte[] raw = c.toText().getBytes(UTF_8);
- ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
- noteMap.set(noteId, dataBlob);
- return noteId;
- });
- }
-
- private String insertExternalIdWithInvalidConfig(Repository repo, RevWalk rw, String externalId)
- throws IOException {
- return insertExternalId(
- repo,
- rw,
- (ins, noteMap) -> {
- ObjectId noteId = ExternalId.Key.parse(externalId).sha1();
- byte[] raw = "bad-config".getBytes(UTF_8);
- ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
- noteMap.set(noteId, dataBlob);
- return noteId;
- });
- }
-
- private String insertExternalIdWithEmptyNote(Repository repo, RevWalk rw, String externalId)
- throws IOException {
- return insertExternalId(
- repo,
- rw,
- (ins, noteMap) -> {
- ObjectId noteId = ExternalId.Key.parse(externalId).sha1();
- byte[] raw = "".getBytes(UTF_8);
- ObjectId dataBlob = ins.insert(OBJ_BLOB, raw);
- noteMap.set(noteId, dataBlob);
- return noteId;
- });
- }
-
- private String insertExternalId(Repository repo, RevWalk rw, ExternalIdInserter extIdInserter)
- throws IOException {
- ObjectId rev = ExternalIdReader.readRevision(repo);
- NoteMap noteMap = ExternalIdReader.readNoteMap(rw, rev);
-
- try (ObjectInserter ins = repo.newObjectInserter()) {
- ObjectId noteId = extIdInserter.addNote(ins, noteMap);
-
- CommitBuilder cb = new CommitBuilder();
- cb.setMessage("Update external IDs");
- cb.setTreeId(noteMap.writeTree(ins));
- cb.setAuthor(admin.newIdent());
- cb.setCommitter(admin.newIdent());
- if (!rev.equals(ObjectId.zeroId())) {
- cb.setParentId(rev);
- } else {
- cb.setParentIds(); // Ref is currently nonexistent, commit has no parents.
- }
- if (cb.getTreeId() == null) {
- if (rev.equals(ObjectId.zeroId())) {
- cb.setTreeId(ins.insert(OBJ_TREE, new byte[] {})); // No parent, assume empty tree.
- } else {
- RevCommit p = rw.parseCommit(rev);
- cb.setTreeId(p.getTree()); // Copy tree from parent.
- }
- }
- ObjectId commitId = ins.insert(cb);
- ins.flush();
-
- RefUpdate u = repo.updateRef(RefNames.REFS_EXTERNAL_IDS);
- u.setExpectedOldObjectId(rev);
- u.setNewObjectId(commitId);
- RefUpdate.Result res = u.update();
- switch (res) {
- case NEW:
- case FAST_FORWARD:
- case NO_CHANGE:
- case RENAMED:
- case FORCED:
- break;
- case LOCK_FAILURE:
- case IO_FAILURE:
- case NOT_ATTEMPTED:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- throw new IOException("Updating external IDs failed with " + res);
- }
- return noteId.getName();
- }
- }
-
private ExternalId createExternalIdForNonExistingAccount(String externalId) {
- return ExternalId.create(ExternalId.Key.parse(externalId), new Account.Id(1));
+ return ExternalId.create(ExternalId.Key.parse(externalId), Account.id(1));
}
private ExternalId createExternalIdWithInvalidEmail(String externalId) {
@@ -715,7 +668,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void readExternalIdWithAccountIdThatCanBeExpressedInKiB() throws Exception {
ExternalId.Key extIdKey = ExternalId.Key.parse("foo:bar");
- Account.Id accountId = new Account.Id(1024 * 100);
+ Account.Id accountId = Account.id(1024 * 100);
accountsUpdateProvider
.get()
.insert(
@@ -757,23 +710,25 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void byAccountFailIfReadingExternalIdsFails() throws Exception {
+ assume().that(isPartialCacheReloadingEnabled()).isFalse();
+
try (AutoCloseable ctx = createFailOnLoadContext()) {
// update external ID branch so that external IDs need to be reloaded
insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id()));
- exception.expect(IOException.class);
- externalIds.byAccount(admin.id());
+ assertThrows(IOException.class, () -> externalIds.byAccount(admin.id()));
}
}
@Test
public void byEmailFailIfReadingExternalIdsFails() throws Exception {
+ assume().that(isPartialCacheReloadingEnabled()).isFalse();
+
try (AutoCloseable ctx = createFailOnLoadContext()) {
// update external ID branch so that external IDs need to be reloaded
insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id()));
- exception.expect(IOException.class);
- externalIds.byEmail(admin.email());
+ assertThrows(IOException.class, () -> externalIds.byEmail(admin.email()));
}
}
@@ -910,6 +865,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
}
}
+ private boolean isPartialCacheReloadingEnabled() {
+ return cfg.getBoolean("cache", "external_ids_map", "enablePartialReloads", true);
+ }
+
private void insertExtId(ExternalId extId) throws Exception {
accountsUpdateProvider
.get()
@@ -976,9 +935,13 @@ public class ExternalIdIT extends AbstractDaemonTest {
return info;
}
- private void allowPushOfExternalIds() throws IOException, ConfigInvalidException {
- grant(allUsers, RefNames.REFS_EXTERNAL_IDS, Permission.READ);
- grant(allUsers, RefNames.REFS_EXTERNAL_IDS, Permission.PUSH);
+ private void allowPushOfExternalIds() {
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.READ).ref(RefNames.REFS_EXTERNAL_IDS).group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_EXTERNAL_IDS).group(adminGroupUuid()))
+ .update();
}
private void assertRefUpdateFailure(RemoteRefUpdate update, String msg) {
@@ -990,9 +953,4 @@ public class ExternalIdIT extends AbstractDaemonTest {
externalIdReader.setFailOnLoad(true);
return () -> externalIdReader.setFailOnLoad(false);
}
-
- @FunctionalInterface
- private interface ExternalIdInserter {
- public ObjectId addNote(ObjectInserter ins, NoteMap noteMap) throws IOException;
- }
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
index 27ae8b1295..9202f42803 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
@@ -19,8 +19,8 @@ import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAcco
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.AccountDetailInfo;
-import com.google.gerrit.reviewdb.client.Account;
import org.junit.Test;
public class GetAccountDetailIT extends AbstractDaemonTest {
@@ -30,6 +30,6 @@ public class GetAccountDetailIT extends AbstractDaemonTest {
AccountDetailInfo info = newGson().fromJson(r.getReader(), AccountDetailInfo.class);
assertAccountInfo(admin, info);
Account account = getAccount(admin.id());
- assertThat(info.registeredOn).isEqualTo(account.getRegisteredOn());
+ assertThat(info.registeredOn).isEqualTo(account.registeredOn());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
index 11f7c0f0af..782638a107 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
@@ -14,19 +14,26 @@
package com.google.gerrit.acceptance.rest.account;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAccountInfo;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.inject.Inject;
import org.junit.Test;
@NoHttpd
public class GetAccountIT extends AbstractDaemonTest {
- @Test(expected = ResourceNotFoundException.class)
+ @Inject private AccountOperations accountOperations;
+
+ @Test
public void getNonExistingAccount_NotFound() throws Exception {
- gApi.accounts().id("non-existing").get();
+ assertThrows(ResourceNotFoundException.class, () -> gApi.accounts().id("non-existing").get());
}
@Test
@@ -50,6 +57,16 @@ public class GetAccountIT extends AbstractDaemonTest {
testGetAccount("self", admin);
}
+ @Test
+ public void getInactiveAccount() throws Exception {
+ accountOperations.account(user.id()).forUpdate().inactive().update();
+ AccountInfo accountInfo = gApi.accounts().id(user.id().get()).get();
+ assertThat(accountInfo._accountId).isEqualTo(user.id().get());
+ assertThat(accountInfo.name).isEqualTo(user.fullName());
+ assertThat(accountInfo.email).isEqualTo(user.email());
+ assertThat(accountInfo.inactive).isTrue();
+ }
+
private void testGetAccount(String id, TestAccount expectedAccount) throws Exception {
assertAccountInfo(expectedAccount, gApi.accounts().id(id).get());
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
index 4dec505066..faaba06cfe 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
@@ -15,9 +15,15 @@
package com.google.gerrit.acceptance.rest.account;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.capabilityKey;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -28,10 +34,17 @@ import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.RestSession;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
@@ -49,17 +62,11 @@ import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.RobotComment;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.account.AccountControl;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import org.apache.http.Header;
@@ -73,6 +80,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
@Inject private ApprovalsUtil approvalsUtil;
@Inject private ChangeMessagesUtil cmUtil;
@Inject private CommentsUtil commentsUtil;
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
private RestSession anonRestSession;
@@ -106,11 +114,11 @@ public class ImpersonationIT extends AbstractDaemonTest {
revision.review(in);
PatchSetApproval psa = Iterables.getOnlyElement(r.getChange().approvals().values());
- assertThat(psa.getPatchSetId().get()).isEqualTo(1);
- assertThat(psa.getLabel()).isEqualTo("Code-Review");
- assertThat(psa.getAccountId()).isEqualTo(user.id());
- assertThat(psa.getValue()).isEqualTo(1);
- assertThat(psa.getRealAccountId()).isEqualTo(admin.id());
+ assertThat(psa.patchSetId().get()).isEqualTo(1);
+ assertThat(psa.label()).isEqualTo("Code-Review");
+ assertThat(psa.accountId()).isEqualTo(user.id());
+ assertThat(psa.value()).isEqualTo(1);
+ assertThat(psa.realAccountId()).isEqualTo(admin.id());
ChangeData cd = r.getChange();
ChangeMessage m = Iterables.getLast(cmUtil.byChange(cd.notes()));
@@ -129,9 +137,10 @@ public class ImpersonationIT extends AbstractDaemonTest {
in.onBehalfOf = user.id().toString();
in.message = "Message on behalf of";
- exception.expect(AuthException.class);
- exception.expectMessage("label required to post review on behalf of \"" + in.onBehalfOf + '"');
- revision.review(in);
+ AuthException thrown = assertThrows(AuthException.class, () -> revision.review(in));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("label required to post review on behalf of \"" + in.onBehalfOf + '"');
}
@Test
@@ -143,9 +152,10 @@ public class ImpersonationIT extends AbstractDaemonTest {
ReviewInput in = new ReviewInput().label("Not-A-Label", 5);
in.onBehalfOf = user.id().toString();
- exception.expect(BadRequestException.class);
- exception.expectMessage("label \"Not-A-Label\" is not a configured label");
- gApi.changes().id(changeId).current().review(in);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class, () -> gApi.changes().id(changeId).current().review(in));
+ assertThat(thrown).hasMessageThat().contains("label \"Not-A-Label\" is not a configured label");
}
@Test
@@ -163,7 +173,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
@Test
public void voteOnBehalfOfLabelNotPermitted() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
- LabelType verified = Util.verified();
+ LabelType verified = TestLabels.verified();
u.getConfig().getLabelSections().put(verified.getName(), verified);
u.save();
}
@@ -175,10 +185,11 @@ public class ImpersonationIT extends AbstractDaemonTest {
in.onBehalfOf = user.id().toString();
in.label("Verified", 1);
- exception.expect(AuthException.class);
- exception.expectMessage(
- "not permitted to modify label \"Verified\" on behalf of \"" + in.onBehalfOf + '"');
- revision.review(in);
+ AuthException thrown = assertThrows(AuthException.class, () -> revision.review(in));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "not permitted to modify label \"Verified\" on behalf of \"" + in.onBehalfOf + '"');
}
@Test
@@ -207,11 +218,11 @@ public class ImpersonationIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).current().review(in);
PatchSetApproval psa = Iterables.getOnlyElement(r.getChange().approvals().values());
- assertThat(psa.getPatchSetId().get()).isEqualTo(1);
- assertThat(psa.getLabel()).isEqualTo("Code-Review");
- assertThat(psa.getAccountId()).isEqualTo(user.id());
- assertThat(psa.getValue()).isEqualTo(1);
- assertThat(psa.getRealAccountId()).isEqualTo(admin.id());
+ assertThat(psa.patchSetId().get()).isEqualTo(1);
+ assertThat(psa.label()).isEqualTo("Code-Review");
+ assertThat(psa.accountId()).isEqualTo(user.id());
+ assertThat(psa.value()).isEqualTo(1);
+ assertThat(psa.realAccountId()).isEqualTo(admin.id());
ChangeData cd = r.getChange();
Comment c = Iterables.getOnlyElement(commentsUtil.publishedByChange(cd.notes()));
@@ -266,9 +277,10 @@ public class ImpersonationIT extends AbstractDaemonTest {
in.label("Code-Review", 1);
in.drafts = DraftHandling.PUBLISH;
- exception.expect(AuthException.class);
- exception.expectMessage("not allowed to modify other user's drafts");
- gApi.changes().id(r.getChangeId()).current().review(in);
+ AuthException thrown =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(r.getChangeId()).current().review(in));
+ assertThat(thrown).hasMessageThat().contains("not allowed to modify other user's drafts");
}
@Test
@@ -281,10 +293,10 @@ public class ImpersonationIT extends AbstractDaemonTest {
in.onBehalfOf = "doesnotexist";
in.label("Code-Review", 1);
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("not found");
- exception.expectMessage("doesnotexist");
- revision.review(in);
+ UnprocessableEntityException thrown =
+ assertThrows(UnprocessableEntityException.class, () -> revision.review(in));
+ assertThat(thrown).hasMessageThat().contains("not found");
+ assertThat(thrown).hasMessageThat().contains("doesnotexist");
}
@Test
@@ -299,9 +311,11 @@ public class ImpersonationIT extends AbstractDaemonTest {
in.onBehalfOf = user.id().toString();
in.label("Code-Review", 1);
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("on_behalf_of account " + user.id() + " cannot see change");
- revision.review(in);
+ UnprocessableEntityException thrown =
+ assertThrows(UnprocessableEntityException.class, () -> revision.review(in));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("on_behalf_of account " + user.id() + " cannot see change");
}
@GerritConfig(name = "accounts.visibility", value = "SAME_GROUP")
@@ -318,10 +332,10 @@ public class ImpersonationIT extends AbstractDaemonTest {
in.onBehalfOf = user.id().toString();
in.label("Code-Review", 1);
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("not found");
- exception.expectMessage(in.onBehalfOf);
- revision.review(in);
+ UnprocessableEntityException thrown =
+ assertThrows(UnprocessableEntityException.class, () -> revision.review(in));
+ assertThat(thrown).hasMessageThat().contains("not found");
+ assertThat(thrown).hasMessageThat().contains(in.onBehalfOf);
}
@Test
@@ -338,8 +352,8 @@ public class ImpersonationIT extends AbstractDaemonTest {
assertThat(cd.change().isMerged()).isTrue();
PatchSetApproval submitter =
approvalsUtil.getSubmitter(cd.notes(), cd.change().currentPatchSetId());
- assertThat(submitter.getAccountId()).isEqualTo(admin2.id());
- assertThat(submitter.getRealAccountId()).isEqualTo(admin.id());
+ assertThat(submitter.accountId()).isEqualTo(admin2.id());
+ assertThat(submitter.realAccountId()).isEqualTo(admin.id());
}
@Test
@@ -350,10 +364,12 @@ public class ImpersonationIT extends AbstractDaemonTest {
gApi.changes().id(changeId).current().review(ReviewInput.approve());
SubmitInput in = new SubmitInput();
in.onBehalfOf = "doesnotexist";
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("not found");
- exception.expectMessage("doesnotexist");
- gApi.changes().id(changeId).current().submit(in);
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.changes().id(changeId).current().submit(in));
+ assertThat(thrown).hasMessageThat().contains("not found");
+ assertThat(thrown).hasMessageThat().contains("doesnotexist");
}
@Test
@@ -365,9 +381,15 @@ public class ImpersonationIT extends AbstractDaemonTest {
.review(ReviewInput.approve());
SubmitInput in = new SubmitInput();
in.onBehalfOf = admin2.email();
- exception.expect(AuthException.class);
- exception.expectMessage("submit on behalf of other users not permitted");
- gApi.changes().id(project.get() + "~master~" + r.getChangeId()).current().submit(in);
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () ->
+ gApi.changes()
+ .id(project.get() + "~master~" + r.getChangeId())
+ .current()
+ .submit(in));
+ assertThat(thrown).hasMessageThat().contains("submit on behalf of other users not permitted");
}
@Test
@@ -380,9 +402,13 @@ public class ImpersonationIT extends AbstractDaemonTest {
gApi.changes().id(changeId).current().review(ReviewInput.approve());
SubmitInput in = new SubmitInput();
in.onBehalfOf = user.email();
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("on_behalf_of account " + user.id() + " cannot see change");
- gApi.changes().id(changeId).current().submit(in);
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.changes().id(changeId).current().submit(in));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("on_behalf_of account " + user.id() + " cannot see change");
}
@GerritConfig(name = "accounts.visibility", value = "SAME_GROUP")
@@ -397,10 +423,12 @@ public class ImpersonationIT extends AbstractDaemonTest {
gApi.changes().id(changeId).current().review(ReviewInput.approve());
SubmitInput in = new SubmitInput();
in.onBehalfOf = user.email();
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("not found");
- exception.expectMessage(in.onBehalfOf);
- gApi.changes().id(changeId).current().submit(in);
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.changes().id(changeId).current().submit(in));
+ assertThat(thrown).hasMessageThat().contains("not found");
+ assertThat(thrown).hasMessageThat().contains(in.onBehalfOf);
}
@Test
@@ -510,11 +538,11 @@ public class ImpersonationIT extends AbstractDaemonTest {
adminRestSession.postWithHeader(endpoint, runAsHeader(user2.id()), in).assertOK();
PatchSetApproval psa = Iterables.getOnlyElement(r.getChange().approvals().values());
- assertThat(psa.getPatchSetId().get()).isEqualTo(1);
- assertThat(psa.getLabel()).isEqualTo("Code-Review");
- assertThat(psa.getAccountId()).isEqualTo(user.id());
- assertThat(psa.getValue()).isEqualTo(1);
- assertThat(psa.getRealAccountId()).isEqualTo(admin.id()); // not user2
+ assertThat(psa.patchSetId().get()).isEqualTo(1);
+ assertThat(psa.label()).isEqualTo("Code-Review");
+ assertThat(psa.accountId()).isEqualTo(user.id());
+ assertThat(psa.value()).isEqualTo(1);
+ assertThat(psa.realAccountId()).isEqualTo(admin.id()); // not user2
ChangeData cd = r.getChange();
ChangeMessage m = Iterables.getLast(cmUtil.byChange(cd.notes()));
@@ -546,54 +574,53 @@ public class ImpersonationIT extends AbstractDaemonTest {
}
private void allowCodeReviewOnBehalfOf() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- LabelType codeReviewType = Util.codeReview();
- String forCodeReviewAs = Permission.forLabelAs(codeReviewType.getName());
- String heads = "refs/heads/*";
- AccountGroup.UUID uuid = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
- Util.allow(u.getConfig(), forCodeReviewAs, -1, 1, uuid, heads);
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel(TestLabels.codeReview().getName())
+ .impersonation(true)
+ .ref("refs/heads/*")
+ .group(REGISTERED_USERS)
+ .range(-1, 1))
+ .update();
}
private void allowSubmitOnBehalfOf() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- String heads = "refs/heads/*";
- AccountGroup.UUID uuid = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
- Util.allow(u.getConfig(), Permission.SUBMIT_AS, uuid, heads);
- Util.allow(u.getConfig(), Permission.SUBMIT, uuid, heads);
- LabelType codeReviewType = Util.codeReview();
- Util.allow(u.getConfig(), Permission.forLabel(codeReviewType.getName()), -2, 2, uuid, heads);
- u.save();
- }
+ String heads = "refs/heads/*";
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT_AS).ref(heads).group(REGISTERED_USERS))
+ .add(allow(Permission.SUBMIT).ref(heads).group(REGISTERED_USERS))
+ .add(
+ allowLabel(TestLabels.codeReview().getName())
+ .ref(heads)
+ .group(REGISTERED_USERS)
+ .range(-2, 2))
+ .update();
}
private void blockRead(GroupInfo group) throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.block(
- u.getConfig(), Permission.READ, new AccountGroup.UUID(group.id), "refs/heads/master");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/master").group(AccountGroup.uuid(group.id)))
+ .update();
}
private void allowRunAs() throws Exception {
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- Util.allow(
- u.getConfig(),
- GlobalCapability.RUN_AS,
- systemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID());
- u.save();
- }
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.RUN_AS).group(ANONYMOUS_USERS))
+ .update();
}
private void removeRunAs() throws Exception {
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- Util.remove(
- u.getConfig(),
- GlobalCapability.RUN_AS,
- systemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID());
- u.save();
- }
+ projectOperations
+ .allProjectsForUpdate()
+ .remove(capabilityKey(GlobalCapability.RUN_AS).group(ANONYMOUS_USERS))
+ .update();
}
private static Header runAsHeader(Object user) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
index d0c1fa4ff0..2c9107c325 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.rest.account;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -114,9 +115,11 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
pwi.notifyNewPatchSets = true;
projectsToWatch.add(pwi);
- exception.expect(BadRequestException.class);
- exception.expectMessage("duplicate entry for project " + projectName);
- gApi.accounts().self().setWatchedProjects(projectsToWatch);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.accounts().self().setWatchedProjects(projectsToWatch));
+ assertThat(thrown).hasMessageThat().contains("duplicate entry for project " + projectName);
}
@Test
@@ -146,9 +149,9 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
pwi.notifyNewChanges = true;
pwi.notifyAllComments = true;
projectsToWatch.add(pwi);
-
- exception.expect(UnprocessableEntityException.class);
- gApi.accounts().self().setWatchedProjects(projectsToWatch);
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.accounts().self().setWatchedProjects(projectsToWatch));
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/auth/AuthenticationCheckIT.java b/javatests/com/google/gerrit/acceptance/rest/auth/AuthenticationCheckIT.java
new file mode 100644
index 0000000000..b6ef5a37a1
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/auth/AuthenticationCheckIT.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.auth;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import org.junit.Test;
+
+public class AuthenticationCheckIT extends AbstractDaemonTest {
+ @Test
+ public void authCheck_loggedInUser_returnsOk() throws Exception {
+ RestResponse r = adminRestSession.get("/auth-check");
+ r.assertNoContent();
+ }
+
+ @Test
+ public void authCheck_anonymousUser_returnsForbidden() throws Exception {
+ RestSession anonymous = new RestSession(server, null);
+ RestResponse r = anonymous.get("/auth-check");
+ r.assertForbidden();
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/auth/BUILD b/javatests/com/google/gerrit/acceptance/rest/auth/BUILD
new file mode 100644
index 0000000000..5de1607107
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/auth/BUILD
@@ -0,0 +1,7 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+
+acceptance_tests(
+ srcs = glob(["*IT.java"]),
+ group = "auth",
+ labels = ["rest"],
+)
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
index 55744ccb8e..8a284d9503 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
@@ -24,6 +24,7 @@ import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.rest.util.RestApiCallHelper;
import com.google.gerrit.acceptance.rest.util.RestCall;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -35,7 +36,6 @@ import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.FixReplacementInfo;
import com.google.gerrit.extensions.common.FixSuggestionInfo;
import com.google.gerrit.extensions.common.RobotCommentInfo;
-import com.google.gerrit.reviewdb.client.Patch;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java
index 5f210cc6d5..00dcb4f6ba 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.rest.binding;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.collect.ImmutableList;
@@ -22,10 +23,12 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.rest.util.RestApiCallHelper;
import com.google.gerrit.acceptance.rest.util.RestCall;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.server.project.ProjectCacheImpl;
import com.google.gerrit.server.restapi.config.ListTasks.TaskInfo;
import com.google.gson.reflect.TypeToken;
+import com.google.inject.Inject;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
@@ -81,10 +84,15 @@ public class ConfigRestApiBindingsIT extends AbstractDaemonTest {
// Task deletion must be tested last
RestCall.delete("/config/server/tasks/%s"));
+ @Inject private ProjectOperations projectOperations;
+
@Test
public void configEndpoints() throws Exception {
// 'Access Database' is needed for the '/config/server/check.consistency' REST endpoint
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
+ .update();
RestApiCallHelper.execute(adminRestSession, CONFIG_ENDPOINTS);
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java
index 27df565c75..22feeb7d7f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java
@@ -21,9 +21,11 @@ import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.rest.util.RestApiCallHelper;
import com.google.gerrit.acceptance.rest.util.RestCall;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
@@ -31,7 +33,6 @@ import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.change.RevisionResource;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
@@ -107,7 +108,8 @@ public class PluginProvidedChildRestApiBindingsIT extends AbstractDaemonTest {
@Override
public RestView<RevisionResource> list() throws RestApiException {
- return (RestReadView<RevisionResource>) resource -> ImmutableList.of("one", "two");
+ return (RestReadView<RevisionResource>)
+ resource -> Response.ok(ImmutableList.of("one", "two"));
}
@Override
@@ -125,24 +127,24 @@ public class PluginProvidedChildRestApiBindingsIT extends AbstractDaemonTest {
static class TestPostOnCollection
implements RestCollectionModifyView<RevisionResource, TestPluginResource, String> {
@Override
- public Object apply(RevisionResource parentResource, String input) throws Exception {
- return "test";
+ public Response<String> apply(RevisionResource parentResource, String input) throws Exception {
+ return Response.ok("test");
}
}
@Singleton
static class TestPost implements RestModifyView<TestPluginResource, String> {
@Override
- public String apply(TestPluginResource resource, String input) throws Exception {
- return "test";
+ public Response<String> apply(TestPluginResource resource, String input) throws Exception {
+ return Response.ok("test");
}
}
@Singleton
static class TestGet implements RestReadView<TestPluginResource> {
@Override
- public String apply(TestPluginResource resource) throws Exception {
- return "test";
+ public Response<String> apply(TestPluginResource resource) throws Exception {
+ return Response.ok("test");
}
}
@@ -153,8 +155,8 @@ public class PluginProvidedChildRestApiBindingsIT extends AbstractDaemonTest {
RestApiCallHelper.execute(
adminRestSession,
TEST_CALLS.asList(),
- String.valueOf(patchSetId.changeId.id),
- String.valueOf(patchSetId.patchSetId));
+ String.valueOf(patchSetId.changeId().get()),
+ String.valueOf(patchSetId.get()));
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java
index 178a32656e..b447534914 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java
@@ -25,6 +25,7 @@ import com.google.gerrit.acceptance.rest.util.RestCall.Method;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.gerrit.extensions.restapi.RestCollectionModifyView;
@@ -157,7 +158,8 @@ public class PluginProvidedRootRestApiBindingsIT extends AbstractDaemonTest {
@Override
public RestView<TopLevelResource> list() throws RestApiException {
- return (RestReadView<TopLevelResource>) resource -> ImmutableList.of("one", "two");
+ return (RestReadView<TopLevelResource>)
+ resource -> Response.ok(ImmutableList.of("one", "two"));
}
@Override
@@ -175,24 +177,24 @@ public class PluginProvidedRootRestApiBindingsIT extends AbstractDaemonTest {
static class TestPostOnCollection
implements RestCollectionModifyView<TopLevelResource, TestPluginResource, String> {
@Override
- public Object apply(TopLevelResource parentResource, String input) throws Exception {
- return "test";
+ public Response<String> apply(TopLevelResource parentResource, String input) throws Exception {
+ return Response.ok("test");
}
}
@Singleton
static class TestPost implements RestModifyView<TestPluginResource, String> {
@Override
- public String apply(TestPluginResource resource, String input) throws Exception {
- return "test";
+ public Response<String> apply(TestPluginResource resource, String input) throws Exception {
+ return Response.ok("test");
}
}
@Singleton
static class TestGet implements RestReadView<TestPluginResource> {
@Override
- public String apply(TestPluginResource resource) throws Exception {
- return "test";
+ public Response<String> apply(TestPluginResource resource) throws Exception {
+ return Response.ok("test");
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
index ed09ddd44c..f48a60372b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
@@ -17,7 +17,8 @@ package com.google.gerrit.acceptance.rest.binding;
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.acceptance.rest.util.RestCall.Method.GET;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.entities.RefNames.REFS_DASHBOARDS;
import static com.google.gerrit.server.restapi.project.DashboardsCollection.DEFAULT_DASHBOARD_NAME;
import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
@@ -29,10 +30,10 @@ import com.google.gerrit.acceptance.rest.util.RestApiCallHelper;
import com.google.gerrit.acceptance.rest.util.RestCall;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.TagInput;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Repository;
@@ -216,7 +217,7 @@ public class ProjectsRestApiBindingsIT extends AbstractDaemonTest {
testRepo
.commit()
.message("A change")
- .parent(getRemoteHead())
+ .parent(projectOperations.project(project).getHead("master"))
.add(filename, "content")
.insertChangeId()
.create();
@@ -234,7 +235,11 @@ public class ProjectsRestApiBindingsIT extends AbstractDaemonTest {
private void createDefaultDashboard() throws Exception {
String dashboardRef = REFS_DASHBOARDS + "team";
- grant(project, "refs/meta/*", Permission.CREATE);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref("refs/meta/*").group(adminGroupUuid()))
+ .update();
gApi.projects().name(project.get()).branch(dashboardRef).create(new BranchInput());
try (Repository r = repoManager.openRepository(project);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index a299b9a0c3..b822750cfe 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -16,15 +16,25 @@ package com.google.gerrit.acceptance.rest.change;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.extensions.client.ListChangesOption.SUBMITTABLE;
import static com.google.gerrit.server.group.SystemGroupBackend.CHANGE_OWNER;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
+import static org.eclipse.jgit.lib.Constants.EMPTY_TREE_ID;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -32,14 +42,27 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
+import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.UseTimezone;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
@@ -52,27 +75,18 @@ import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.LabelInfo;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.extensions.events.ChangeIndexedListener;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.TestSubmitInput;
import com.google.gerrit.server.git.validators.OnSubmitValidationListener;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.restapi.change.Submit;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
@@ -80,7 +94,7 @@ import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.server.validators.ValidationException;
import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.TestTimeUtil;
+import com.google.gerrit.testing.GerritJUnit.ThrowingRunnable;
import com.google.inject.Inject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -103,11 +117,11 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.RefSpec;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
@NoHttpd
+@UseClockStep
+@UseTimezone(timezone = "US/Eastern")
public abstract class AbstractSubmit extends AbstractDaemonTest {
@ConfigSuite.Config
public static Config submitWholeTopicEnabled() {
@@ -115,57 +129,36 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Inject private ApprovalsUtil approvalsUtil;
- @Inject private DynamicSet<OnSubmitValidationListener> onSubmitValidationListeners;
@Inject private IdentifiedUser.GenericFactory userFactory;
@Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Inject private Submit submitHandler;
-
- private RegistrationHandle onSubmitValidatorHandle;
- private String systemTimeZone;
-
- @Before
- public void setTimeForTesting() {
- systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @After
- public void resetTime() {
- TestTimeUtil.useSystemTime();
- System.setProperty("user.timezone", systemTimeZone);
- }
-
- @After
- public void removeOnSubmitValidator() {
- if (onSubmitValidatorHandle != null) {
- onSubmitValidatorHandle.remove();
- }
- }
+ @Inject private ExtensionRegistry extensionRegistry;
protected abstract SubmitType getSubmitType();
@Test
@TestProjectInput(createEmptyCommit = false)
- public void submitToEmptyRepo() throws Exception {
+ public void submitToEmptyRepo() throws Throwable {
assertThat(projectOperations.project(project).hasHead("master")).isFalse();
PushOneCommit.Result change = createChange();
assertThat(change.getCommit().getParents()).isEmpty();
- Map<Branch.NameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
+ Map<BranchNameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
assertThat(projectOperations.project(project).hasHead("master")).isFalse();
assertThat(actual).hasSize(1);
submit(change.getChangeId());
- assertThat(getRemoteHead().getId()).isEqualTo(change.getCommit());
+ assertThat(projectOperations.project(project).getHead("master").getId())
+ .isEqualTo(change.getCommit());
assertTrees(project, actual);
}
@Test
- public void submitSingleChange() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitSingleChange() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
- Map<Branch.NameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
- RevCommit headAfterSubmit = getRemoteHead();
+ Map<BranchNameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterSubmit).isEqualTo(initialHead);
assertRefUpdatedEvents();
assertChangeMergedEvents();
@@ -183,13 +176,13 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitMultipleChangesOtherMergeConflictPreview() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitMultipleChangesOtherMergeConflictPreview() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "other content");
PushOneCommit.Result change3 = createChange("Change 3", "d", "d");
@@ -223,7 +216,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
break;
case REBASE_IF_NECESSARY:
case REBASE_ALWAYS:
- String change2hash = change2.getChange().currentPatchSet().getRevision().get();
+ String change2hash = change2.getChange().currentPatchSet().commitId().name();
assertThat(e.getMessage())
.isEqualTo(
"Cannot rebase "
@@ -255,11 +248,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
break;
case CHERRY_PICK:
default:
- fail("Should not reach here.");
+ assertWithMessage("Should not reach here.").fail();
break;
}
- RevCommit headAfterSubmit = getRemoteHead();
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterSubmit).isEqualTo(headAfterFirstSubmit);
assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
assertChangeMergedEvents(change.getChangeId(), headAfterFirstSubmit.name());
@@ -267,19 +260,19 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitMultipleChangesPreview() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitMultipleChangesPreview() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "other content");
PushOneCommit.Result change3 = createChange("Change 3", "d", "d");
PushOneCommit.Result change4 = createChange("Change 4", "e", "e");
// change 2 is not approved, but we ignore labels
approve(change3.getChangeId());
- Map<Branch.NameKey, ObjectId> actual = fetchFromSubmitPreview(change4.getChangeId());
+ Map<BranchNameKey, ObjectId> actual = fetchFromSubmitPreview(change4.getChangeId());
Map<String, Map<String, Integer>> expected = new HashMap<>();
expected.put(project.get(), new HashMap<>());
expected.get(project.get()).put("refs/heads/master", 3);
- assertThat(actual).containsKey(new Branch.NameKey(project, "refs/heads/master"));
+ assertThat(actual).containsKey(BranchNameKey.create(project, "refs/heads/master"));
if (getSubmitType() == SubmitType.CHERRY_PICK) {
// CherryPick ignores dependencies, thus only change and destination
// branch refs are modified.
@@ -293,7 +286,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
// check that the submit preview did not actually submit
- RevCommit headAfterSubmit = getRemoteHead();
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterSubmit).isEqualTo(initialHead);
assertRefUpdatedEvents();
assertChangeMergedEvents();
@@ -305,10 +298,14 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitNoPermission() throws Exception {
+ public void submitNoPermission() throws Throwable {
// create project where submit is blocked
Project.NameKey p = projectOperations.newProject().create();
- block(p, "refs/*", Permission.SUBMIT, REGISTERED_USERS);
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(block(Permission.SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .update();
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
@@ -319,16 +316,16 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void noSelfSubmit() throws Exception {
+ public void noSelfSubmit() throws Throwable {
// create project where submit is blocked for the change owner
Project.NameKey p = projectOperations.newProject().create();
- try (ProjectConfigUpdate u = updateProject(p)) {
- Util.block(u.getConfig(), Permission.SUBMIT, CHANGE_OWNER, "refs/*");
- Util.allow(u.getConfig(), Permission.SUBMIT, REGISTERED_USERS, "refs/heads/*");
- Util.allow(
- u.getConfig(), Permission.forLabel("Code-Review"), -2, +2, REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(block(Permission.SUBMIT).ref("refs/*").group(CHANGE_OWNER))
+ .add(allow(Permission.SUBMIT).ref("refs/heads/*").group(REGISTERED_USERS))
+ .add(allowLabel("Code-Review").ref("refs/*").group(REGISTERED_USERS).range(-2, +2))
+ .update();
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
@@ -345,16 +342,16 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void onlySelfSubmit() throws Exception {
+ public void onlySelfSubmit() throws Throwable {
// create project where only the change owner can submit
Project.NameKey p = projectOperations.newProject().create();
- try (ProjectConfigUpdate u = updateProject(p)) {
- Util.block(u.getConfig(), Permission.SUBMIT, REGISTERED_USERS, "refs/*");
- Util.allow(u.getConfig(), Permission.SUBMIT, CHANGE_OWNER, "refs/*");
- Util.allow(
- u.getConfig(), Permission.forLabel("Code-Review"), -2, +2, REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(block(Permission.SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.SUBMIT).ref("refs/*").group(CHANGE_OWNER))
+ .add(allowLabel("Code-Review").ref("refs/*").group(REGISTERED_USERS).range(-2, +2))
+ .update();
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
@@ -372,7 +369,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitWholeTopicMultipleProjects() throws Exception {
+ public void submitWholeTopicMultipleProjects() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
String topic = "test-topic";
@@ -408,7 +405,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitWholeTopicMultipleBranchesOnSameProject() throws Exception {
+ public void submitWholeTopicMultipleBranchesOnSameProject() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
String topic = "test-topic";
@@ -416,7 +413,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
Project.NameKey keyA = createProjectForPush(getSubmitType());
TestRepository<?> repoA = cloneProject(keyA);
- RevCommit initialHead = getRemoteHead(keyA, "master");
+ RevCommit initialHead = projectOperations.project(keyA).getHead("master");
// Create the dev branch on the test project
BranchInput in = new BranchInput();
@@ -450,7 +447,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitWholeTopic() throws Exception {
+ public void submitWholeTopic() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
String topic = "test-topic";
PushOneCommit.Result change1 = createChange("Change 1", "a.txt", "content", topic);
@@ -488,7 +485,82 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitReusingOldTopic() throws Exception {
+ public void submitWholeTopicWithMultipleTopics() throws Throwable {
+ assume().that(isSubmitWholeTopicEnabled()).isTrue();
+ String topic1 = "test-topic-1";
+ String topic2 = "test-topic-2";
+ PushOneCommit.Result change1 = createChange("Change 1", "a.txt", "content", topic1);
+ PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "content", topic1);
+ PushOneCommit.Result change3 = createChange("Change 3", "c.txt", "content", topic2);
+ PushOneCommit.Result change4 = createChange("Change 4", "d.txt", "content", topic2);
+ approve(change1.getChangeId());
+ approve(change2.getChangeId());
+ approve(change3.getChangeId());
+ approve(change4.getChangeId());
+ submit(change4.getChangeId());
+ String expectedTopic1 = name(topic1);
+ String expectedTopic2 = name(topic2);
+ if (getSubmitType() == SubmitType.CHERRY_PICK) {
+ change1.assertChange(Change.Status.NEW, expectedTopic1, admin);
+ change2.assertChange(Change.Status.NEW, expectedTopic1, admin);
+
+ } else {
+ change1.assertChange(Change.Status.MERGED, expectedTopic1, admin);
+ change2.assertChange(Change.Status.MERGED, expectedTopic1, admin);
+ }
+
+ // Check for the exact change to have the correct submitter.
+ assertSubmitter(change4);
+ // Also check submitters for changes submitted via the topic relationship.
+ assertSubmitter(change3);
+ if (getSubmitType() != SubmitType.CHERRY_PICK) {
+ assertSubmitter(change1);
+ assertSubmitter(change2);
+ }
+
+ // Check that the repo has the expected commits
+ List<RevCommit> log = getRemoteLog();
+ List<String> commitsInRepo = log.stream().map(RevCommit::getShortMessage).collect(toList());
+ int expectedCommitCount;
+ switch (getSubmitType()) {
+ case MERGE_ALWAYS:
+ // initial commit + 4 commits + merge commit
+ expectedCommitCount = 6;
+ break;
+ case CHERRY_PICK:
+ // initial commit + 2 commits
+ expectedCommitCount = 3;
+ break;
+ case FAST_FORWARD_ONLY:
+ case INHERIT:
+ case MERGE_IF_NECESSARY:
+ case REBASE_ALWAYS:
+ case REBASE_IF_NECESSARY:
+ default:
+ // initial commit + 4 commits
+ expectedCommitCount = 5;
+ break;
+ }
+ assertThat(log).hasSize(expectedCommitCount);
+
+ if (getSubmitType() == SubmitType.CHERRY_PICK) {
+ assertThat(commitsInRepo).containsAtLeast("Initial empty repository", "Change 3", "Change 4");
+ assertThat(commitsInRepo).doesNotContain("Change 1");
+ assertThat(commitsInRepo).doesNotContain("Change 2");
+ } else if (getSubmitType() == SubmitType.MERGE_ALWAYS) {
+ assertThat(commitsInRepo)
+ .contains(
+ String.format(
+ "Merge changes from topics \"%s\", \"%s\"", expectedTopic1, expectedTopic2));
+ } else {
+ assertThat(commitsInRepo)
+ .containsAtLeast(
+ "Initial empty repository", "Change 1", "Change 2", "Change 3", "Change 4");
+ }
+ }
+
+ @Test
+ public void submitReusingOldTopic() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
String topic = "test-topic";
@@ -519,13 +591,13 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
private void assertSubmittedTogether(String changeId, Iterable<String> expected)
- throws Exception {
+ throws Throwable {
assertThat(gApi.changes().id(changeId).submittedTogether().stream().map(i -> i.changeId))
.containsExactlyElementsIn(expected);
}
@Test
- public void submitWorkInProgressChange() throws Exception {
+ public void submitWorkInProgressChange() throws Throwable {
PushOneCommit.Result change = pushTo("refs/for/master%wip");
Change.Id num = change.getChange().getId();
submitWithConflict(
@@ -539,15 +611,19 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitWithHiddenBranchInSameTopic() throws Exception {
+ public void submitWithHiddenBranchInSameTopic() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
PushOneCommit.Result visible = createChange("refs/for/master/" + name("topic"));
Change.Id num = visible.getChange().getId();
- createBranch(new Branch.NameKey(project, "hidden"));
+ createBranch(BranchNameKey.create(project, "hidden"));
PushOneCommit.Result hidden = createChange("refs/for/hidden/" + name("topic"));
approve(hidden.getChangeId());
- blockRead("refs/heads/hidden");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/hidden").group(REGISTERED_USERS))
+ .update();
submit(
visible.getChangeId(),
@@ -557,7 +633,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitChangeWhenParentOfOtherBranchTip() throws Exception {
+ public void submitChangeWhenParentOfOtherBranchTip() throws Throwable {
// Chain of two commits
// Push both to topic-branch
// Push the first commit for review and submit
@@ -588,7 +664,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitMergeOfNonChangeBranchTip() throws Exception {
+ public void submitMergeOfNonChangeBranchTip() throws Throwable {
// Merge a branch with commits that have not been submitted as
// changes.
//
@@ -598,7 +674,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// | /
// I -- master
//
- RevCommit master = getRemoteHead(project, "master");
+ RevCommit master = projectOperations.project(project).getHead("master");
PushOneCommit stableTip =
pushFactory.create(admin.newIdent(), testRepo, "Tip of branch stable", "stable.txt", "");
PushOneCommit.Result stable = stableTip.to("refs/heads/stable");
@@ -615,7 +691,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitMergeOfNonChangeBranchNonTip() throws Exception {
+ public void submitMergeOfNonChangeBranchNonTip() throws Throwable {
// Merge a branch with commits that have not been submitted as
// changes.
//
@@ -626,7 +702,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// | /
// I -- master
//
- RevCommit initial = getRemoteHead(project, "master");
+ RevCommit initial = projectOperations.project(project).getHead("master");
// push directly to stable to S1
PushOneCommit.Result s1 =
pushFactory
@@ -659,11 +735,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void submitChangeWithCommitThatWasAlreadyMerged() throws Exception {
+ public void submitChangeWithCommitThatWasAlreadyMerged() throws Throwable {
// create and submit a change
PushOneCommit.Result change = createChange();
submit(change.getChangeId());
- RevCommit headAfterSubmit = getRemoteHead();
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
// set the status of the change back to NEW to simulate a failed submit that
// merged the commit but failed to update the change status
@@ -672,11 +748,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// submitting the change again should detect that the commit was already
// merged and just fix the change status to be MERGED
submit(change.getChangeId());
- assertThat(getRemoteHead()).isEqualTo(headAfterSubmit);
+ assertThat(projectOperations.project(project).getHead("master")).isEqualTo(headAfterSubmit);
}
@Test
- public void submitChangesWithCommitsThatWereAlreadyMerged() throws Exception {
+ public void submitChangesWithCommitsThatWereAlreadyMerged() throws Throwable {
// create and submit 2 changes
PushOneCommit.Result change1 = createChange();
PushOneCommit.Result change2 = createChange();
@@ -686,7 +762,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
submit(change2.getChangeId());
assertMerged(change1.getChangeId());
- RevCommit headAfterSubmit = getRemoteHead();
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
// set the status of the changes back to NEW to simulate a failed submit that
// merged the commits but failed to update the change status
@@ -696,11 +772,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// merged and just fix the change status to be MERGED
submit(change1.getChangeId());
submit(change2.getChangeId());
- assertThat(getRemoteHead()).isEqualTo(headAfterSubmit);
+ assertThat(projectOperations.project(project).getHead("master")).isEqualTo(headAfterSubmit);
}
@Test
- public void submitTopicWithCommitsThatWereAlreadyMerged() throws Exception {
+ public void submitTopicWithCommitsThatWereAlreadyMerged() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
// create and submit 2 changes with the same topic
@@ -710,7 +786,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
approve(change1.getChangeId());
submit(change2.getChangeId());
assertMerged(change1.getChangeId());
- RevCommit headAfterSubmit = getRemoteHead();
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
// set the status of the second change back to NEW to simulate a failed
// submit that merged the commits but failed to update the change status of
@@ -720,33 +796,38 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// submitting the topic again should detect that the commits were already
// merged and just fix the change status to be MERGED
submit(change2.getChangeId());
- assertThat(getRemoteHead()).isEqualTo(headAfterSubmit);
+ assertThat(projectOperations.project(project).getHead("master")).isEqualTo(headAfterSubmit);
}
@Test
- public void submitWithValidation() throws Exception {
+ public void submitWithValidation() throws Throwable {
AtomicBoolean called = new AtomicBoolean(false);
- this.addOnSubmitValidationListener(
- args -> {
- called.set(true);
- HashSet<String> refs = Sets.newHashSet(args.getCommands().keySet());
- assertThat(refs).contains("refs/heads/master");
- refs.remove("refs/heads/master");
- if (!refs.isEmpty()) {
- // Some submit strategies need to insert new patchset.
- assertThat(refs).hasSize(1);
- assertThat(refs.iterator().next()).startsWith(RefNames.REFS_CHANGES);
+ OnSubmitValidationListener listener =
+ new OnSubmitValidationListener() {
+ @Override
+ public void preBranchUpdate(Arguments args) throws ValidationException {
+ called.set(true);
+ HashSet<String> refs = Sets.newHashSet(args.getCommands().keySet());
+ assertThat(refs).contains("refs/heads/master");
+ refs.remove("refs/heads/master");
+ if (!refs.isEmpty()) {
+ // Some submit strategies need to insert new patchset.
+ assertThat(refs).hasSize(1);
+ assertThat(refs.iterator().next()).startsWith(RefNames.REFS_CHANGES);
+ }
}
- });
+ };
- PushOneCommit.Result change = createChange();
- approve(change.getChangeId());
- submit(change.getChangeId());
- assertThat(called.get()).isTrue();
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
+ PushOneCommit.Result change = createChange();
+ approve(change.getChangeId());
+ submit(change.getChangeId());
+ assertThat(called.get()).isTrue();
+ }
}
@Test
- public void submitWithValidationMultiRepo() throws Exception {
+ public void submitWithValidationMultiRepo() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
String topic = "test-topic";
@@ -777,42 +858,47 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// Since there are 2 repos, first submit attempt will fail, the second will
// succeed.
List<String> projectsCalled = new ArrayList<>(4);
- this.addOnSubmitValidationListener(
- args -> {
- String master = "refs/heads/master";
- assertThat(args.getCommands()).containsKey(master);
- ReceiveCommand cmd = args.getCommands().get(master);
- ObjectId newMasterId = cmd.getNewId();
- try (Repository repo = repoManager.openRepository(args.getProject())) {
- assertThat(repo.exactRef(master).getObjectId()).isEqualTo(cmd.getOldId());
- assertThat(args.getRef(master)).hasValue(newMasterId);
- args.getRevWalk().parseBody(args.getRevWalk().parseCommit(newMasterId));
- } catch (IOException e) {
- throw new AssertionError("failed checking new ref value", e);
+ OnSubmitValidationListener listener =
+ new OnSubmitValidationListener() {
+ @Override
+ public void preBranchUpdate(Arguments args) throws ValidationException {
+ String master = "refs/heads/master";
+ assertThat(args.getCommands()).containsKey(master);
+ ReceiveCommand cmd = args.getCommands().get(master);
+ ObjectId newMasterId = cmd.getNewId();
+ try (Repository repo = repoManager.openRepository(args.getProject())) {
+ assertThat(repo.exactRef(master).getObjectId()).isEqualTo(cmd.getOldId());
+ assertThat(args.getRef(master)).hasValue(newMasterId);
+ args.getRevWalk().parseBody(args.getRevWalk().parseCommit(newMasterId));
+ } catch (IOException e) {
+ throw new AssertionError("failed checking new ref value", e);
+ }
+ projectsCalled.add(args.getProject().get());
+ if (projectsCalled.size() == 2) {
+ throw new ValidationException("time to fail");
+ }
}
- projectsCalled.add(args.getProject().get());
- if (projectsCalled.size() == 2) {
- throw new ValidationException("time to fail");
- }
- });
- submitWithConflict(change4.getChangeId(), "time to fail");
- assertThat(projectsCalled).containsExactly(keyA.get(), keyB.get());
- for (PushOneCommit.Result change : changes) {
- change.assertChange(Change.Status.NEW, name(topic), admin);
- }
+ };
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
+ submitWithConflict(change4.getChangeId(), "time to fail");
+ assertThat(projectsCalled).containsExactly(keyA.get(), keyB.get());
+ for (PushOneCommit.Result change : changes) {
+ change.assertChange(Change.Status.NEW, name(topic), admin);
+ }
- submit(change4.getChangeId());
- assertThat(projectsCalled).containsExactly(keyA.get(), keyB.get(), keyA.get(), keyB.get());
- for (PushOneCommit.Result change : changes) {
- change.assertChange(Change.Status.MERGED, name(topic), admin);
+ submit(change4.getChangeId());
+ assertThat(projectsCalled).containsExactly(keyA.get(), keyB.get(), keyA.get(), keyB.get());
+ for (PushOneCommit.Result change : changes) {
+ change.assertChange(Change.Status.MERGED, name(topic), admin);
+ }
}
}
@Test
- public void submitWithCommitAndItsMergeCommitTogether() throws Exception {
+ public void submitWithCommitAndItsMergeCommitTogether() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
// Create a stable branch and bootstrap it.
gApi.projects().name(project.get()).branch("stable").create(new BranchInput());
@@ -820,8 +906,8 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
pushFactory.create(user.newIdent(), testRepo, "initial commit", "a.txt", "a");
PushOneCommit.Result change = push.to("refs/heads/stable");
- RevCommit stable = getRemoteHead(project, "stable");
- RevCommit master = getRemoteHead(project, "master");
+ RevCommit stable = projectOperations.project(project).getHead("stable");
+ RevCommit master = projectOperations.project(project).getHead("master");
assertThat(master).isEqualTo(initialHead);
assertThat(stable).isEqualTo(change.getCommit());
@@ -874,13 +960,13 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
assertMerged(mergeId);
testRepo.git().fetch().call();
RevWalk rw = testRepo.getRevWalk();
- master = rw.parseCommit(getRemoteHead(project, "master"));
+ master = rw.parseCommit(projectOperations.project(project).getHead("master"));
assertThat(rw.isMergedInto(merge, master)).isTrue();
assertThat(rw.isMergedInto(fix, master)).isTrue();
}
@Test
- public void retrySubmitSingleChangeOnLockFailure() throws Exception {
+ public void retrySubmitSingleChangeOnLockFailure() throws Throwable {
PushOneCommit.Result change = createChange();
String id = change.getChangeId();
approve(id);
@@ -897,7 +983,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
testRepo.git().fetch().call();
RevWalk rw = testRepo.getRevWalk();
- RevCommit master = rw.parseCommit(getRemoteHead(project, "master"));
+ RevCommit master = rw.parseCommit(projectOperations.project(project).getHead("master"));
RevCommit patchSet = parseCurrentRevision(rw, change.getChangeId());
assertThat(rw.isMergedInto(patchSet, master)).isTrue();
@@ -905,7 +991,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void retrySubmitAfterTornTopicOnLockFailure() throws Exception {
+ public void retrySubmitAfterTornTopicOnLockFailure() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
String topic = "test-topic";
@@ -940,13 +1026,13 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
repoA.git().fetch().call();
RevWalk rwA = repoA.getRevWalk();
- RevCommit masterA = rwA.parseCommit(getRemoteHead(keyA, "master"));
+ RevCommit masterA = rwA.parseCommit(projectOperations.project(keyA).getHead("master"));
RevCommit change1Ps = parseCurrentRevision(rwA, change1.getChangeId());
assertThat(rwA.isMergedInto(change1Ps, masterA)).isTrue();
repoB.git().fetch().call();
RevWalk rwB = repoB.getRevWalk();
- RevCommit masterB = rwB.parseCommit(getRemoteHead(keyB, "master"));
+ RevCommit masterB = rwB.parseCommit(projectOperations.project(keyB).getHead("master"));
RevCommit change2Ps = parseCurrentRevision(rwB, change2.getChangeId());
assertThat(rwB.isMergedInto(change2Ps, masterB)).isTrue();
@@ -954,14 +1040,14 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Test
- public void authorAndCommitDateAreEqual() throws Exception {
+ public void authorAndCommitDateAreEqual() throws Throwable {
assume().that(getSubmitType()).isNotEqualTo(SubmitType.FAST_FORWARD_ONLY);
ConfigInput ci = new ConfigInput();
ci.matchAuthorToCommitterDate = InheritableBoolean.TRUE;
gApi.projects().name(project.get()).config(ci);
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change = createChange("Change 1", "b", "b");
@@ -975,12 +1061,12 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
submit(change2.getChangeId());
- assertAuthorAndCommitDateEquals(getRemoteHead());
+ assertAuthorAndCommitDateEquals(projectOperations.project(project).getHead("master"));
}
@Test
@TestProjectInput(rejectEmptyCommit = InheritableBoolean.FALSE)
- public void submitEmptyCommitPatchSetCanNotFastForward_emptyCommitAllowed() throws Exception {
+ public void submitEmptyCommitPatchSetCanNotFastForward_emptyCommitAllowed() throws Throwable {
assume().that(getSubmitType()).isNotEqualTo(SubmitType.FAST_FORWARD_ONLY);
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
@@ -997,7 +1083,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
@TestProjectInput(rejectEmptyCommit = InheritableBoolean.TRUE)
- public void submitEmptyCommitPatchSetCanNotFastForward_emptyCommitNotAllowed() throws Exception {
+ public void submitEmptyCommitPatchSetCanNotFastForward_emptyCommitNotAllowed() throws Throwable {
assume().that(getSubmitType()).isNotEqualTo(SubmitType.FAST_FORWARD_ONLY);
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
@@ -1010,18 +1096,20 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
ChangeApi revert2 = gApi.changes().id(change.getChangeId()).revert();
approve(revert2.id());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- "Change "
- + revert2.get()._number
- + ": Change could not be merged because the commit is empty. "
- + "Project policy requires all commits to contain modifications to at least one file.");
- revert2.current().submit();
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> revert2.current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "Change "
+ + revert2.get()._number
+ + ": Change could not be merged because the commit is empty. Project policy"
+ + " requires all commits to contain modifications to at least one file.");
}
@Test
@TestProjectInput(rejectEmptyCommit = InheritableBoolean.FALSE)
- public void submitEmptyCommitPatchSetCanFastForward_emptyCommitAllowed() throws Exception {
+ public void submitEmptyCommitPatchSetCanFastForward_emptyCommitAllowed() throws Throwable {
ChangeInput ci = new ChangeInput();
ci.subject = "Empty change";
ci.project = project.get();
@@ -1033,7 +1121,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
@TestProjectInput(rejectEmptyCommit = InheritableBoolean.TRUE)
- public void submitEmptyCommitPatchSetCanFastForward_emptyCommitNotAllowed() throws Exception {
+ public void submitEmptyCommitPatchSetCanFastForward_emptyCommitNotAllowed() throws Throwable {
ChangeInput ci = new ChangeInput();
ci.subject = "Empty change";
ci.project = project.get();
@@ -1041,53 +1129,55 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
ChangeApi change = gApi.changes().create(ci);
approve(change.id());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- "Change "
- + change.get()._number
- + ": Change could not be merged because the commit is empty. "
- + "Project policy requires all commits to contain modifications to at least one file.");
- change.current().submit();
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> change.current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "Change "
+ + change.get()._number
+ + ": Change could not be merged because the commit is empty. Project policy"
+ + " requires all commits to contain modifications to at least one file.");
}
@Test
@TestProjectInput(createEmptyCommit = false, rejectEmptyCommit = InheritableBoolean.TRUE)
- public void submitNonemptyCommitToEmptyRepoWithRejectEmptyCommit_allowed() throws Exception {
+ public void submitNonemptyCommitToEmptyRepoWithRejectEmptyCommit_allowed() throws Throwable {
assertThat(projectOperations.project(project).hasHead("master")).isFalse();
PushOneCommit.Result change = createChange();
assertThat(change.getCommit().getParents()).isEmpty();
- Map<Branch.NameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
+ Map<BranchNameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
assertThat(projectOperations.project(project).hasHead("master")).isFalse();
assertThat(actual).hasSize(1);
submit(change.getChangeId());
- assertThat(getRemoteHead().getId()).isEqualTo(change.getCommit());
+ assertThat(projectOperations.project(project).getHead("master").getId())
+ .isEqualTo(change.getCommit());
assertTrees(project, actual);
}
@Test
@TestProjectInput(createEmptyCommit = false, rejectEmptyCommit = InheritableBoolean.TRUE)
- public void submitEmptyCommitToEmptyRepoWithRejectEmptyCommit_allowed() throws Exception {
+ public void submitEmptyCommitToEmptyRepoWithRejectEmptyCommit_allowed() throws Throwable {
assertThat(projectOperations.project(project).hasHead("master")).isFalse();
PushOneCommit.Result change =
pushFactory
.create(admin.newIdent(), testRepo, "Change 1", ImmutableMap.of())
.to("refs/for/master");
change.assertOkStatus();
- // TODO(dborowitz): Use EMPTY_TREE_ID after upgrading to https://git.eclipse.org/r/127473
- assertThat(change.getCommit().getTree())
- .isEqualTo(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"));
+ assertThat(change.getCommit().getTree()).isEqualTo(EMPTY_TREE_ID);
- Map<Branch.NameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
+ Map<BranchNameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
assertThat(projectOperations.project(project).hasHead("master")).isFalse();
assertThat(actual).hasSize(1);
submit(change.getChangeId());
- assertThat(getRemoteHead().getId()).isEqualTo(change.getCommit());
+ assertThat(projectOperations.project(project).getHead("master").getId())
+ .isEqualTo(change.getCommit());
assertTrees(project, actual);
}
- private void setChangeStatusToNew(PushOneCommit.Result... changes) throws Exception {
+ private void setChangeStatusToNew(PushOneCommit.Result... changes) throws Throwable {
for (PushOneCommit.Result change : changes) {
try (BatchUpdate bu =
batchUpdateFactory.create(project, userFactory.create(admin.id()), TimeUtil.nowTs())) {
@@ -1106,7 +1196,61 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
}
- private void assertSubmitter(PushOneCommit.Result change) throws Exception {
+ @Test
+ @GerritConfig(name = "index.reindexAfterRefUpdate", value = "true")
+ public void submitSchedulesOpenChangesOfSameBranchForReindexing() throws Throwable {
+ // Create a merged change.
+ PushOneCommit push =
+ pushFactory.create(admin.newIdent(), testRepo, "Merged Change", "foo.txt", "foo");
+ PushOneCommit.Result mergedChange = push.to("refs/for/master");
+ mergedChange.assertOkStatus();
+ approve(mergedChange.getChangeId());
+ submit(mergedChange.getChangeId());
+
+ // Create some open changes.
+ PushOneCommit.Result change1 = createChange();
+ PushOneCommit.Result change2 = createChange();
+ PushOneCommit.Result change3 = createChange();
+
+ // Create a branch with one open change.
+ BranchInput in = new BranchInput();
+ in.revision = projectOperations.project(project).getHead("master").name();
+ gApi.projects().name(project.get()).branch("dev").create(in);
+ PushOneCommit.Result changeOtherBranch = createChange("refs/for/dev");
+
+ ChangeIndexedListener changeIndexedListener = mock(ChangeIndexedListener.class);
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedListener)) {
+ // submit a change, this should trigger asynchronous reindexing of the open changes on the
+ // same branch
+ approve(change1.getChangeId());
+ submit(change1.getChangeId());
+ assertThat(gApi.changes().id(change1.getChangeId()).get().status)
+ .isEqualTo(ChangeStatus.MERGED);
+
+ // on submit the change that is submitted gets reindexed synchronously
+ verify(changeIndexedListener, atLeast(1))
+ .onChangeScheduledForIndexing(project.get(), change1.getChange().getId().get());
+ verify(changeIndexedListener, atLeast(1))
+ .onChangeIndexed(project.get(), change1.getChange().getId().get());
+
+ // the open changes on the same branch get reindexed asynchronously
+ verify(changeIndexedListener, times(1))
+ .onChangeScheduledForIndexing(project.get(), change2.getChange().getId().get());
+ verify(changeIndexedListener, times(1))
+ .onChangeScheduledForIndexing(project.get(), change3.getChange().getId().get());
+
+ // merged changes don't get reindexed
+ verify(changeIndexedListener, times(0))
+ .onChangeScheduledForIndexing(project.get(), mergedChange.getChange().getId().get());
+
+ // open changes on other branches don't get reindexed
+ verify(changeIndexedListener, times(0))
+ .onChangeScheduledForIndexing(project.get(), changeOtherBranch.getChange().getId().get());
+ }
+ }
+
+ private void assertSubmitter(PushOneCommit.Result change) throws Throwable {
ChangeInfo info = get(change.getChangeId(), ListChangesOption.MESSAGES);
assertThat(info.messages).isNotNull();
Iterable<String> messages = Iterables.transform(info.messages, i -> i.message);
@@ -1129,102 +1273,86 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
}
- protected void submit(String changeId) throws Exception {
+ protected void submit(String changeId) throws Throwable {
submit(changeId, new SubmitInput(), null, null);
}
- protected void submit(String changeId, SubmitInput input) throws Exception {
+ protected void submit(String changeId, SubmitInput input) throws Throwable {
submit(changeId, input, null, null);
}
- protected void submitWithConflict(String changeId, String expectedError) throws Exception {
+ protected void submitWithConflict(String changeId, String expectedError) throws Throwable {
submit(changeId, new SubmitInput(), ResourceConflictException.class, expectedError);
}
protected void submit(
String changeId,
SubmitInput input,
- Class<? extends RestApiException> expectedExceptionType,
+ @Nullable Class<? extends RestApiException> expectedExceptionType,
String expectedExceptionMsg)
- throws Exception {
+ throws Throwable {
approve(changeId);
if (expectedExceptionType == null) {
assertSubmittable(changeId);
+ } else {
+ requireNonNull(expectedExceptionMsg);
}
- try {
- gApi.changes().id(changeId).current().submit(input);
- if (expectedExceptionType != null) {
- fail("Expected exception of type " + expectedExceptionType.getSimpleName());
- }
- } catch (RestApiException e) {
- if (expectedExceptionType == null) {
- throw e;
- }
- // More verbose than using assertThat and/or ExpectedException, but gives
- // us the stack trace.
- if (!expectedExceptionType.isAssignableFrom(e.getClass())
- || !e.getMessage().equals(expectedExceptionMsg)) {
- throw new AssertionError(
- "Expected exception of type "
- + expectedExceptionType.getSimpleName()
- + " with message: \""
- + expectedExceptionMsg
- + "\" but got exception of type "
- + e.getClass().getSimpleName()
- + " with message \""
- + e.getMessage()
- + "\"",
- e);
- }
+ ThrowingRunnable submit = () -> gApi.changes().id(changeId).current().submit(input);
+ if (expectedExceptionType != null) {
+ RestApiException thrown = assertThrows(expectedExceptionType, submit);
+ assertThat(thrown).hasMessageThat().isEqualTo(expectedExceptionMsg);
return;
}
+ submit.run();
ChangeInfo change = gApi.changes().id(changeId).info();
assertMerged(change.changeId);
}
- protected void assertSubmittable(String changeId) throws Exception {
- assertThat(get(changeId, SUBMITTABLE).submittable).named("submit bit on ChangeInfo").isTrue();
+ protected void assertSubmittable(String changeId) throws Throwable {
+ assertWithMessage("submit bit on ChangeInfo")
+ .that(get(changeId, SUBMITTABLE).submittable)
+ .isTrue();
RevisionResource rsrc = parseCurrentRevisionResource(changeId);
UiAction.Description desc = submitHandler.getDescription(rsrc);
- assertThat(desc.isVisible()).named("visible bit on submit action").isTrue();
- assertThat(desc.isEnabled()).named("enabled bit on submit action").isTrue();
+ assertWithMessage("visible bit on submit action").that(desc.isVisible()).isTrue();
+ assertWithMessage("enabled bit on submit action").that(desc.isEnabled()).isTrue();
}
- protected void assertChangeMergedEvents(String... expected) throws Exception {
+ protected void assertChangeMergedEvents(String... expected) throws Throwable {
eventRecorder.assertChangeMergedEvents(project.get(), "refs/heads/master", expected);
}
- protected void assertRefUpdatedEvents(RevCommit... expected) throws Exception {
+ protected void assertRefUpdatedEvents(RevCommit... expected) throws Throwable {
eventRecorder.assertRefUpdatedEvents(project.get(), "refs/heads/master", expected);
}
protected void assertCurrentRevision(String changeId, int expectedNum, ObjectId expectedId)
- throws Exception {
+ throws Throwable {
ChangeInfo c = get(changeId, CURRENT_REVISION);
assertThat(c.currentRevision).isEqualTo(expectedId.name());
assertThat(c.revisions.get(expectedId.name())._number).isEqualTo(expectedNum);
- try (Repository repo = repoManager.openRepository(new Project.NameKey(c.project))) {
- String refName = new PatchSet.Id(new Change.Id(c._number), expectedNum).toRefName();
+ try (Repository repo = repoManager.openRepository(Project.nameKey(c.project))) {
+ String refName = PatchSet.id(Change.id(c._number), expectedNum).toRefName();
Ref ref = repo.exactRef(refName);
- assertThat(ref).named(refName).isNotNull();
+ assertWithMessage(refName).that(ref).isNotNull();
assertThat(ref.getObjectId()).isEqualTo(expectedId);
}
}
- protected void assertNew(String changeId) throws Exception {
+ protected void assertNew(String changeId) throws Throwable {
assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
}
- protected void assertApproved(String changeId) throws Exception {
+ protected void assertApproved(String changeId) throws Throwable {
assertApproved(changeId, admin);
}
- protected void assertApproved(String changeId, TestAccount user) throws Exception {
+ protected void assertApproved(String changeId, TestAccount user) throws Throwable {
ChangeInfo c = get(changeId, DETAILED_LABELS);
LabelInfo cr = c.labels.get("Code-Review");
assertThat(cr.all).hasSize(1);
assertThat(cr.all.get(0).value).isEqualTo(2);
- assertThat(new Account.Id(cr.all.get(0)._accountId)).isEqualTo(user.id());
+ assertThat(Account.id(cr.all.get(0)._accountId)).isEqualTo(user.id());
}
protected void assertMerged(String changeId) throws RestApiException {
@@ -1244,40 +1372,40 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
.isEqualTo(commit.getCommitterIdent().getTimeZone());
}
- protected void assertSubmitter(String changeId, int psId) throws Exception {
+ protected void assertSubmitter(String changeId, int psId) throws Throwable {
assertSubmitter(changeId, psId, admin);
}
- protected void assertSubmitter(String changeId, int psId, TestAccount user) throws Exception {
+ protected void assertSubmitter(String changeId, int psId, TestAccount user) throws Throwable {
Change c = getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change();
ChangeNotes cn = notesFactory.createChecked(c);
PatchSetApproval submitter =
- approvalsUtil.getSubmitter(cn, new PatchSet.Id(cn.getChangeId(), psId));
+ approvalsUtil.getSubmitter(cn, PatchSet.id(cn.getChangeId(), psId));
assertThat(submitter).isNotNull();
assertThat(submitter.isLegacySubmit()).isTrue();
- assertThat(submitter.getAccountId()).isEqualTo(user.id());
+ assertThat(submitter.accountId()).isEqualTo(user.id());
}
- protected void assertNoSubmitter(String changeId, int psId) throws Exception {
+ protected void assertNoSubmitter(String changeId, int psId) throws Throwable {
Change c = getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change();
ChangeNotes cn = notesFactory.createChecked(c);
PatchSetApproval submitter =
- approvalsUtil.getSubmitter(cn, new PatchSet.Id(cn.getChangeId(), psId));
+ approvalsUtil.getSubmitter(cn, PatchSet.id(cn.getChangeId(), psId));
assertThat(submitter).isNull();
}
protected void assertCherryPick(TestRepository<?> testRepo, boolean contentMerge)
- throws Exception {
+ throws Throwable {
assertRebase(testRepo, contentMerge);
- RevCommit remoteHead = getRemoteHead();
+ RevCommit remoteHead = projectOperations.project(project).getHead("master");
assertThat(remoteHead.getFooterLines("Reviewed-On")).isNotEmpty();
assertThat(remoteHead.getFooterLines("Reviewed-By")).isNotEmpty();
}
- protected void assertRebase(TestRepository<?> testRepo, boolean contentMerge) throws Exception {
+ protected void assertRebase(TestRepository<?> testRepo, boolean contentMerge) throws Throwable {
Repository repo = testRepo.getRepository();
RevCommit localHead = getHead(repo, "HEAD");
- RevCommit remoteHead = getRemoteHead();
+ RevCommit remoteHead = projectOperations.project(project).getHead("master");
assertThat(localHead.getId()).isNotEqualTo(remoteHead.getId());
assertThat(remoteHead.getParentCount()).isEqualTo(1);
if (!contentMerge) {
@@ -1286,7 +1414,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
assertThat(remoteHead.getShortMessage()).isEqualTo(localHead.getShortMessage());
}
- protected List<RevCommit> getRemoteLog(Project.NameKey project, String branch) throws Exception {
+ protected List<RevCommit> getRemoteLog(Project.NameKey project, String branch) throws Throwable {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
rw.markStart(rw.parseCommit(repo.exactRef("refs/heads/" + branch).getObjectId()));
@@ -1294,22 +1422,17 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
}
- protected List<RevCommit> getRemoteLog() throws Exception {
+ protected List<RevCommit> getRemoteLog() throws Throwable {
return getRemoteLog(project, "master");
}
- protected void addOnSubmitValidationListener(OnSubmitValidationListener listener) {
- assertThat(onSubmitValidatorHandle).isNull();
- onSubmitValidatorHandle = onSubmitValidationListeners.add("gerrit", listener);
- }
-
- private String getLatestDiff(Repository repo) throws Exception {
+ private String getLatestDiff(Repository repo) throws Throwable {
ObjectId oldTreeId = repo.resolve("HEAD~1^{tree}");
ObjectId newTreeId = repo.resolve("HEAD^{tree}");
return getLatestDiff(repo, oldTreeId, newTreeId);
}
- private String getLatestRemoteDiff() throws Exception {
+ private String getLatestRemoteDiff() throws Throwable {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
ObjectId oldTreeId = repo.resolve("refs/heads/master~1^{tree}");
@@ -1319,7 +1442,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
private String getLatestDiff(Repository repo, ObjectId oldTreeId, ObjectId newTreeId)
- throws Exception {
+ throws Throwable {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (DiffFormatter fmt = new DiffFormatter(out)) {
fmt.setRepository(repo);
@@ -1330,15 +1453,19 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
// TODO(hanwen): the submodule tests have a similar method; maybe we could share code?
- protected Project.NameKey createProjectForPush(SubmitType submitType) throws Exception {
+ protected Project.NameKey createProjectForPush(SubmitType submitType) throws Throwable {
Project.NameKey project = projectOperations.newProject().submitType(submitType).create();
- grant(project, "refs/heads/*", Permission.PUSH);
- grant(project, "refs/for/refs/heads/*", Permission.SUBMIT);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/*").group(adminGroupUuid()))
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/*").group(adminGroupUuid()))
+ .update();
return project;
}
protected PushOneCommit.Result createChange(
- String subject, String fileName, String content, String topic) throws Exception {
+ String subject, String fileName, String content, String topic) throws Throwable {
PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
return push.to("refs/for/master/" + name(topic));
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
index 36a09fd402..a4fa84b11c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
@@ -19,23 +19,26 @@ import static com.google.common.truth.TruthJUnit.assume;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.extensions.client.InheritableBoolean;
+import com.google.inject.Inject;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public abstract class AbstractSubmitByMerge extends AbstractSubmit {
+ @Inject private ProjectOperations projectOperations;
@Test
- public void submitWithMerge() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithMerge() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit oldHead = getRemoteHead();
+ RevCommit oldHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
submit(change2.getChangeId());
- RevCommit head = getRemoteHead();
+ RevCommit head = projectOperations.project(project).getHead("master");
assertThat(head.getParentCount()).isEqualTo(2);
assertThat(head.getParent(0)).isEqualTo(oldHead);
assertThat(head.getParent(1)).isEqualTo(change2.getCommit());
@@ -43,17 +46,17 @@ public abstract class AbstractSubmitByMerge extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithContentMerge() throws Exception {
+ public void submitWithContentMerge() throws Throwable {
PushOneCommit.Result change = createChange("Change 1", "a.txt", "aaa\nbbb\nccc\n");
submit(change.getChangeId());
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n");
submit(change2.getChangeId());
- RevCommit oldHead = getRemoteHead();
+ RevCommit oldHead = projectOperations.project(project).getHead("master");
testRepo.reset(change.getCommit());
PushOneCommit.Result change3 = createChange("Change 3", "a.txt", "bbb\nccc\n");
submit(change3.getChangeId());
- RevCommit head = getRemoteHead();
+ RevCommit head = projectOperations.project(project).getHead("master");
assertThat(head.getParentCount()).isEqualTo(2);
assertThat(head.getParent(0)).isEqualTo(oldHead);
assertThat(head.getParent(1)).isEqualTo(change3.getCommit());
@@ -61,12 +64,12 @@ public abstract class AbstractSubmitByMerge extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithContentMerge_Conflict() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithContentMerge_Conflict() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit oldHead = getRemoteHead();
+ RevCommit oldHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "other content");
submitWithConflict(
@@ -78,22 +81,23 @@ public abstract class AbstractSubmitByMerge extends AbstractSubmit {
+ "Change could not be merged due to a path conflict. "
+ "Please rebase the change locally "
+ "and upload the rebased commit for review.");
- assertThat(getRemoteHead()).isEqualTo(oldHead);
+ assertThat(projectOperations.project(project).getHead("master")).isEqualTo(oldHead);
}
@Test
@TestProjectInput(createEmptyCommit = false)
- public void submitMultipleCommitsToEmptyRepoAsFastForward() throws Exception {
+ public void submitMultipleCommitsToEmptyRepoAsFastForward() throws Throwable {
PushOneCommit.Result change1 = createChange();
PushOneCommit.Result change2 = createChange();
approve(change1.getChangeId());
submit(change2.getChangeId());
- assertThat(getRemoteHead().getId()).isEqualTo(change2.getCommit());
+ assertThat(projectOperations.project(project).getHead("master").getId())
+ .isEqualTo(change2.getCommit());
}
@Test
@TestProjectInput(createEmptyCommit = false)
- public void submitMultipleCommitsToEmptyRepoWithOneMerge() throws Exception {
+ public void submitMultipleCommitsToEmptyRepoWithOneMerge() throws Throwable {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
PushOneCommit.Result change1 =
pushFactory
@@ -108,7 +112,7 @@ public abstract class AbstractSubmitByMerge extends AbstractSubmit {
approve(change1.getChangeId());
submit(change2.getChangeId());
- RevCommit head = getRemoteHead();
+ RevCommit head = projectOperations.project(project).getHead("master");
assertThat(head.getParents()).hasLength(2);
assertThat(head.getParent(0)).isEqualTo(change1.getCommit());
assertThat(head.getParent(1)).isEqualTo(change2.getCommit());
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
index c12adfaf7f..fff67f3de1 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
@@ -17,21 +17,25 @@ package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.getChangeId;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@@ -40,6 +44,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Test;
public abstract class AbstractSubmitByRebase extends AbstractSubmit {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Override
@@ -47,42 +52,41 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithRebase() throws Exception {
+ public void submitWithRebase() throws Throwable {
submitWithRebase(admin);
}
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithRebaseWithoutAddPatchSetPermission() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.block(u.getConfig(), Permission.ADD_PATCH_SET, REGISTERED_USERS, "refs/*");
- Util.allow(u.getConfig(), Permission.SUBMIT, REGISTERED_USERS, "refs/heads/*");
- Util.allow(
- u.getConfig(),
- Permission.forLabel(Util.codeReview().getName()),
- -2,
- 2,
- REGISTERED_USERS,
- "refs/heads/*");
- u.save();
- }
+ public void submitWithRebaseWithoutAddPatchSetPermission() throws Throwable {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.ADD_PATCH_SET).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.SUBMIT).ref("refs/heads/*").group(REGISTERED_USERS))
+ .add(
+ allowLabel(TestLabels.codeReview().getName())
+ .ref("refs/heads/*")
+ .group(REGISTERED_USERS)
+ .range(-2, 2))
+ .update();
submitWithRebase(user);
}
protected ImmutableList<PushOneCommit.Result> submitWithRebase(TestAccount submitter)
- throws Exception {
+ throws Throwable {
requestScopeOperations.setApiUser(submitter.id());
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
submit(change2.getChangeId());
assertRebase(testRepo, false);
- RevCommit headAfterSecondSubmit = getRemoteHead();
+ RevCommit headAfterSecondSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterSecondSubmit.getParent(0)).isEqualTo(headAfterFirstSubmit);
assertApproved(change2.getChangeId(), submitter);
assertCurrentRevision(change2.getChangeId(), 2, headAfterSecondSubmit);
@@ -102,11 +106,11 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
}
@Test
- public void submitWithRebaseMultipleChanges() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithRebaseMultipleChanges() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change1 = createChange("Change 1", "a.txt", "content");
submit(change1.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
if (getSubmitType() == SubmitType.REBASE_ALWAYS) {
assertCurrentRevision(change1.getChangeId(), 2, headAfterFirstSubmit);
} else {
@@ -127,7 +131,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
assertApproved(change3.getChangeId());
assertApproved(change4.getChangeId());
- RevCommit headAfterSecondSubmit = parse(getRemoteHead());
+ RevCommit headAfterSecondSubmit = parse(projectOperations.project(project).getHead("master"));
assertThat(headAfterSecondSubmit.getShortMessage()).isEqualTo("Change 4");
assertThat(headAfterSecondSubmit).isNotEqualTo(change4.getCommit());
assertCurrentRevision(change4.getChangeId(), 2, headAfterSecondSubmit);
@@ -163,7 +167,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
}
@Test
- public void submitWithRebaseMergeCommit() throws Exception {
+ public void submitWithRebaseMergeCommit() throws Throwable {
/*
* (HEAD, origin/master, origin/HEAD) Merge changes X,Y
|\
@@ -175,7 +179,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
|/
* Initial empty repository
*/
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change1 = createChange("Added a", "a.txt", "");
PushOneCommit change2Push =
@@ -193,7 +197,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
approve(change2.getChangeId());
submit(change2.getChangeId());
- RevCommit newHead = getRemoteHead();
+ RevCommit newHead = projectOperations.project(project).getHead("master");
assertThat(newHead.getParentCount()).isEqualTo(2);
RevCommit headParent1 = parse(newHead.getParent(0).getId());
@@ -219,12 +223,12 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithContentMerge_Conflict() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithContentMerge_Conflict() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "other content");
submitWithConflict(
@@ -232,7 +236,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
"Cannot rebase "
+ change2.getCommit().name()
+ ": The change could not be rebased due to a conflict during merge.");
- RevCommit head = getRemoteHead();
+ RevCommit head = projectOperations.project(project).getHead("master");
assertThat(head).isEqualTo(headAfterFirstSubmit);
assertCurrentRevision(change2.getChangeId(), 1, change2.getCommit());
assertNoSubmitter(change2.getChangeId(), 1);
@@ -241,7 +245,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
assertChangeMergedEvents(change.getChangeId(), headAfterFirstSubmit.name());
}
- protected RevCommit parse(ObjectId id) throws Exception {
+ protected RevCommit parse(ObjectId id) throws Throwable {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
RevCommit c = rw.parseCommit(id);
@@ -251,8 +255,8 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
}
@Test
- public void submitAfterReorderOfCommits() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitAfterReorderOfCommits() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
// Create two commits and push.
RevCommit c1 = commitBuilder().add("a.txt", "1").message("subject: 1").create();
@@ -271,15 +275,15 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
approve(id1);
approve(id2);
submit(id1);
- RevCommit headAfterSubmit = getRemoteHead();
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
assertRefUpdatedEvents(initialHead, headAfterSubmit);
assertChangeMergedEvents(id2, headAfterSubmit.name(), id1, headAfterSubmit.name());
}
@Test
- public void submitChangesAfterBranchOnSecond() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitChangesAfterBranchOnSecond() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
approve(change.getChangeId());
@@ -287,13 +291,13 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
PushOneCommit.Result change2 = createChange();
approve(change2.getChangeId());
Project.NameKey project = change2.getChange().change().getProject();
- Branch.NameKey branch = new Branch.NameKey(project, "branch");
+ BranchNameKey branch = BranchNameKey.create(project, "branch");
createBranchWithRevision(branch, change2.getCommit().getName());
gApi.changes().id(change2.getChangeId()).current().submit();
assertMerged(change2.getChangeId());
assertMerged(change.getChangeId());
- RevCommit newHead = getRemoteHead();
+ RevCommit newHead = projectOperations.project(this.project).getHead("master");
assertRefUpdatedEvents(initialHead, newHead);
assertChangeMergedEvents(
change.getChangeId(), newHead.name(), change2.getChangeId(), newHead.name());
@@ -301,8 +305,8 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitFastForwardIdenticalTree() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitFastForwardIdenticalTree() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change1 = createChange("Change 1", "a.txt", "a");
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "a");
@@ -313,18 +317,18 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
testRepo.reset(initialHead);
PushOneCommit.Result change0 = createChange("Change 0", "b.txt", "b");
submit(change0.getChangeId());
- RevCommit headAfterChange0 = getRemoteHead();
+ RevCommit headAfterChange0 = projectOperations.project(project).getHead("master");
assertThat(headAfterChange0.getShortMessage()).isEqualTo("Change 0");
submit(change1.getChangeId());
- RevCommit headAfterChange1 = getRemoteHead();
+ RevCommit headAfterChange1 = projectOperations.project(project).getHead("master");
assertThat(headAfterChange1.getShortMessage()).isEqualTo("Change 1");
assertThat(headAfterChange0).isEqualTo(headAfterChange1.getParent(0));
// Do manual rebase first.
gApi.changes().id(change2.getChangeId()).current().rebase();
submit(change2.getChangeId());
- RevCommit headAfterChange2 = getRemoteHead();
+ RevCommit headAfterChange2 = projectOperations.project(project).getHead("master");
assertThat(headAfterChange2.getShortMessage()).isEqualTo("Change 2");
assertThat(headAfterChange1).isEqualTo(headAfterChange2.getParent(0));
@@ -334,7 +338,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitChainOneByOne() throws Exception {
+ public void submitChainOneByOne() throws Throwable {
PushOneCommit.Result change1 = createChange("subject 1", "fileName 1", "content 1");
PushOneCommit.Result change2 = createChange("subject 2", "fileName 2", "content 2");
submit(change1.getChangeId());
@@ -343,7 +347,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitChainFailsOnRework() throws Exception {
+ public void submitChainFailsOnRework() throws Throwable {
PushOneCommit.Result change1 = createChange("subject 1", "fileName 1", "content 1");
RevCommit headAfterChange1 = change1.getCommit();
PushOneCommit.Result change2 = createChange("subject 2", "fileName 2", "content 2");
@@ -351,7 +355,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
change1 =
amendChange(change1.getChangeId(), "subject 1 amend", "fileName 2", "rework content 2");
submit(change1.getChangeId());
- headAfterChange1 = getRemoteHead();
+ headAfterChange1 = projectOperations.project(project).getHead("master");
submitWithConflict(
change2.getChangeId(),
@@ -359,13 +363,13 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
+ change2.getCommit().getName()
+ ": "
+ "The change could not be rebased due to a conflict during merge.");
- assertThat(getRemoteHead()).isEqualTo(headAfterChange1);
+ assertThat(projectOperations.project(project).getHead("master")).isEqualTo(headAfterChange1);
}
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitChainOneByOneManualRebase() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitChainOneByOneManualRebase() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change1 = createChange("subject 1", "fileName 1", "content 1");
PushOneCommit.Result change2 = createChange("subject 2", "fileName 2", "content 2");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
index e7f3d54bb4..dda7bbd89a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
@@ -22,9 +22,14 @@ import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVI
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.ActionVisitor;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ListChangesOption;
@@ -32,11 +37,8 @@ import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.change.RevisionJson;
+import com.google.gerrit.server.change.testing.TestChangeETagComputation;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Inject;
@@ -45,8 +47,6 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.jgit.lib.Config;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
public class ActionsIT extends AbstractDaemonTest {
@@ -55,23 +55,9 @@ public class ActionsIT extends AbstractDaemonTest {
return submitWholeTopicEnabledConfig();
}
- @Inject private DynamicSet<ActionVisitor> actionVisitors;
@Inject private RequestScopeOperations requestScopeOperations;
@Inject private RevisionJson.Factory revisionJsonFactory;
-
- private RegistrationHandle visitorHandle;
-
- @Before
- public void setUp() {
- visitorHandle = null;
- }
-
- @After
- public void tearDown() {
- if (visitorHandle != null) {
- visitorHandle.remove();
- }
- }
+ @Inject private ExtensionRegistry extensionRegistry;
protected Map<String, ActionInfo> getActions(String id) throws Exception {
return gApi.changes().id(id).revision(1).actions();
@@ -206,6 +192,45 @@ public class ActionsIT extends AbstractDaemonTest {
}
@Test
+ public void pluginCanContributeToETagComputation() throws Exception {
+ String change = createChange().getChangeId();
+ String oldETag = getETag(change);
+
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(TestChangeETagComputation.withETag("foo"))) {
+ assertThat(getETag(change)).isNotEqualTo(oldETag);
+ }
+
+ assertThat(getETag(change)).isEqualTo(oldETag);
+ }
+
+ @Test
+ public void returningNullFromETagComputationDoesNotBreakGerrit() throws Exception {
+ String change = createChange().getChangeId();
+ String oldETag = getETag(change);
+
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(TestChangeETagComputation.withETag(null))) {
+ assertThat(getETag(change)).isEqualTo(oldETag);
+ }
+ }
+
+ @Test
+ public void throwingExceptionFromETagComputationDoesNotBreakGerrit() throws Exception {
+ String change = createChange().getChangeId();
+ String oldETag = getETag(change);
+
+ try (Registration registration =
+ extensionRegistry
+ .newRegistration()
+ .add(
+ TestChangeETagComputation.withException(
+ new StorageException("exception during test")))) {
+ assertThat(getETag(change)).isEqualTo(oldETag);
+ }
+ }
+
+ @Test
public void revisionActionsTwoChangesInTopic_conflicting() throws Exception {
String changeId = createChangeWithTopic().getChangeId();
approve(changeId);
@@ -331,19 +356,18 @@ public class ActionsIT extends AbstractDaemonTest {
assertThat(origActions.keySet()).containsAtLeast("followup", "abandon");
assertThat(origActions.get("abandon").label).isEqualTo("Abandon");
- Visitor v = new Visitor();
- visitorHandle = actionVisitors.add("gerrit", v);
-
- Map<String, ActionInfo> newActions =
- gApi.changes().id(id).get(EnumSet.of(ListChangesOption.CHANGE_ACTIONS)).actions;
+ try (Registration registration = extensionRegistry.newRegistration().add(new Visitor())) {
+ Map<String, ActionInfo> newActions =
+ gApi.changes().id(id).get(EnumSet.of(ListChangesOption.CHANGE_ACTIONS)).actions;
- Set<String> expectedNames = new TreeSet<>(origActions.keySet());
- expectedNames.remove("followup");
- assertThat(newActions.keySet()).isEqualTo(expectedNames);
+ Set<String> expectedNames = new TreeSet<>(origActions.keySet());
+ expectedNames.remove("followup");
+ assertThat(newActions.keySet()).isEqualTo(expectedNames);
- ActionInfo abandon = newActions.get("abandon");
- assertThat(abandon).isNotNull();
- assertThat(abandon.label).isEqualTo("Abandon All Hope");
+ ActionInfo abandon = newActions.get("abandon");
+ assertThat(abandon).isNotNull();
+ assertThat(abandon.label).isEqualTo("Abandon All Hope");
+ }
}
@Test
@@ -351,7 +375,7 @@ public class ActionsIT extends AbstractDaemonTest {
String id = createChange().getChangeId();
amendChange(id);
ChangeInfo origChange = gApi.changes().id(id).get(CHANGE_ACTIONS);
- Change.Id changeId = new Change.Id(origChange._number);
+ Change.Id changeId = Change.id(origChange._number);
class Visitor implements ActionVisitor {
@Override
@@ -380,22 +404,22 @@ public class ActionsIT extends AbstractDaemonTest {
assertThat(origActions.keySet()).containsAtLeast("cherrypick", "rebase");
assertThat(origActions.get("rebase").label).isEqualTo("Rebase");
- Visitor v = new Visitor();
- visitorHandle = actionVisitors.add("gerrit", v);
-
- // Test different codepaths within ActionJson...
- // ...via revision API.
- visitedCurrentRevisionActionsAssertions(origActions, gApi.changes().id(id).current().actions());
-
- // ...via change API with option.
- EnumSet<ListChangesOption> opts = EnumSet.of(CURRENT_ACTIONS, CURRENT_REVISION);
- ChangeInfo changeInfo = gApi.changes().id(id).get(opts);
- RevisionInfo revisionInfo = Iterables.getOnlyElement(changeInfo.revisions.values());
- visitedCurrentRevisionActionsAssertions(origActions, revisionInfo.actions);
-
- // ...via ChangeJson directly.
- ChangeData cd = changeDataFactory.create(project, changeId);
- revisionJsonFactory.create(opts).getRevisionInfo(cd, cd.patchSet(new PatchSet.Id(changeId, 1)));
+ try (Registration registration = extensionRegistry.newRegistration().add(new Visitor())) {
+ // Test different codepaths within ActionJson...
+ // ...via revision API.
+ visitedCurrentRevisionActionsAssertions(
+ origActions, gApi.changes().id(id).current().actions());
+
+ // ...via change API with option.
+ EnumSet<ListChangesOption> opts = EnumSet.of(CURRENT_ACTIONS, CURRENT_REVISION);
+ ChangeInfo changeInfo = gApi.changes().id(id).get(opts);
+ RevisionInfo revisionInfo = Iterables.getOnlyElement(changeInfo.revisions.values());
+ visitedCurrentRevisionActionsAssertions(origActions, revisionInfo.actions);
+
+ // ...via ChangeJson directly.
+ ChangeData cd = changeDataFactory.create(project, changeId);
+ revisionJsonFactory.create(opts).getRevisionInfo(cd, cd.patchSet(PatchSet.id(changeId, 1)));
+ }
}
private void visitedCurrentRevisionActionsAssertions(
@@ -440,18 +464,17 @@ public class ActionsIT extends AbstractDaemonTest {
assertThat(origActions.keySet()).containsExactly("description");
assertThat(origActions.get("description").label).isEqualTo("Edit Description");
- Visitor v = new Visitor();
- visitorHandle = actionVisitors.add("gerrit", v);
-
- // Unlike for the current revision, actions for old revisions are only available via the
- // revision API.
- Map<String, ActionInfo> newActions = gApi.changes().id(id).revision(1).actions();
- assertThat(newActions).isNotNull();
- assertThat(newActions.keySet()).isEqualTo(origActions.keySet());
+ try (Registration registration = extensionRegistry.newRegistration().add(new Visitor())) {
+ // Unlike for the current revision, actions for old revisions are only available via the
+ // revision API.
+ Map<String, ActionInfo> newActions = gApi.changes().id(id).revision(1).actions();
+ assertThat(newActions).isNotNull();
+ assertThat(newActions.keySet()).isEqualTo(origActions.keySet());
- ActionInfo description = newActions.get("description");
- assertThat(description).isNotNull();
- assertThat(description.label).isEqualTo("Describify");
+ ActionInfo description = newActions.get("description");
+ assertThat(description).isNotNull();
+ assertThat(description.label).isEqualTo("Describify");
+ }
}
private void commonActionsAssertions(Map<String, ActionInfo> actions) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
index 2d6227bd47..0f5def699a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
@@ -15,47 +15,38 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.AssigneeInput;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
import com.google.gerrit.testing.FakeEmailSender.Message;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jgit.transport.RefSpec;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
import org.junit.Test;
@NoHttpd
+@UseClockStep
public class AssigneeIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
- @BeforeClass
- public static void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @AfterClass
- public static void restoreTime() {
- TestTimeUtil.useSystemTime();
- }
-
@Test
public void getNoAssignee() throws Exception {
PushOneCommit.Result r = createChange();
@@ -93,16 +84,32 @@ public class AssigneeIT extends AbstractDaemonTest {
}
@Test
- public void assigneeAddedAsReviewer() throws Exception {
- ReviewerState state = ReviewerState.CC;
+ public void assigneeAddedAsCc() throws Exception {
PushOneCommit.Result r = createChange();
- Iterable<AccountInfo> reviewers = getReviewers(r, state);
+ Iterable<AccountInfo> reviewers = getReviewers(r, ReviewerState.CC);
assertThat(reviewers).isNull();
+
assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
- reviewers = getReviewers(r, state);
+ reviewers = getReviewers(r, ReviewerState.CC);
assertThat(reviewers).hasSize(1);
- AccountInfo reviewer = Iterables.getFirst(reviewers, null);
- assertThat(reviewer._accountId).isEqualTo(user.id().get());
+ assertThat(Iterables.getFirst(reviewers, null)._accountId).isEqualTo(user.id().get());
+ assertThat(getReviewers(r, ReviewerState.REVIEWER)).isNull();
+ }
+
+ @Test
+ public void assigneeStaysReviewer() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).addReviewer(user.email());
+ Iterable<AccountInfo> reviewers = getReviewers(r, ReviewerState.REVIEWER);
+ assertThat(reviewers).hasSize(1);
+ assertThat(Iterables.getFirst(reviewers, null)._accountId).isEqualTo(user.id().get());
+ assertThat(getReviewers(r, ReviewerState.CC)).isNull();
+
+ assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
+ reviewers = getReviewers(r, ReviewerState.REVIEWER);
+ assertThat(reviewers).hasSize(1);
+ assertThat(Iterables.getFirst(reviewers, null)._accountId).isEqualTo(user.id().get());
+ assertThat(getReviewers(r, ReviewerState.CC)).isNull();
}
@Test
@@ -130,20 +137,17 @@ public class AssigneeIT extends AbstractDaemonTest {
public void setAssigneeToInactiveUser() throws Exception {
PushOneCommit.Result r = createChange();
gApi.accounts().id(user.id().get()).setActive(false);
- try {
- setAssignee(r, user.email());
- assert_().fail("expected UnresolvableAccountException");
- } catch (UnresolvableAccountException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- "Account '"
- + user.email()
- + "' only matches inactive accounts. To use an inactive account, retry with one"
- + " of the following exact account IDs:\n"
- + user.id()
- + ": User <user@example.com>");
- }
+ UnresolvableAccountException thrown =
+ assertThrows(UnresolvableAccountException.class, () -> setAssignee(r, user.email()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ "Account '"
+ + user.email()
+ + "' only matches inactive accounts. To use an inactive account, retry with one"
+ + " of the following exact account IDs:\n"
+ + user.id()
+ + ": User <user@example.com>");
}
@Test
@@ -159,24 +163,26 @@ public class AssigneeIT extends AbstractDaemonTest {
git().fetch().setRefSpecs(new RefSpec("refs/meta/config:refs/meta/config")).call();
testRepo.reset(RefNames.REFS_CONFIG);
PushOneCommit.Result r = createChange("refs/for/refs/meta/config");
- exception.expect(AuthException.class);
- exception.expectMessage("read not permitted");
- setAssignee(r, user.email());
+ AuthException thrown = assertThrows(AuthException.class, () -> setAssignee(r, user.email()));
+ assertThat(thrown).hasMessageThat().contains("read not permitted");
}
@Test
public void setAssigneeNotAllowedWithoutPermission() throws Exception {
PushOneCommit.Result r = createChange();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("not permitted");
- setAssignee(r, user.email());
+ AuthException thrown = assertThrows(AuthException.class, () -> setAssignee(r, user.email()));
+ assertThat(thrown).hasMessageThat().contains("not permitted");
}
@Test
public void setAssigneeAllowedWithPermission() throws Exception {
PushOneCommit.Result r = createChange();
- grant(project, "refs/heads/master", Permission.EDIT_ASSIGNEE, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.EDIT_ASSIGNEE).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/BUILD b/javatests/com/google/gerrit/acceptance/rest/change/BUILD
index 7ccf10f717..1eddccec48 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/change/BUILD
@@ -20,14 +20,14 @@ acceptance_tests(
],
)
-acceptance_tests(
- srcs = SUBMIT_TESTS,
- group = "rest_change_submit",
+[acceptance_tests(
+ srcs = [f],
+ group = f[:f.index(".")],
labels = ["rest"],
deps = [
":submit_util",
],
-)
+) for f in SUBMIT_TESTS]
java_library(
name = "submit_util",
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java
index 3f1608c2a1..bc52681fdb 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java
@@ -17,8 +17,8 @@ package com.google.gerrit.acceptance.rest.change;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.ChangeApi;
-import com.google.gerrit.reviewdb.client.Change;
import org.junit.Test;
public class ChangeIdIT extends AbstractDaemonTest {
@@ -98,14 +98,14 @@ public class ChangeIdIT extends AbstractDaemonTest {
// This test tests a redirect that is primarily intended for the UI (though the backend doesn't
// really care who the caller is). The redirect rewrites a shorthand change number URL (/123) to
// it's canonical long form (/c/project/+/123).
- int changeId = createChange().getChange().getId().id;
+ int changeId = createChange().getChange().getId().get();
RestResponse res = anonymousRestSession.get("/" + changeId);
res.assertTemporaryRedirect("/c/" + project.get() + "/+/" + changeId + "/");
}
@Test
public void changeNumberRedirectsWithTrailingSlash() throws Exception {
- int changeId = createChange().getChange().getId().id;
+ int changeId = createChange().getChange().getId().get();
RestResponse res = anonymousRestSession.get("/" + changeId + "/");
res.assertTemporaryRedirect("/c/" + project.get() + "/+/" + changeId + "/");
}
@@ -125,8 +125,8 @@ public class ChangeIdIT extends AbstractDaemonTest {
@Test
public void hiddenChangeNotFound() throws Exception {
Change.Id changeId = createChange().getChange().getId();
- gApi.changes().id(changeId.id).setPrivate(true, null);
- RestResponse res = anonymousRestSession.get("/" + changeId.id);
+ gApi.changes().id(changeId.get()).setPrivate(true, null);
+ RestResponse res = anonymousRestSession.get("/" + changeId.get());
res.assertNotFound();
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeIncludedInIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeIncludedInIT.java
index 59b6e29293..47fb20a43e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeIncludedInIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeIncludedInIT.java
@@ -15,20 +15,25 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit.Result;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.TagInput;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.inject.Inject;
import org.junit.Test;
@NoHttpd
public class ChangeIncludedInIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+
@Test
public void includedInOpenChange() throws Exception {
Result result = createChange();
@@ -49,13 +54,17 @@ public class ChangeIncludedInIT extends AbstractDaemonTest {
.containsExactly("master");
assertThat(gApi.changes().id(result.getChangeId()).includedIn().tags).isEmpty();
- grant(project, R_TAGS + "*", Permission.CREATE_TAG);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE_TAG).ref(R_TAGS + "*").group(adminGroupUuid()))
+ .update();
gApi.projects().name(project.get()).tag("test-tag").create(new TagInput());
assertThat(gApi.changes().id(result.getChangeId()).includedIn().tags)
.containsExactly("test-tag");
- createBranch(new Branch.NameKey(project.get(), "test-branch"));
+ createBranch(BranchNameKey.create(project, "test-branch"));
assertThat(gApi.changes().id(result.getChangeId()).includedIn().branches)
.containsExactly("master", "test-branch");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
index 51c5fc8686..8cfcbabb05 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
@@ -14,12 +14,15 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.parseCommitMessageRange;
import static com.google.gerrit.server.restapi.change.DeleteChangeMessage.createNewChangeMessage;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toSet;
import static org.eclipse.jgit.util.RawParseUtils.decode;
@@ -28,8 +31,12 @@ import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.UseTimezone;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.DeleteChangeMessageInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -37,10 +44,8 @@ import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -49,29 +54,16 @@ import java.util.List;
import java.util.Optional;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.util.RawParseUtils;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+@UseClockStep
+@UseTimezone(timezone = "US/Eastern")
@RunWith(ConfigSuite.class)
public class ChangeMessagesIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
- private String systemTimeZone;
-
- @Before
- public void setTimeForTesting() {
- systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @After
- public void resetTime() {
- TestTimeUtil.useSystemTime();
- System.setProperty("user.timezone", systemTimeZone);
- }
-
@Test
public void messagesNotReturnedByDefault() throws Exception {
String changeId = createChange().getChangeId();
@@ -159,17 +151,17 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
int changeNum = createOneChangeWithMultipleChangeMessagesInHistory();
requestScopeOperations.setApiUser(user.id());
- try {
- deleteOneChangeMessage(changeNum, 0, user, "spam");
- fail("expected AuthException");
- } catch (AuthException e) {
- assertThat(e.getMessage()).isEqualTo("administrate server not permitted");
- }
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> deleteOneChangeMessage(changeNum, 0, user, "spam"));
+ assertThat(thrown).hasMessageThat().isEqualTo("administrate server not permitted");
}
@Test
public void deleteCanBeAppliedWithAdministrateServerCapability() throws Exception {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ADMINISTRATE_SERVER);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.ADMINISTRATE_SERVER).group(REGISTERED_USERS))
+ .update();
int changeNum = createOneChangeWithMultipleChangeMessagesInHistory();
requestScopeOperations.setApiUser(user.id());
deleteOneChangeMessage(changeNum, 0, user, "spam");
@@ -179,12 +171,15 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
public void deleteCannotBeAppliedWithEmptyChangeMessageUuid() throws Exception {
String changeId = createChange().getChangeId();
- try {
- gApi.changes().id(changeId).message("").delete(new DeleteChangeMessageInput("spam"));
- fail("expected ResourceNotFoundException");
- } catch (ResourceNotFoundException e) {
- assertThat(e.getMessage()).isEqualTo("change message not found");
- }
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () ->
+ gApi.changes()
+ .id(changeId)
+ .message("")
+ .delete(new DeleteChangeMessageInput("spam")));
+ assertThat(thrown).hasMessageThat().isEqualTo("change message not found");
}
@Test
@@ -194,12 +189,11 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
String id = "8473b95934b5732ac55d26311a706c9c2bde9941";
input.reason = "spam";
- try {
- gApi.changes().id(changeId).message(id).delete(input);
- fail("expected ResourceNotFoundException");
- } catch (ResourceNotFoundException e) {
- assertThat(e.getMessage()).isEqualTo(String.format("change message %s not found", id));
- }
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(changeId).message(id).delete(input));
+ assertThat(thrown).hasMessageThat().isEqualTo(String.format("change message %s not found", id));
}
@Test
@@ -279,7 +273,7 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
List<ChangeMessageInfo> messagesBeforeDeletion = gApi.changes().id(changeNum).messages();
List<CommentInfo> commentsBefore = getChangeSortedComments(changeNum);
- List<RevCommit> commitsBefore = getChangeMetaCommitsInReverseOrder(new Change.Id(changeNum));
+ List<RevCommit> commitsBefore = getChangeMetaCommitsInReverseOrder(Change.id(changeNum));
String id = messagesBeforeDeletion.get(deletedMessageIndex).id;
DeleteChangeMessageInput input = new DeleteChangeMessageInput(reason);
@@ -306,8 +300,8 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
int deletedMessageIndex,
TestAccount deletedBy,
String deleteReason) {
- assertThat(messagesAfterDeletion)
- .named("after: %s; before: %s", messagesAfterDeletion, messagesBeforeDeletion)
+ assertWithMessage("after: %s; before: %s", messagesAfterDeletion, messagesBeforeDeletion)
+ .that(messagesAfterDeletion)
.hasSize(messagesBeforeDeletion.size());
for (int i = 0; i < messagesAfterDeletion.size(); ++i) {
@@ -340,8 +334,7 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
TestAccount deletedBy,
String deleteReason)
throws Exception {
- List<RevCommit> commitsAfterDeletion =
- getChangeMetaCommitsInReverseOrder(new Change.Id(changeNum));
+ List<RevCommit> commitsAfterDeletion = getChangeMetaCommitsInReverseOrder(Change.id(changeNum));
assertThat(commitsAfterDeletion).hasSize(commitsBeforeDeletion.size());
for (int i = 0; i < commitsBeforeDeletion.size(); i++) {
@@ -356,8 +349,8 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
parseCommitMessageRange(commitBefore);
Optional<ChangeNoteUtil.CommitMessageRange> rangeAfter =
parseCommitMessageRange(commitAfter);
- assertThat(rangeBefore.isPresent()).isTrue();
- assertThat(rangeAfter.isPresent()).isTrue();
+ assertThat(rangeBefore).isPresent();
+ assertThat(rangeAfter).isPresent();
String subjectBefore =
decode(
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
index d51221e1b2..10194eb075 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
@@ -14,6 +14,11 @@
package com.google.gerrit.acceptance.rest.change;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.blockLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.labelPermissionKey;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
import com.google.gerrit.acceptance.PushOneCommit;
@@ -21,10 +26,10 @@ import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.inject.Inject;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
@@ -121,8 +126,7 @@ public class ChangeOwnerIT extends AbstractDaemonTest {
}
private void assertApproveFails(TestAccount a, String changeId) throws Exception {
- exception.expect(AuthException.class);
- approve(a, changeId);
+ assertThrows(AuthException.class, () -> approve(a, changeId));
}
private void grantApproveToChangeOwner(Project.NameKey project) throws Exception {
@@ -139,11 +143,24 @@ public class ChangeOwnerIT extends AbstractDaemonTest {
private void grantApprove(Project.NameKey project, AccountGroup.UUID groupUUID, boolean exclusive)
throws Exception {
- grantLabel("Code-Review", -2, 2, project, "refs/heads/*", false, groupUUID, exclusive);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(groupUUID).range(-2, 2))
+ .setExclusiveGroup(labelPermissionKey("Code-Review").ref("refs/heads/*"), exclusive)
+ .update();
}
private void blockApproveForChangeOwner(Project.NameKey project) throws Exception {
- blockLabel("Code-Review", -2, 2, SystemGroupBackend.CHANGE_OWNER, "refs/heads/*", project);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ blockLabel("Code-Review")
+ .ref("refs/heads/*")
+ .group(SystemGroupBackend.CHANGE_OWNER)
+ .range(-2, 2))
+ .update();
}
private String createMyChange(TestRepository<InMemoryRepository> testRepo) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index 173b78d9c2..fbe6533766 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -15,11 +15,14 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.extensions.client.ReviewerState.CC;
import static com.google.gerrit.extensions.client.ReviewerState.REMOVED;
import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_OK;
@@ -31,8 +34,10 @@ import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -49,7 +54,6 @@ import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.common.ReviewerUpdateInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.change.ReviewerAdder;
import com.google.gerrit.testing.FakeEmailSender.Message;
import com.google.gson.stream.JsonReader;
@@ -66,6 +70,7 @@ import org.junit.Test;
public class ChangeReviewersIT extends AbstractDaemonTest {
@Inject private GroupOperations groupOperations;
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Test
@@ -332,20 +337,18 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(result.reviewers).isNotNull();
assertThat(result.reviewers).hasSize(1);
- // Verify reviewer state. Both admin and user should be REVIEWERs now,
- // because admin gets forced into REVIEWER state by virtue of being owner.
+ // Verify reviewer state.
c = gApi.changes().id(r.getChangeId()).get();
- assertReviewers(c, REVIEWER, admin, user);
+ assertReviewers(c, REVIEWER, user);
assertReviewers(c, CC);
label = c.labels.get("Code-Review");
assertThat(label).isNotNull();
assertThat(label.all).isNotNull();
- assertThat(label.all).hasSize(2);
+ assertThat(label.all).hasSize(1);
Map<Integer, Integer> approvals = new HashMap<>();
for (ApprovalInfo approval : label.all) {
approvals.put(approval._accountId, approval.value);
}
- assertThat(approvals).containsEntry(admin.id().get(), 0);
assertThat(approvals).containsEntry(user.id().get(), 0);
// Comment as user without voting. This should delete the approval and
@@ -360,17 +363,16 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
// Verify reviewer state.
c = gApi.changes().id(r.getChangeId()).get();
- assertReviewers(c, REVIEWER, admin, user);
+ assertReviewers(c, REVIEWER, user);
assertReviewers(c, CC);
label = c.labels.get("Code-Review");
assertThat(label).isNotNull();
assertThat(label.all).isNotNull();
- assertThat(label.all).hasSize(2);
+ assertThat(label.all).hasSize(1);
approvals.clear();
for (ApprovalInfo approval : label.all) {
approvals.put(approval._accountId, approval.value);
}
- assertThat(approvals).containsEntry(admin.id().get(), 0);
assertThat(approvals).containsEntry(user.id().get(), 0);
}
@@ -386,8 +388,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(result.reviewers).isNotNull();
assertThat(result.reviewers).hasSize(2);
- // Verify reviewer and CC were added. If not in NoteDb read mode, both
- // parties will be returned as CCed.
+ // Verify reviewer and CC were added.
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
assertReviewers(c, REVIEWER, admin, user);
assertReviewers(c, CC, observer);
@@ -488,7 +489,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
}
@Test
- public void noteDbAddReviewerToReviewerChangeInfo() throws Exception {
+ public void addReviewerToReviewerChangeInfo() throws Exception {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
AddReviewerInput in = new AddReviewerInput();
@@ -502,7 +503,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
gApi.changes().id(changeId).current().review(ReviewInput.dislike());
requestScopeOperations.setApiUser(user.id());
- // NoteDb adds reviewer to a change on every review.
+ // By posting a review the user is added as reviewer.
gApi.changes().id(changeId).current().review(ReviewInput.dislike());
deleteReviewer(changeId, user).assertNoContent();
@@ -662,9 +663,11 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).current().review(new ReviewInput().label("Code-Review", 1));
requestScopeOperations.setApiUser(newUser.id());
- exception.expect(AuthException.class);
- exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove());
+ assertThat(thrown).hasMessageThat().contains("remove reviewer not permitted");
}
@Test
@@ -674,7 +677,11 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
// This test creates a new user so that it can explicitly check the REMOVE_REVIEWER permission
// rather than bypassing the check because of project or ref ownership.
TestAccount newUser = createAccounts(1, name("foo")).get(0);
- grant(project, RefNames.REFS + "*", Permission.REMOVE_REVIEWER, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.REMOVE_REVIEWER).ref(RefNames.REFS + "*").group(REGISTERED_USERS))
+ .update();
gApi.changes().id(r.getChangeId()).addReviewer(user.email());
assertThatUserIsOnlyReviewer(r.getChangeId());
@@ -690,9 +697,11 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).addReviewer(user.email());
requestScopeOperations.setApiUser(newUser.id());
- exception.expect(AuthException.class);
- exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove());
+ assertThat(thrown).hasMessageThat().contains("remove reviewer not permitted");
}
@Test
@@ -705,9 +714,11 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
input.state = ReviewerState.CC;
gApi.changes().id(r.getChangeId()).addReviewer(input);
requestScopeOperations.setApiUser(newUser.id());
- exception.expect(AuthException.class);
- exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
+ AuthException thrown =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove());
+ assertThat(thrown).hasMessageThat().contains("remove reviewer not permitted");
}
@Test
@@ -742,6 +753,81 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(r.getChangeId()).addReviewer(input).ccs).isEmpty();
}
+ @Test
+ public void moveCcToReviewer() throws Exception {
+ // Create a change and add 'user' as CC.
+ String changeId = createChange().getChangeId();
+ AddReviewerInput reviewerInput = new AddReviewerInput();
+ reviewerInput.reviewer = user.email();
+ reviewerInput.state = ReviewerState.CC;
+ gApi.changes().id(changeId).addReviewer(reviewerInput);
+
+ // Verify that 'user' is a CC on the change and that there are no reviewers.
+ ChangeInfo c = gApi.changes().id(changeId).get();
+ Collection<AccountInfo> ccs = c.reviewers.get(CC);
+ assertThat(ccs).isNotNull();
+ assertThat(ccs).hasSize(1);
+ assertThat(ccs.iterator().next()._accountId).isEqualTo(user.id().get());
+ assertThat(c.reviewers.get(REVIEWER)).isNull();
+
+ // Move 'user' from CC to reviewer.
+ gApi.changes().id(changeId).addReviewer(user.id().toString());
+
+ // Verify that 'user' is a reviewer on the change now and that there are no CCs.
+ c = gApi.changes().id(changeId).get();
+ Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
+ assertThat(reviewers).isNotNull();
+ assertThat(reviewers).hasSize(1);
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
+ assertThat(c.reviewers.get(CC)).isNull();
+ }
+
+ @Test
+ public void moveReviewerToCc() throws Exception {
+ // Allow everyone to approve changes.
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, 2))
+ .update();
+
+ // Create a change and add 'user' as reviewer.
+ String changeId = createChange().getChangeId();
+ gApi.changes().id(changeId).addReviewer(user.id().toString());
+
+ // Verify that 'user' is a reviewer on the change and that there are no CCs.
+ ChangeInfo c = gApi.changes().id(changeId).get();
+ Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
+ assertThat(reviewers).isNotNull();
+ assertThat(reviewers).hasSize(1);
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
+ assertThat(c.reviewers.get(CC)).isNull();
+
+ // Let 'user' approve the change and verify that the change has the approval.
+ requestScopeOperations.setApiUser(user.id());
+ approve(changeId);
+ c = gApi.changes().id(changeId).get();
+ assertThat(c.labels.get("Code-Review").approved._accountId).isEqualTo(user.id().get());
+
+ // Move 'user' from reviewer to CC.
+ requestScopeOperations.setApiUser(admin.id());
+ AddReviewerInput reviewerInput = new AddReviewerInput();
+ reviewerInput.reviewer = user.id().toString();
+ reviewerInput.state = CC;
+ gApi.changes().id(changeId).addReviewer(reviewerInput);
+
+ // Verify that 'user' is a CC on the change now and that there are no reviewers.
+ c = gApi.changes().id(changeId).get();
+ Collection<AccountInfo> ccs = c.reviewers.get(CC);
+ assertThat(ccs).isNotNull();
+ assertThat(ccs).hasSize(1);
+ assertThat(ccs.iterator().next()._accountId).isEqualTo(user.id().get());
+ assertThat(c.reviewers.get(REVIEWER)).isNull();
+
+ // Verify that the approval of 'user' is still there.
+ assertThat(c.labels.get("Code-Review").approved._accountId).isEqualTo(user.id().get());
+ }
+
private void assertThatUserIsOnlyReviewer(String changeId) throws Exception {
AccountInfo userInfo = new AccountInfo(user.fullName(), user.getEmailAddress().getEmail());
userInfo._accountId = user.id().get();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
index 9a907aaee3..243991b96c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
@@ -15,47 +15,46 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.truth.ConfigSubject.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.project.testing.Util;
import com.google.inject.Inject;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.junit.Before;
import org.junit.Test;
public class ConfigChangeIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Before
public void setUp() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(u.getConfig(), Permission.OWNER, REGISTERED_USERS, "refs/*");
- Util.allow(u.getConfig(), Permission.PUSH, REGISTERED_USERS, "refs/for/refs/meta/config");
- Util.allow(u.getConfig(), Permission.SUBMIT, REGISTERED_USERS, RefNames.REFS_CONFIG);
- u.save();
- }
-
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref("refs/for/refs/meta/config").group(REGISTERED_USERS))
+ .add(allow(Permission.SUBMIT).ref(RefNames.REFS_CONFIG).group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
fetchRefsMetaConfig();
}
@@ -75,8 +74,8 @@ public class ConfigChangeIT extends AbstractDaemonTest {
}
private String testUpdateProjectConfig() throws Exception {
- Config cfg = readProjectConfig();
- assertThat(cfg.getString("project", null, "description")).isNull();
+ Config cfg = projectOperations.project(project).getConfig();
+ assertThat(cfg).stringValue("project", null, "description").isNull();
String desc = "new project description";
cfg.setString("project", null, "description", desc);
@@ -89,7 +88,12 @@ public class ConfigChangeIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(id).info().status).isEqualTo(ChangeStatus.MERGED);
assertThat(gApi.projects().name(project.get()).get().description).isEqualTo(desc);
fetchRefsMetaConfig();
- assertThat(readProjectConfig().getString("project", null, "description")).isEqualTo(desc);
+ assertThat(
+ projectOperations
+ .project(project)
+ .getConfig()
+ .getString("project", null, "description"))
+ .isEqualTo(desc);
String changeRev = gApi.changes().id(id).get().currentRevision;
String branchRev =
gApi.projects().name(project.get()).branch(RefNames.REFS_CONFIG).get().revision;
@@ -107,33 +111,31 @@ public class ConfigChangeIT extends AbstractDaemonTest {
gApi.projects().create(parent);
requestScopeOperations.setApiUser(user.id());
- Config cfg = readProjectConfig();
- assertThat(cfg.getString("access", null, "inheritFrom")).isAnyOf(null, allProjects.get());
+ Config cfg = projectOperations.project(project).getConfig();
+ assertThat(cfg).stringValue("access", null, "inheritFrom").isAnyOf(null, allProjects.get());
cfg.setString("access", null, "inheritFrom", parent.name);
PushOneCommit.Result r = createConfigChange(cfg);
String id = r.getChangeId();
gApi.changes().id(id).current().review(ReviewInput.approve());
- try {
- gApi.changes().id(id).current().submit();
- fail("expected submit to fail");
- } catch (ResourceConflictException e) {
- int n = gApi.changes().id(id).info()._number;
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- "Failed to submit 1 change due to the following problems:\n"
- + "Change "
- + n
- + ": Change contains a project configuration that"
- + " changes the parent project.\n"
- + "The change must be submitted by a Gerrit administrator.");
- }
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> gApi.changes().id(id).current().submit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ "Failed to submit 1 change due to the following problems:\n"
+ + "Change "
+ + gApi.changes().id(id).info()._number
+ + ": Change contains a project configuration that"
+ + " changes the parent project.\n"
+ + "The change must be submitted by a Gerrit administrator.");
assertThat(gApi.projects().name(project.get()).get().parent).isEqualTo(allProjects.get());
fetchRefsMetaConfig();
- assertThat(readProjectConfig().getString("access", null, "inheritFrom"))
+ assertThat(
+ projectOperations.project(project).getConfig().getString("access", null, "inheritFrom"))
.isAnyOf(null, allProjects.get());
requestScopeOperations.setApiUser(admin.id());
@@ -141,7 +143,9 @@ public class ConfigChangeIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(id).info().status).isEqualTo(ChangeStatus.MERGED);
assertThat(gApi.projects().name(project.get()).get().parent).isEqualTo(parent.name);
fetchRefsMetaConfig();
- assertThat(readProjectConfig().getString("access", null, "inheritFrom")).isEqualTo(parent.name);
+ assertThat(
+ projectOperations.project(project).getConfig().getString("access", null, "inheritFrom"))
+ .isEqualTo(parent.name);
}
@Test
@@ -179,17 +183,6 @@ public class ConfigChangeIT extends AbstractDaemonTest {
testRepo.reset(RefNames.REFS_CONFIG);
}
- private Config readProjectConfig() throws Exception {
- RevWalk rw = testRepo.getRevWalk();
- RevTree tree = rw.parseTree(testRepo.getRepository().resolve("HEAD"));
- RevObject obj = rw.parseAny(testRepo.get(tree, "project.config"));
- ObjectLoader loader = rw.getObjectReader().open(obj);
- String text = new String(loader.getCachedBytes(), UTF_8);
- Config cfg = new Config();
- cfg.fromText(text);
- return cfg;
- }
-
private PushOneCommit.Result createConfigChange(Config cfg) throws Exception {
PushOneCommit.Result r =
pushFactory
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
index 9c42542188..3b26459958 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
@@ -26,6 +26,7 @@ import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
import static com.google.common.net.HttpHeaders.ORIGIN;
import static com.google.common.net.HttpHeaders.VARY;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
@@ -80,11 +81,11 @@ public class CorsIT extends AbstractDaemonTest {
String allowMethods = r.getHeader(ACCESS_CONTROL_ALLOW_METHODS);
String allowHeaders = r.getHeader(ACCESS_CONTROL_ALLOW_HEADERS);
- assertThat(allowOrigin).named(ACCESS_CONTROL_ALLOW_ORIGIN).isNull();
- assertThat(allowCred).named(ACCESS_CONTROL_ALLOW_CREDENTIALS).isNull();
- assertThat(maxAge).named(ACCESS_CONTROL_MAX_AGE).isNull();
- assertThat(allowMethods).named(ACCESS_CONTROL_ALLOW_METHODS).isNull();
- assertThat(allowHeaders).named(ACCESS_CONTROL_ALLOW_HEADERS).isNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_ORIGIN).that(allowOrigin).isNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_CREDENTIALS).that(allowCred).isNull();
+ assertWithMessage(ACCESS_CONTROL_MAX_AGE).that(maxAge).isNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_METHODS).that(allowMethods).isNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_HEADERS).that(allowHeaders).isNull();
}
@Test
@@ -162,7 +163,7 @@ public class CorsIT extends AbstractDaemonTest {
res.assertOK();
String vary = res.getHeader(VARY);
- assertThat(vary).named(VARY).isNotNull();
+ assertWithMessage(VARY).that(vary).isNotNull();
assertThat(Splitter.on(", ").splitToList(vary))
.containsExactly(ORIGIN, ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
checkCors(res, true, origin);
@@ -213,7 +214,7 @@ public class CorsIT extends AbstractDaemonTest {
auth = c.getValue();
}
}
- assertThat(auth).named("GerritAccount cookie").isNotNull();
+ assertWithMessage("GerritAccount cookie").that(auth).isNotNull();
cookies.clear();
UrlEncoded url =
@@ -232,16 +233,18 @@ public class CorsIT extends AbstractDaemonTest {
assertThat(r.getStatusLine().getStatusCode()).isEqualTo(200);
Header vary = r.getFirstHeader(VARY);
- assertThat(vary).named(VARY).isNotNull();
- assertThat(Splitter.on(", ").splitToList(vary.getValue())).named(VARY).contains(ORIGIN);
+ assertWithMessage(VARY).that(vary).isNotNull();
+ assertWithMessage(VARY).that(Splitter.on(", ").splitToList(vary.getValue())).contains(ORIGIN);
Header allowOrigin = r.getFirstHeader(ACCESS_CONTROL_ALLOW_ORIGIN);
- assertThat(allowOrigin).named(ACCESS_CONTROL_ALLOW_ORIGIN).isNotNull();
- assertThat(allowOrigin.getValue()).named(ACCESS_CONTROL_ALLOW_ORIGIN).isEqualTo(origin);
+ assertWithMessage(ACCESS_CONTROL_ALLOW_ORIGIN).that(allowOrigin).isNotNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_ORIGIN).that(allowOrigin.getValue()).isEqualTo(origin);
Header allowAuth = r.getFirstHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS);
- assertThat(allowAuth).named(ACCESS_CONTROL_ALLOW_CREDENTIALS).isNotNull();
- assertThat(allowAuth.getValue()).named(ACCESS_CONTROL_ALLOW_CREDENTIALS).isEqualTo("true");
+ assertWithMessage(ACCESS_CONTROL_ALLOW_CREDENTIALS).that(allowAuth).isNotNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_CREDENTIALS)
+ .that(allowAuth.getValue())
+ .isEqualTo("true");
checkTopic(change, "test-xd");
}
@@ -264,7 +267,7 @@ public class CorsIT extends AbstractDaemonTest {
private void checkTopic(Result change, @Nullable String topic) throws RestApiException {
ChangeInfo info = gApi.changes().id(change.getChangeId()).get();
- StringSubject t = assertThat(info.topic).named("topic");
+ StringSubject t = assertWithMessage("topic").that(info.topic);
if (topic != null) {
t.isEqualTo(topic);
} else {
@@ -287,8 +290,8 @@ public class CorsIT extends AbstractDaemonTest {
private void checkCors(RestResponse r, boolean accept, String origin) {
String vary = r.getHeader(VARY);
- assertThat(vary).named(VARY).isNotNull();
- assertThat(Splitter.on(", ").splitToList(vary)).named(VARY).contains(ORIGIN);
+ assertWithMessage(VARY).that(vary).isNotNull();
+ assertWithMessage(VARY).that(Splitter.on(", ").splitToList(vary)).contains(ORIGIN);
String allowOrigin = r.getHeader(ACCESS_CONTROL_ALLOW_ORIGIN);
String allowCred = r.getHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS);
@@ -296,28 +299,28 @@ public class CorsIT extends AbstractDaemonTest {
String allowMethods = r.getHeader(ACCESS_CONTROL_ALLOW_METHODS);
String allowHeaders = r.getHeader(ACCESS_CONTROL_ALLOW_HEADERS);
if (accept) {
- assertThat(allowOrigin).named(ACCESS_CONTROL_ALLOW_ORIGIN).isEqualTo(origin);
- assertThat(allowCred).named(ACCESS_CONTROL_ALLOW_CREDENTIALS).isEqualTo("true");
- assertThat(maxAge).named(ACCESS_CONTROL_MAX_AGE).isEqualTo("600");
+ assertWithMessage(ACCESS_CONTROL_ALLOW_ORIGIN).that(allowOrigin).isEqualTo(origin);
+ assertWithMessage(ACCESS_CONTROL_ALLOW_CREDENTIALS).that(allowCred).isEqualTo("true");
+ assertWithMessage(ACCESS_CONTROL_MAX_AGE).that(maxAge).isEqualTo("600");
- assertThat(allowMethods).named(ACCESS_CONTROL_ALLOW_METHODS).isNotNull();
- assertThat(Splitter.on(", ").splitToList(allowMethods))
- .named(ACCESS_CONTROL_ALLOW_METHODS)
+ assertWithMessage(ACCESS_CONTROL_ALLOW_METHODS).that(allowMethods).isNotNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_METHODS)
+ .that(Splitter.on(", ").splitToList(allowMethods))
.containsExactly("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS");
- assertThat(allowHeaders).named(ACCESS_CONTROL_ALLOW_HEADERS).isNotNull();
- assertThat(Splitter.on(", ").splitToList(allowHeaders))
- .named(ACCESS_CONTROL_ALLOW_HEADERS)
+ assertWithMessage(ACCESS_CONTROL_ALLOW_HEADERS).that(allowHeaders).isNotNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_HEADERS)
+ .that(Splitter.on(", ").splitToList(allowHeaders))
.containsExactlyElementsIn(
Stream.of(AUTHORIZATION, CONTENT_TYPE, "X-Gerrit-Auth", "X-Requested-With")
.map(s -> s.toLowerCase(Locale.US))
.collect(ImmutableSet.toImmutableSet()));
} else {
- assertThat(allowOrigin).named(ACCESS_CONTROL_ALLOW_ORIGIN).isNull();
- assertThat(allowCred).named(ACCESS_CONTROL_ALLOW_CREDENTIALS).isNull();
- assertThat(maxAge).named(ACCESS_CONTROL_MAX_AGE).isNull();
- assertThat(allowMethods).named(ACCESS_CONTROL_ALLOW_METHODS).isNull();
- assertThat(allowHeaders).named(ACCESS_CONTROL_ALLOW_HEADERS).isNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_ORIGIN).that(allowOrigin).isNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_CREDENTIALS).that(allowCred).isNull();
+ assertWithMessage(ACCESS_CONTROL_MAX_AGE).that(maxAge).isNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_METHODS).that(allowMethods).isNull();
+ assertWithMessage(ACCESS_CONTROL_ALLOW_HEADERS).that(allowHeaders).isNull();
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index bc675e539c..a1167ede8a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -15,10 +15,11 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.common.data.Permission.READ;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.eclipse.jgit.lib.Constants.SIGNED_OFF_BY_TAG;
import com.google.common.base.Strings;
@@ -27,7 +28,13 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.UseSystemTime;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -37,17 +44,14 @@ import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.MergeInput;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.submit.ChangeAlreadyMergedException;
import com.google.gerrit.testing.FakeEmailSender.Message;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
@@ -64,23 +68,13 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
import org.junit.Test;
+@UseClockStep
public class CreateChangeIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
- @BeforeClass
- public static void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @AfterClass
- public static void restoreTime() {
- TestTimeUtil.useSystemTime();
- }
-
@Test
public void createEmptyChange_MissingBranch() throws Exception {
ChangeInput ci = new ChangeInput();
@@ -135,6 +129,13 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
@Test
+ public void createNewChange_RequiresAuthentication() throws Exception {
+ requestScopeOperations.setApiUserAnonymous();
+ assertCreateFails(
+ newChangeInput(ChangeStatus.NEW), AuthException.class, "Authentication required");
+ }
+
+ @Test
public void createNewChange() throws Exception {
ChangeInfo info = assertCreateSucceeds(newChangeInput(ChangeStatus.NEW));
assertThat(info.revisions.get(info.currentRevision).commit.message)
@@ -162,6 +163,31 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
@Test
+ public void cannotCreateChangeWithChangeIfOfExistingChangeOnSameBranch() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ ChangeInput ci = newChangeInput(ChangeStatus.NEW);
+ ci.subject = "Subject\n\nChange-Id: " + changeId;
+ assertCreateFails(
+ ci,
+ ResourceConflictException.class,
+ "A change with Change-Id " + changeId + " already exists for this branch.");
+ }
+
+ @Test
+ public void canCreateChangeWithChangeIfOfExistingChangeOnOtherBranch() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ createBranch(BranchNameKey.create(project, "other"));
+
+ ChangeInput ci = newChangeInput(ChangeStatus.NEW);
+ ci.subject = "Subject\n\nChange-Id: " + changeId;
+ ci.branch = "other";
+ ChangeInfo info = assertCreateSucceeds(ci);
+ assertThat(info.changeId).isEqualTo(changeId);
+ }
+
+ @Test
public void notificationsOnChangeCreation() throws Exception {
requestScopeOperations.setApiUser(user.id());
watch(project.get());
@@ -300,7 +326,11 @@ public class CreateChangeIT extends AbstractDaemonTest {
public void createChangeWithoutAccessToParentCommitFails() throws Exception {
Map<String, PushOneCommit.Result> results =
changeInTwoBranches("invisible-branch", "a.txt", "visible-branch", "b.txt");
- block(project, "refs/heads/invisible-branch", READ, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(READ).ref("refs/heads/invisible-branch").group(REGISTERED_USERS))
+ .update();
ChangeInput in = newChangeInput(ChangeStatus.NEW);
in.branch = "visible-branch";
@@ -315,7 +345,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
RevCommit commit =
- rw.parseCommit(repo.exactRef(changeMetaRef(new Change.Id(c._number))).getObjectId());
+ rw.parseCommit(repo.exactRef(changeMetaRef(Change.id(c._number))).getObjectId());
assertThat(commit.getShortMessage()).isEqualTo("Create change");
@@ -379,8 +409,8 @@ public class CreateChangeIT extends AbstractDaemonTest {
// Create 2 branches.
String branchA = "branchA";
String branchB = "branchB";
- createBranch(new Branch.NameKey(project, branchA));
- createBranch(new Branch.NameKey(project, branchB));
+ createBranch(BranchNameKey.create(project, branchA));
+ createBranch(BranchNameKey.create(project, branchB));
// Push an octopus merge to both of the branches.
Result octopusA =
@@ -483,7 +513,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
cherry.current().review(ReviewInput.approve());
cherry.current().submit();
- ObjectId remoteId = getRemoteHead();
+ ObjectId remoteId = projectOperations.project(project).getHead("master");
assertThat(remoteId).isNotEqualTo(commitId);
ChangeInput in = newMergeChangeInput("master", commitId.getName(), "");
@@ -491,45 +521,13 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
@Test
- public void sha1sOfTwoNewChangesDiffer() throws Exception {
- ChangeInput changeInput = newChangeInput(ChangeStatus.NEW);
- ChangeInfo info1 = assertCreateSucceeds(changeInput);
- ChangeInfo info2 = assertCreateSucceeds(changeInput);
- assertThat(info1.currentRevision).isNotEqualTo(info2.currentRevision);
- assertThat(info1.changeId).isNotEqualTo(info2.changeId);
- }
-
- @Test
- public void sha1sOfTwoNewChangesDifferIfCreatedConcurrently() throws Exception {
- ExecutorService executor = Executors.newFixedThreadPool(2);
- try {
- for (int i = 0; i < 10; i++) {
- ChangeInput changeInput = newChangeInput(ChangeStatus.NEW);
-
- CyclicBarrier sync = new CyclicBarrier(2);
- Callable<ChangeInfo> createChange =
- () -> {
- requestScopeOperations.setApiUser(admin.id());
- sync.await();
- return assertCreateSucceeds(changeInput);
- };
-
- Future<ChangeInfo> changeInfo1 = executor.submit(createChange);
- Future<ChangeInfo> changeInfo2 = executor.submit(createChange);
- assertThat(changeInfo1.get().currentRevision)
- .isNotEqualTo(changeInfo2.get().currentRevision);
- assertThat(changeInfo1.get().changeId).isNotEqualTo(changeInfo2.get().changeId);
- }
- } finally {
- executor.shutdown();
- executor.awaitTermination(5, TimeUnit.SECONDS);
- }
- }
-
- @Test
public void createChangeOnExistingBranchNotPermitted() throws Exception {
- createBranch(new Branch.NameKey(project, "foo"));
- blockRead("refs/heads/*");
+ createBranch(BranchNameKey.create(project, "foo"));
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(READ).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
ChangeInput input = newChangeInput(ChangeStatus.NEW);
input.branch = "foo";
@@ -548,7 +546,11 @@ public class CreateChangeIT extends AbstractDaemonTest {
@Test
public void createChangeOnNonExistingBranchNotPermitted() throws Exception {
- blockRead("refs/heads/*");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(READ).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
ChangeInput input = newChangeInput(ChangeStatus.NEW);
input.branch = "foo";
@@ -568,6 +570,42 @@ public class CreateChangeIT extends AbstractDaemonTest {
input, BadRequestException.class, "Cannot create merge: destination branch does not exist");
}
+ @Test
+ @UseSystemTime
+ public void sha1sOfTwoNewChangesDiffer() throws Exception {
+ ChangeInput changeInput = newChangeInput(ChangeStatus.NEW);
+ ChangeInfo info1 = assertCreateSucceeds(changeInput);
+ ChangeInfo info2 = assertCreateSucceeds(changeInput);
+ assertThat(info1.currentRevision).isNotEqualTo(info2.currentRevision);
+ }
+
+ @Test
+ @UseSystemTime
+ public void sha1sOfTwoNewChangesDifferIfCreatedConcurrently() throws Exception {
+ ExecutorService executor = Executors.newFixedThreadPool(2);
+ try {
+ for (int i = 0; i < 10; i++) {
+ ChangeInput changeInput = newChangeInput(ChangeStatus.NEW);
+
+ CyclicBarrier sync = new CyclicBarrier(2);
+ Callable<ChangeInfo> createChange =
+ () -> {
+ requestScopeOperations.setApiUser(admin.id());
+ sync.await();
+ return assertCreateSucceeds(changeInput);
+ };
+
+ Future<ChangeInfo> changeInfo1 = executor.submit(createChange);
+ Future<ChangeInfo> changeInfo2 = executor.submit(createChange);
+ assertThat(changeInfo1.get().currentRevision)
+ .isNotEqualTo(changeInfo2.get().currentRevision);
+ }
+ } finally {
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ }
+ }
+
private ChangeInput newChangeInput(ChangeStatus status) {
ChangeInput in = new ChangeInput();
in.project = project.get();
@@ -604,9 +642,8 @@ public class CreateChangeIT extends AbstractDaemonTest {
private void assertCreateFails(
ChangeInput in, Class<? extends RestApiException> errType, String errSubstring)
throws Exception {
- exception.expect(errType);
- exception.expectMessage(errSubstring);
- gApi.changes().create(in);
+ Throwable thrown = assertThrows(errType, () -> gApi.changes().create(in));
+ assertThat(thrown).hasMessageThat().contains(errSubstring);
}
// TODO(davido): Expose setting of account preferences in the API
@@ -665,8 +702,8 @@ public class CreateChangeIT extends AbstractDaemonTest {
initialCommit.assertOkStatus();
// create two new branches
- createBranch(new Branch.NameKey(project, branchA));
- createBranch(new Branch.NameKey(project, branchB));
+ createBranch(BranchNameKey.create(project, branchA));
+ createBranch(BranchNameKey.create(project, branchB));
// create a commit in branchA
Result changeA =
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
index 286980f392..37fa2ce96b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
@@ -23,11 +23,11 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.testing.FakeEmailSender;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
@@ -104,6 +104,6 @@ public class DeleteVoteIT extends AbstractDaemonTest {
}
private Iterable<Account.Id> getReviewers(Collection<AccountInfo> r) {
- return Iterables.transform(r, a -> new Account.Id(a._accountId));
+ return Iterables.transform(r, a -> Account.id(a._accountId));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
index c13df497d7..c57a035a2b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
@@ -15,9 +15,11 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -25,29 +27,21 @@ import com.google.common.truth.IterableSubject;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.HashtagsInput;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
import org.junit.Test;
@NoHttpd
+@UseClockStep
public class HashtagsIT extends AbstractDaemonTest {
- @BeforeClass
- public static void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @AfterClass
- public static void restoreTime() {
- TestTimeUtil.useSystemTime();
- }
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@@ -78,9 +72,9 @@ public class HashtagsIT extends AbstractDaemonTest {
public void addInvalidHashtag() throws Exception {
PushOneCommit.Result r = createChange();
- exception.expect(BadRequestException.class);
- exception.expectMessage("hashtags may not contain commas");
- addHashtags(r, "invalid,hashtag");
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> addHashtags(r, "invalid,hashtag"));
+ assertThat(thrown).hasMessageThat().contains("hashtags may not contain commas");
}
@Test
@@ -258,15 +252,18 @@ public class HashtagsIT extends AbstractDaemonTest {
public void addHashtagWithoutPermissionNotAllowed() throws Exception {
PushOneCommit.Result r = createChange();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("edit hashtags not permitted");
- addHashtags(r, "MyHashtag");
+ AuthException thrown = assertThrows(AuthException.class, () -> addHashtags(r, "MyHashtag"));
+ assertThat(thrown).hasMessageThat().contains("edit hashtags not permitted");
}
@Test
public void addHashtagWithPermissionAllowed() throws Exception {
PushOneCommit.Result r = createChange();
- grant(project, "refs/heads/master", Permission.EDIT_HASHTAGS, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.EDIT_HASHTAGS).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
addHashtags(r, "MyHashtag");
assertThatGet(r).containsExactly("MyHashtag");
@@ -304,7 +301,7 @@ public class HashtagsIT extends AbstractDaemonTest {
private ChangeMessageInfo getLastMessage(PushOneCommit.Result r) throws Exception {
ChangeMessageInfo lastMessage =
Iterables.getLast(gApi.changes().id(r.getChange().getId().get()).get().messages, null);
- assertThat(lastMessage).named(lastMessage.message).isNotNull();
+ assertWithMessage(lastMessage.message).that(lastMessage).isNotNull();
return lastMessage;
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
index f49d1fb156..3030b02a6e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
@@ -15,6 +15,8 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -24,10 +26,9 @@ import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.project.testing.Util;
import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
@@ -48,7 +49,11 @@ public class IndexChangeIT extends AbstractDaemonTest {
@Test
public void indexChangeOnNonVisibleBranch() throws Exception {
String changeId = createChange().getChangeId();
- blockRead("refs/heads/master");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
userRestSession.post("/changes/" + changeId + "/index/").assertNotFound();
}
@@ -62,15 +67,15 @@ public class IndexChangeIT extends AbstractDaemonTest {
// Create a project and restrict its visibility to the group
Project.NameKey p = projectOperations.newProject().create();
- try (ProjectConfigUpdate u = updateProject(p)) {
- Util.allow(
- u.getConfig(),
- Permission.READ,
- groupCache.get(new AccountGroup.NameKey(group)).get().getGroupUUID(),
- "refs/*");
- Util.block(u.getConfig(), Permission.READ, REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(p)
+ .forUpdate()
+ .add(
+ allow(Permission.READ)
+ .ref("refs/*")
+ .group(groupCache.get(AccountGroup.nameKey(group)).get().getGroupUUID()))
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// Clone it and push a change as a regular user
TestRepository<InMemoryRepository> repo = cloneProject(p, user);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
index 2448ff881c..063f1a0656 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
@@ -16,17 +16,22 @@ package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.changes.MoveInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
@@ -35,10 +40,7 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.inject.Inject;
import java.util.Arrays;
import org.eclipse.jgit.junit.TestRepository;
@@ -48,15 +50,16 @@ import org.junit.Test;
@NoHttpd
public class MoveChangeIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Test
public void moveChangeWithShortRef() throws Exception {
// Move change to a different branch using short ref name
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch = new Branch.NameKey(r.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch = BranchNameKey.create(r.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
- move(r.getChangeId(), newBranch.getShortName());
+ move(r.getChangeId(), newBranch.shortName());
assertThat(r.getChange().change().getDest()).isEqualTo(newBranch);
}
@@ -64,9 +67,9 @@ public class MoveChangeIT extends AbstractDaemonTest {
public void moveChangeWithFullRef() throws Exception {
// Move change to a different branch using full ref name
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch = new Branch.NameKey(r.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch = BranchNameKey.create(r.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
- move(r.getChangeId(), newBranch.get());
+ move(r.getChangeId(), newBranch.branch());
assertThat(r.getChange().change().getDest()).isEqualTo(newBranch);
}
@@ -74,10 +77,10 @@ public class MoveChangeIT extends AbstractDaemonTest {
public void moveChangeWithMessage() throws Exception {
// Provide a message using --message flag
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch = new Branch.NameKey(r.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch = BranchNameKey.create(r.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
String moveMessage = "Moving for the move test";
- move(r.getChangeId(), newBranch.get(), moveMessage);
+ move(r.getChangeId(), newBranch.branch(), moveMessage);
assertThat(r.getChange().change().getDest()).isEqualTo(newBranch);
StringBuilder expectedMessage = new StringBuilder();
expectedMessage.append("Change destination moved from master to moveTest");
@@ -90,49 +93,59 @@ public class MoveChangeIT extends AbstractDaemonTest {
public void moveChangeToSameRefAsCurrent() throws Exception {
// Move change to the branch same as change's destination
PushOneCommit.Result r = createChange();
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Change is already destined for the specified branch");
- move(r.getChangeId(), r.getChange().change().getDest().get());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> move(r.getChangeId(), r.getChange().change().getDest().branch()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Change is already destined for the specified branch");
}
@Test
public void moveChangeToSameChangeId() throws Exception {
// Move change to a branch with existing change with same change ID
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch = new Branch.NameKey(r.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch = BranchNameKey.create(r.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
int changeNum = r.getChange().change().getChangeId();
- createChange(newBranch.get(), r.getChangeId());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- "Destination "
- + newBranch.getShortName()
- + " has a different change with same change key "
- + r.getChangeId());
- move(changeNum, newBranch.get());
+ createChange(newBranch.branch(), r.getChangeId());
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> move(changeNum, newBranch.branch()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "Destination "
+ + newBranch.shortName()
+ + " has a different change with same change key "
+ + r.getChangeId());
}
@Test
public void moveChangeToNonExistentRef() throws Exception {
// Move change to a non-existing branch
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch =
- new Branch.NameKey(r.getChange().change().getProject(), "does_not_exist");
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Destination " + newBranch.get() + " not found in the project");
- move(r.getChangeId(), newBranch.get());
+ BranchNameKey newBranch =
+ BranchNameKey.create(r.getChange().change().getProject(), "does_not_exist");
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> move(r.getChangeId(), newBranch.branch()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Destination " + newBranch.branch() + " not found in the project");
}
@Test
public void moveClosedChange() throws Exception {
// Move a change which is not open
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch = new Branch.NameKey(r.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch = BranchNameKey.create(r.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
merge(r);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Change is merged");
- move(r.getChangeId(), newBranch.get());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> move(r.getChangeId(), newBranch.branch()));
+ assertThat(thrown).hasMessageThat().contains("Change is merged");
}
@Test
@@ -153,43 +166,51 @@ public class MoveChangeIT extends AbstractDaemonTest {
pushHead(testRepo, "refs/for/master", false, false);
// Try to move the merge commit to another branch
- Branch.NameKey newBranch = new Branch.NameKey(r1.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch =
+ BranchNameKey.create(r1.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Merge commit cannot be moved");
- move(GitUtil.getChangeId(testRepo, c).get(), newBranch.get());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> move(GitUtil.getChangeId(testRepo, c).get(), newBranch.branch()));
+ assertThat(thrown).hasMessageThat().contains("Merge commit cannot be moved");
}
@Test
public void moveChangeToBranchWithoutUploadPerms() throws Exception {
// Move change to a destination where user doesn't have upload permissions
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch =
- new Branch.NameKey(r.getChange().change().getProject(), "blocked_branch");
+ BranchNameKey newBranch =
+ BranchNameKey.create(r.getChange().change().getProject(), "blocked_branch");
createBranch(newBranch);
- block(
- "refs/for/" + newBranch.get(),
- Permission.PUSH,
- systemGroupBackend.getGroup(REGISTERED_USERS).getUUID());
- exception.expect(AuthException.class);
- exception.expectMessage("move not permitted");
- move(r.getChangeId(), newBranch.get());
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.PUSH).ref("refs/for/" + newBranch.branch()).group(REGISTERED_USERS))
+ .update();
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> move(r.getChangeId(), newBranch.branch()));
+ assertThat(thrown).hasMessageThat().contains("move not permitted");
}
@Test
public void moveChangeFromBranchWithoutAbandonPerms() throws Exception {
// Move change for which user does not have abandon permissions
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch = new Branch.NameKey(r.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch = BranchNameKey.create(r.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
- block(
- r.getChange().change().getDest().get(),
- Permission.ABANDON,
- systemGroupBackend.getGroup(REGISTERED_USERS).getUUID());
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ block(Permission.ABANDON)
+ .ref(r.getChange().change().getDest().branch())
+ .group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("move not permitted");
- move(r.getChangeId(), newBranch.get());
+ AuthException thrown =
+ assertThrows(AuthException.class, () -> move(r.getChangeId(), newBranch.branch()));
+ assertThat(thrown).hasMessageThat().contains("move not permitted");
}
@Test
@@ -202,52 +223,56 @@ public class MoveChangeIT extends AbstractDaemonTest {
int changeNum = r.getChange().change().getChangeId();
// Create a branch with that same commit
- Branch.NameKey newBranch = new Branch.NameKey(r.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch = BranchNameKey.create(r.getChange().change().getProject(), "moveTest");
BranchInput bi = new BranchInput();
bi.revision = r.getCommit().name();
- gApi.projects().name(newBranch.getParentKey().get()).branch(newBranch.get()).create(bi);
+ gApi.projects().name(newBranch.project().get()).branch(newBranch.branch()).create(bi);
// Try to move the change to the branch with the same commit
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- "Current patchset revision is reachable from tip of " + newBranch.get());
- move(changeNum, newBranch.get());
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> move(changeNum, newBranch.branch()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Current patchset revision is reachable from tip of " + newBranch.branch());
}
@Test
public void moveChangeWithCurrentPatchSetLocked() throws Exception {
// Move change that is locked
PushOneCommit.Result r = createChange();
- Branch.NameKey newBranch = new Branch.NameKey(r.getChange().change().getProject(), "moveTest");
+ BranchNameKey newBranch = BranchNameKey.create(r.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
+ LabelType patchSetLock = TestLabels.patchSetLock();
try (ProjectConfigUpdate u = updateProject(project)) {
- LabelType patchSetLock = Util.patchSetLock();
u.getConfig().getLabelSections().put(patchSetLock.getName(), patchSetLock);
- AccountGroup.UUID registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
- Util.allow(
- u.getConfig(),
- Permission.forLabel(patchSetLock.getName()),
- 0,
- 1,
- registeredUsers,
- "refs/heads/*");
u.save();
}
- grant(project, "refs/heads/*", Permission.LABEL + "Patch-Set-Lock");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(
+ allowLabel(patchSetLock.getName())
+ .ref("refs/heads/*")
+ .group(REGISTERED_USERS)
+ .range(0, 1))
+ .update();
revision(r).review(new ReviewInput().label("Patch-Set-Lock", 1));
- exception.expect(ResourceConflictException.class);
- exception.expectMessage(
- String.format("The current patch set of change %s is locked", r.getChange().getId()));
- move(r.getChangeId(), newBranch.get());
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class, () -> move(r.getChangeId(), newBranch.branch()));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ String.format("The current patch set of change %s is locked", r.getChange().getId()));
}
@Test
public void moveChangeOnlyKeepVetoVotes() throws Exception {
// A vote for a label will be kept after moving if the label's function is *WithBlock and the
// vote holds the minimum value.
- createBranch(new Branch.NameKey(project, "foo"));
+ createBranch(BranchNameKey.create(project, "foo"));
String codeReviewLabel = "Code-Review"; // 'Code-Review' uses 'MaxWithBlock' function.
String testLabelA = "Label-A";
@@ -257,16 +282,13 @@ public class MoveChangeIT extends AbstractDaemonTest {
configLabel(testLabelB, LabelFunction.MAX_NO_BLOCK);
configLabel(testLabelC, LabelFunction.NO_BLOCK);
- AccountGroup.UUID registered = SystemGroupBackend.REGISTERED_USERS;
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(
- u.getConfig(), Permission.forLabel(testLabelA), -1, +1, registered, "refs/heads/*");
- Util.allow(
- u.getConfig(), Permission.forLabel(testLabelB), -1, +1, registered, "refs/heads/*");
- Util.allow(
- u.getConfig(), Permission.forLabel(testLabelC), -1, +1, registered, "refs/heads/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(testLabelA).ref("refs/heads/*").group(REGISTERED_USERS).range(-1, +1))
+ .add(allowLabel(testLabelB).ref("refs/heads/*").group(REGISTERED_USERS).range(-1, +1))
+ .add(allowLabel(testLabelC).ref("refs/heads/*").group(REGISTERED_USERS).range(-1, +1))
+ .update();
String changeId = createChange().getChangeId();
gApi.changes().id(changeId).current().review(ReviewInput.reject());
@@ -301,17 +323,16 @@ public class MoveChangeIT extends AbstractDaemonTest {
}
@Test
- public void moveToBranchWithoutLabel() throws Exception {
- createBranch(new Branch.NameKey(project, "foo"));
+ public void moveToBranchThatDoesNotHaveCustomLabel() throws Exception {
+ createBranch(BranchNameKey.create(project, "foo"));
String testLabelA = "Label-A";
configLabel(testLabelA, LabelFunction.MAX_WITH_BLOCK, Arrays.asList("refs/heads/master"));
- AccountGroup.UUID registered = SystemGroupBackend.REGISTERED_USERS;
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(
- u.getConfig(), Permission.forLabel(testLabelA), -1, +1, registered, "refs/heads/master");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(testLabelA).ref("refs/heads/master").group(REGISTERED_USERS).range(-1, +1))
+ .update();
String changeId = createChange().getChangeId();
@@ -326,16 +347,26 @@ public class MoveChangeIT extends AbstractDaemonTest {
move(changeId, "foo");
- // TODO(dpursehouse): Assert about state of labels after move
+ // "foo" branch does not have the custom label
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().keySet())
+ .isEmpty();
+
+ // Move back to master and confirm that the custom label score is still there
+ move(changeId, "master");
+
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().keySet())
+ .containsExactly(testLabelA);
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
+ .containsExactly((short) -1);
}
@Test
public void moveNoDestinationBranchSpecified() throws Exception {
PushOneCommit.Result r = createChange();
- exception.expect(BadRequestException.class);
- exception.expectMessage("destination branch is required");
- move(r.getChangeId(), null);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> move(r.getChangeId(), null));
+ assertThat(thrown).hasMessageThat().contains("destination branch is required");
}
@Test
@@ -343,9 +374,9 @@ public class MoveChangeIT extends AbstractDaemonTest {
public void moveCanBeDisabledByConfig() throws Exception {
PushOneCommit.Result r = createChange();
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("move changes endpoint is disabled");
- move(r.getChangeId(), null);
+ MethodNotAllowedException thrown =
+ assertThrows(MethodNotAllowedException.class, () -> move(r.getChangeId(), null));
+ assertThat(thrown).hasMessageThat().contains("move changes endpoint is disabled");
}
private void move(int changeNum, String destination) throws RestApiException {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java
index 649c7ae107..76493161a9 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java
@@ -21,8 +21,8 @@ import com.google.common.collect.ImmutableListMultimap;
import com.google.gerrit.acceptance.AbstractPluginFieldsTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.json.OutputFormat;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.util.List;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java b/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
index ca4288f636..1a3c10f75f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
@@ -15,21 +15,21 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
@@ -81,9 +81,9 @@ public class PrivateByDefaultIT extends AbstractDaemonTest {
setPrivateByDefault(project2, InheritableBoolean.TRUE);
ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("private changes are disabled");
- gApi.changes().create(input);
+ MethodNotAllowedException thrown =
+ assertThrows(MethodNotAllowedException.class, () -> gApi.changes().create(input));
+ assertThat(thrown).hasMessageThat().contains("private changes are disabled");
}
@Test
@@ -125,22 +125,6 @@ public class PrivateByDefaultIT extends AbstractDaemonTest {
result.assertErrorStatus();
}
- @Test
- @GerritConfig(name = "change.disablePrivateChanges", value = "true")
- public void pushDraftsWithPrivateByDefaultAndDisablePrivateChangesTrue() throws Exception {
- setPrivateByDefault(project2, InheritableBoolean.TRUE);
-
- RevCommit initialHead = getRemoteHead(project2, "master");
- TestRepository<InMemoryRepository> testRepo = cloneProject(project2);
- PushOneCommit.Result result =
- pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
- result.assertErrorStatus();
-
- testRepo.reset(initialHead);
- result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
- result.assertErrorStatus();
- }
-
private void setPrivateByDefault(Project.NameKey proj, InheritableBoolean value)
throws Exception {
ConfigInput input = new ConfigInput();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
index 2ad4acabc6..6f519f1d40 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -19,8 +19,11 @@ import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVI
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.client.ChangeStatus;
@@ -28,9 +31,6 @@ import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
-import com.google.gerrit.server.git.ChangeMessageModifier;
import com.google.gerrit.server.submit.CommitMergeStatus;
import com.google.inject.Inject;
import java.util.List;
@@ -39,7 +39,8 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class SubmitByCherryPickIT extends AbstractSubmit {
- @Inject private DynamicSet<ChangeMessageModifier> changeMessageModifiers;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
@Override
protected SubmitType getSubmitType() {
@@ -47,12 +48,12 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void submitWithCherryPickIfFastForwardPossible() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithCherryPickIfFastForwardPossible() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
submit(change.getChangeId());
assertCherryPick(testRepo, false);
- RevCommit newHead = getRemoteHead();
+ RevCommit newHead = projectOperations.project(project).getHead("master");
assertThat(newHead.getParent(0)).isEqualTo(change.getCommit().getParent(0));
assertRefUpdatedEvents(initialHead, newHead);
@@ -60,17 +61,17 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void submitWithCherryPick() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithCherryPick() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
submit(change2.getChangeId());
assertCherryPick(testRepo, false);
- RevCommit newHead = getRemoteHead();
+ RevCommit newHead = projectOperations.project(project).getHead("master");
assertThat(newHead.getParentCount()).isEqualTo(1);
assertThat(newHead.getParent(0)).isEqualTo(headAfterFirstSubmit);
assertCurrentRevision(change2.getChangeId(), 2, newHead);
@@ -85,17 +86,15 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void changeMessageOnSubmit() throws Exception {
+ public void changeMessageOnSubmit() throws Throwable {
PushOneCommit.Result change = createChange();
- RegistrationHandle handle =
- changeMessageModifiers.add(
- "gerrit",
- (newCommitMessage, original, mergeTip, destination) ->
- newCommitMessage + "Custom: " + destination.get());
- try {
+ try (Registration registration =
+ extensionRegistry
+ .newRegistration()
+ .add(
+ (newCommitMessage, original, mergeTip, destination) ->
+ newCommitMessage + "Custom: " + destination.branch())) {
submit(change.getChangeId());
- } finally {
- handle.remove();
}
testRepo.git().fetch().setRemote("origin").call();
ChangeInfo info = get(change.getChangeId(), CURRENT_REVISION);
@@ -107,20 +106,20 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithContentMerge() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithContentMerge() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "aaa\nbbb\nccc\n");
submit(change.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n");
submit(change2.getChangeId());
- RevCommit headAfterSecondSubmit = getRemoteHead();
+ RevCommit headAfterSecondSubmit = projectOperations.project(project).getHead("master");
testRepo.reset(change.getCommit());
PushOneCommit.Result change3 = createChange("Change 3", "a.txt", "bbb\nccc\n");
submit(change3.getChangeId());
assertCherryPick(testRepo, true);
- RevCommit headAfterThirdSubmit = getRemoteHead();
+ RevCommit headAfterThirdSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterThirdSubmit.getParent(0)).isEqualTo(headAfterSecondSubmit);
assertApproved(change3.getChangeId());
assertCurrentRevision(change3.getChangeId(), 2, headAfterThirdSubmit);
@@ -145,12 +144,12 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithContentMerge_Conflict() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithContentMerge_Conflict() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit newHead = getRemoteHead();
+ RevCommit newHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "other content");
submitWithConflict(
@@ -162,7 +161,7 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
+ "merged due to a path conflict. Please rebase the change locally and "
+ "upload the rebased commit for review.");
- assertThat(getRemoteHead()).isEqualTo(newHead);
+ assertThat(projectOperations.project(project).getHead("master")).isEqualTo(newHead);
assertCurrentRevision(change2.getChangeId(), 1, change2.getCommit());
assertNoSubmitter(change2.getChangeId(), 1);
@@ -171,18 +170,18 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void submitOutOfOrder() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitOutOfOrder() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
createChange("Change 2", "b.txt", "other content");
PushOneCommit.Result change3 = createChange("Change 3", "c.txt", "different content");
submit(change3.getChangeId());
assertCherryPick(testRepo, false);
- RevCommit headAfterSecondSubmit = getRemoteHead();
+ RevCommit headAfterSecondSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterSecondSubmit.getParent(0)).isEqualTo(headAfterFirstSubmit);
assertApproved(change3.getChangeId());
assertCurrentRevision(change3.getChangeId(), 2, headAfterSecondSubmit);
@@ -199,12 +198,12 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void submitOutOfOrder_Conflict() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitOutOfOrder_Conflict() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit newHead = getRemoteHead();
+ RevCommit newHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
createChange("Change 2", "b.txt", "other content");
PushOneCommit.Result change3 = createChange("Change 3", "b.txt", "different content");
@@ -217,7 +216,7 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
+ "merged due to a path conflict. Please rebase the change locally and "
+ "upload the rebased commit for review.");
- assertThat(getRemoteHead()).isEqualTo(newHead);
+ assertThat(projectOperations.project(project).getHead("master")).isEqualTo(newHead);
assertCurrentRevision(change3.getChangeId(), 1, change3.getCommit());
assertNoSubmitter(change3.getChangeId(), 1);
@@ -226,8 +225,8 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void submitMultipleChanges() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitMultipleChanges() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change = createChange("Change 1", "b", "b");
@@ -254,8 +253,8 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void submitDependentNonConflictingChangesOutOfOrder() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitDependentNonConflictingChangesOutOfOrder() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change = createChange("Change 1", "b", "b");
@@ -264,11 +263,11 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
// Submit succeeds; change2 is successfully cherry-picked onto head.
submit(change2.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
// Submit succeeds; change is successfully cherry-picked onto head
// (which was change2's cherry-pick).
submit(change.getChangeId());
- RevCommit headAfterSecondSubmit = getRemoteHead();
+ RevCommit headAfterSecondSubmit = projectOperations.project(project).getHead("master");
// change is the new tip.
List<RevCommit> log = getRemoteLog();
@@ -290,8 +289,8 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void submitDependentConflictingChangesOutOfOrder() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitDependentConflictingChangesOutOfOrder() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change = createChange("Change 1", "b", "b1");
@@ -322,8 +321,8 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
}
@Test
- public void submitSubsetOfDependentChanges() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitSubsetOfDependentChanges() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change = createChange("Change 1", "b", "b");
@@ -334,7 +333,7 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
// related to change 3 by topic or ancestor (due to cherrypicking!)
approve(change2.getChangeId());
submit(change3.getChangeId());
- RevCommit newHead = getRemoteHead();
+ RevCommit newHead = projectOperations.project(project).getHead("master");
assertNew(change.getChangeId());
assertNew(change2.getChangeId());
@@ -345,8 +344,8 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitIdenticalTree() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitIdenticalTree() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change1 = createChange("Change 1", "a.txt", "a");
@@ -354,12 +353,13 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "a");
submit(change1.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterFirstSubmit.getShortMessage()).isEqualTo("Change 1");
submit(change2.getChangeId(), new SubmitInput(), null, null);
- assertThat(getRemoteHead()).isEqualTo(headAfterFirstSubmit);
+ assertThat(projectOperations.project(project).getHead("master"))
+ .isEqualTo(headAfterFirstSubmit);
ChangeInfo info2 = get(change2.getChangeId(), MESSAGES);
assertThat(info2.status).isEqualTo(ChangeStatus.MERGED);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
index 974180c83f..670cff2c96 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
@@ -16,19 +16,23 @@ package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ActionInfo;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.inject.Inject;
import java.util.Map;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.PushResult;
import org.junit.Test;
public class SubmitByFastForwardIT extends AbstractSubmit {
+ @Inject private ProjectOperations projectOperations;
@Override
protected SubmitType getSubmitType() {
@@ -36,11 +40,11 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
}
@Test
- public void submitWithFastForward() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithFastForward() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
submit(change.getChangeId());
- RevCommit updatedHead = getRemoteHead();
+ RevCommit updatedHead = projectOperations.project(project).getHead("master");
assertThat(updatedHead.getId()).isEqualTo(change.getCommit());
assertThat(updatedHead.getParent(0)).isEqualTo(initialHead);
assertSubmitter(change.getChangeId(), 1);
@@ -50,8 +54,8 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
}
@Test
- public void submitMultipleChangesWithFastForward() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitMultipleChangesWithFastForward() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
PushOneCommit.Result change2 = createChange();
@@ -64,7 +68,7 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
approve(id2);
submit(id3);
- RevCommit updatedHead = getRemoteHead();
+ RevCommit updatedHead = projectOperations.project(project).getHead("master");
assertThat(updatedHead.getId()).isEqualTo(change3.getCommit());
assertThat(updatedHead.getParent(0).getId()).isEqualTo(change2.getCommit());
assertSubmitter(change.getChangeId(), 1);
@@ -82,12 +86,12 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
}
@Test
- public void submitTwoChangesWithFastForward_missingDependency() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitTwoChangesWithFastForward_missingDependency() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change1 = createChange();
PushOneCommit.Result change2 = createChange();
- Change.Id id1 = change1.getPatchSetId().getParentKey();
+ Change.Id id1 = change1.getPatchSetId().changeId();
submitWithConflict(
change2.getChangeId(),
"Failed to submit 2 changes due to the following problems:\n"
@@ -95,19 +99,19 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
+ id1
+ ": needs Code-Review");
- RevCommit updatedHead = getRemoteHead();
+ RevCommit updatedHead = projectOperations.project(project).getHead("master");
assertThat(updatedHead.getId()).isEqualTo(initialHead.getId());
assertRefUpdatedEvents();
assertChangeMergedEvents();
}
@Test
- public void submitFastForwardNotPossible_Conflict() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitFastForwardNotPossible_Conflict() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
@@ -128,7 +132,8 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
+ ": Project policy requires "
+ "all submissions to be a fast-forward. Please rebase the change "
+ "locally and upload again for review.");
- assertThat(getRemoteHead()).isEqualTo(headAfterFirstSubmit);
+ assertThat(projectOperations.project(project).getHead("master"))
+ .isEqualTo(headAfterFirstSubmit);
assertSubmitter(change.getChangeId(), 1);
assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
@@ -136,11 +141,15 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
}
@Test
- public void submitSameCommitsAsInExperimentalBranch() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitSameCommitsAsInExperimentalBranch() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
- grant(project, "refs/heads/*", Permission.CREATE);
- grant(project, "refs/heads/experimental", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref("refs/heads/*").group(adminGroupUuid()))
+ .add(allow(Permission.PUSH).ref("refs/heads/experimental").group(adminGroupUuid()))
+ .update();
RevCommit c1 = commitBuilder().add("b.txt", "1").message("commit at tip").create();
String id1 = GitUtil.getChangeId(testRepo, c1).get();
@@ -153,9 +162,9 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
.isEqualTo(c1.getId());
submit(id1);
- RevCommit headAfterSubmit = getRemoteHead();
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
- assertThat(getRemoteHead().getId()).isEqualTo(c1.getId());
+ assertThat(projectOperations.project(project).getHead("master").getId()).isEqualTo(c1.getId());
assertSubmitter(id1, 1);
assertRefUpdatedEvents(initialHead, headAfterSubmit);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
index 9bc5a2f592..f80bdcadab 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
@@ -17,11 +17,14 @@ package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.extensions.client.SubmitType;
+import com.google.inject.Inject;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class SubmitByMergeAlwaysIT extends AbstractSubmitByMerge {
+ @Inject private ProjectOperations projectOperations;
@Override
protected SubmitType getSubmitType() {
@@ -29,11 +32,11 @@ public class SubmitByMergeAlwaysIT extends AbstractSubmitByMerge {
}
@Test
- public void submitWithMergeIfFastForwardPossible() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithMergeIfFastForwardPossible() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
submit(change.getChangeId());
- RevCommit headAfterSubmit = getRemoteHead();
+ RevCommit headAfterSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterSubmit.getParentCount()).isEqualTo(2);
assertThat(headAfterSubmit.getParent(0)).isEqualTo(initialHead);
assertThat(headAfterSubmit.getParent(1)).isEqualTo(change.getCommit());
@@ -46,8 +49,8 @@ public class SubmitByMergeAlwaysIT extends AbstractSubmitByMerge {
}
@Test
- public void submitMultipleChanges() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitMultipleChanges() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
// Submit a change so that the remote head advances
PushOneCommit.Result change = createChange("Change 1", "b", "b");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
index 5ebcd85ae3..b259d90b54 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
@@ -16,14 +16,22 @@ package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.common.data.Permission.READ;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -33,9 +41,6 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import java.io.File;
import java.io.InputStream;
@@ -63,11 +68,11 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void submitWithFastForward() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithFastForward() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
submit(change.getChangeId());
- RevCommit updatedHead = getRemoteHead();
+ RevCommit updatedHead = projectOperations.project(project).getHead("master");
assertThat(updatedHead.getId()).isEqualTo(change.getCommit());
assertThat(updatedHead.getParent(0)).isEqualTo(initialHead);
assertSubmitter(change.getChangeId(), 1);
@@ -79,8 +84,8 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void submitMultipleChanges() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitMultipleChanges() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo.reset(initialHead);
PushOneCommit.Result change = createChange("Change 1", "b", "b");
@@ -136,13 +141,13 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void submitChangesAcrossRepos() throws Exception {
+ public void submitChangesAcrossRepos() throws Throwable {
Project.NameKey p1 = projectOperations.newProject().create();
Project.NameKey p2 = projectOperations.newProject().create();
Project.NameKey p3 = projectOperations.newProject().create();
- RevCommit initialHead2 = getRemoteHead(p2, "master");
- RevCommit initialHead3 = getRemoteHead(p3, "master");
+ RevCommit initialHead2 = projectOperations.project(p2).getHead("master");
+ RevCommit initialHead3 = projectOperations.project(p3).getHead("master");
TestRepository<?> repo1 = cloneProject(p1);
TestRepository<?> repo2 = cloneProject(p2);
@@ -180,7 +185,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
approve(change3.getChangeId());
// get a preview before submitting:
- Map<Branch.NameKey, ObjectId> preview = fetchFromSubmitPreview(change1b.getChangeId());
+ Map<BranchNameKey, ObjectId> preview = fetchFromSubmitPreview(change1b.getChangeId());
submit(change1b.getChangeId());
RevCommit tip1 = getRemoteLog(p1, "master").get(0);
@@ -196,24 +201,24 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
// check that the preview matched what happened:
assertThat(preview).hasSize(3);
- assertThat(preview).containsKey(new Branch.NameKey(p1, "refs/heads/master"));
+ assertThat(preview).containsKey(BranchNameKey.create(p1, "refs/heads/master"));
assertTrees(p1, preview);
- assertThat(preview).containsKey(new Branch.NameKey(p2, "refs/heads/master"));
+ assertThat(preview).containsKey(BranchNameKey.create(p2, "refs/heads/master"));
assertTrees(p2, preview);
- assertThat(preview).containsKey(new Branch.NameKey(p3, "refs/heads/master"));
+ assertThat(preview).containsKey(BranchNameKey.create(p3, "refs/heads/master"));
assertTrees(p3, preview);
} else {
assertThat(tip2.getShortMessage()).isEqualTo(initialHead2.getShortMessage());
assertThat(tip3.getShortMessage()).isEqualTo(initialHead3.getShortMessage());
assertThat(preview).hasSize(1);
- assertThat(preview.get(new Branch.NameKey(p1, "refs/heads/master"))).isNotNull();
+ assertThat(preview.get(BranchNameKey.create(p1, "refs/heads/master"))).isNotNull();
}
}
@Test
- public void submitChangesAcrossReposBlocked() throws Exception {
+ public void submitChangesAcrossReposBlocked() throws Throwable {
Project.NameKey p1 = projectOperations.newProject().create();
Project.NameKey p2 = projectOperations.newProject().create();
Project.NameKey p3 = projectOperations.newProject().create();
@@ -222,9 +227,9 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
TestRepository<?> repo2 = cloneProject(p2);
TestRepository<?> repo3 = cloneProject(p3);
- RevCommit initialHead1 = getRemoteHead(p1, "master");
- RevCommit initialHead2 = getRemoteHead(p2, "master");
- RevCommit initialHead3 = getRemoteHead(p3, "master");
+ RevCommit initialHead1 = projectOperations.project(p1).getHead("master");
+ RevCommit initialHead2 = projectOperations.project(p2).getHead("master");
+ RevCommit initialHead3 = projectOperations.project(p3).getHead("master");
PushOneCommit.Result change1a =
createChange(
@@ -277,15 +282,12 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
+ "and upload the rebased commit for review.";
// Get a preview before submitting:
- try (BinaryResult r = gApi.changes().id(change1b.getChangeId()).current().submitPreview()) {
- // We cannot just use the ExpectedException infrastructure as provided
- // by AbstractDaemonTest, as then we'd stop early and not test the
- // actual submit.
-
- fail("expected failure");
- } catch (RestApiException e) {
- assertThat(e.getMessage()).isEqualTo(msg);
- }
+ RestApiException thrown =
+ assertThrows(
+ RestApiException.class,
+ () -> gApi.changes().id(change1b.getChangeId()).current().submitPreview().close());
+ assertThat(thrown.getMessage()).isEqualTo(msg);
+
submitWithConflict(change1b.getChangeId(), msg);
} else {
submit(change1b.getChangeId());
@@ -313,13 +315,13 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void submitWithMergedAncestorsOnOtherBranch() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithMergedAncestorsOnOtherBranch() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change1 =
createChange(testRepo, "master", "base commit", "a.txt", "1", "");
submit(change1.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
gApi.projects().name(project.get()).branch("branch").create(new BranchInput());
@@ -362,12 +364,12 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void submitWithOpenAncestorsOnOtherBranch() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithOpenAncestorsOnOtherBranch() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change1 =
createChange(testRepo, "master", "base commit", "a.txt", "1", "");
submit(change1.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
gApi.projects().name(project.get()).branch("branch").create(new BranchInput());
@@ -395,7 +397,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
Project.NameKey p3 = projectOperations.newProject().create();
TestRepository<?> repo3 = cloneProject(p3);
- RevCommit repo3Head = getRemoteHead(p3, "master");
+ RevCommit repo3Head = projectOperations.project(p3).getHead("master");
PushOneCommit.Result change3b =
createChange(
repo3,
@@ -435,8 +437,8 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void gerritWorkflow() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void gerritWorkflow() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
// We'll setup a master and a stable branch.
// Then we create a change to be applied to master, which is
@@ -462,8 +464,8 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
gApi.changes().id(cherryId).current().submit();
// Create the merge locally
- RevCommit stable = getRemoteHead(project, "stable");
- RevCommit master = getRemoteHead(project, "master");
+ RevCommit stable = projectOperations.project(project).getHead("stable");
+ RevCommit master = projectOperations.project(project).getHead("master");
testRepo.git().fetch().call();
testRepo.git().branchCreate().setName("stable").setStartPoint(stable).call();
testRepo.git().branchCreate().setName("master").setStartPoint(master).call();
@@ -493,7 +495,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void openChangeForTargetBranchPreventsMerge() throws Exception {
+ public void openChangeForTargetBranchPreventsMerge() throws Throwable {
gApi.projects().name(project.get()).branch("stable").create(new BranchInput());
// Propose a change for master, but leave it open for master!
@@ -517,7 +519,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
change3.getChangeId(),
"Failed to submit 1 change due to the following problems:\n"
+ "Change "
- + change3.getPatchSetId().getParentKey().get()
+ + change3.getPatchSetId().changeId().get()
+ ": Depends on change that was not submitted."
+ " Commit "
+ change3.getCommit().name()
@@ -532,7 +534,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void dependencyOnOutdatedPatchSetPreventsMerge() throws Exception {
+ public void dependencyOnOutdatedPatchSetPreventsMerge() throws Throwable {
// Create a change
PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "fix", "a.txt", "foo");
PushOneCommit.Result changeResult = change.to("refs/for/master");
@@ -574,7 +576,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void dependencyOnDeletedChangePreventsMerge() throws Exception {
+ public void dependencyOnDeletedChangePreventsMerge() throws Throwable {
// Create a change
PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "fix", "a.txt", "foo");
PushOneCommit.Result changeResult = change.to("refs/for/master");
@@ -608,9 +610,13 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void dependencyOnChangeForNonVisibleBranchPreventsMerge() throws Exception {
- grantLabel("Code-Review", -2, 2, project, "refs/heads/*", false, REGISTERED_USERS, false);
- grant(project, "refs/*", Permission.SUBMIT, false, REGISTERED_USERS);
+ public void dependencyOnChangeForNonVisibleBranchPreventsMerge() throws Throwable {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, 2))
+ .add(allow(Permission.SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// Create a change
PushOneCommit change = pushFactory.create(admin.newIdent(), testRepo, "fix", "a.txt", "foo");
@@ -624,23 +630,26 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
// Move the first change to a destination branch that is non-visible to user so that user cannot
// this change anymore.
- Branch.NameKey secretBranch = new Branch.NameKey(project, "secretBranch");
+ BranchNameKey secretBranch = BranchNameKey.create(project, "secretBranch");
gApi.projects()
- .name(secretBranch.getParentKey().get())
- .branch(secretBranch.get())
+ .name(secretBranch.project().get())
+ .branch(secretBranch.branch())
.create(new BranchInput());
- gApi.changes().id(changeResult.getChangeId()).move(secretBranch.get());
- block(secretBranch.get(), "read", ANONYMOUS_USERS);
+ gApi.changes().id(changeResult.getChangeId()).move(secretBranch.branch());
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(READ).ref(secretBranch.branch()).group(ANONYMOUS_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
// Verify that user cannot see the first change.
- try {
- gApi.changes().id(changeResult.getChangeId()).get();
- fail("expected failure");
- } catch (ResourceNotFoundException e) {
- assertThat(e.getMessage()).isEqualTo("Not found: " + changeResult.getChangeId());
- }
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(changeResult.getChangeId()).get());
+ assertThat(thrown).hasMessageThat().isEqualTo("Not found: " + changeResult.getChangeId());
// Submit is expected to fail.
submitWithConflict(
@@ -663,9 +672,13 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
}
@Test
- public void dependencyOnHiddenChangePreventsMerge() throws Exception {
- grantLabel("Code-Review", -2, 2, project, "refs/heads/*", false, REGISTERED_USERS, false);
- grant(project, "refs/*", Permission.SUBMIT, false, REGISTERED_USERS);
+ public void dependencyOnHiddenChangePreventsMerge() throws Throwable {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, 2))
+ .add(allow(Permission.SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .update();
// Create a change
PushOneCommit change = pushFactory.create(admin.newIdent(), testRepo, "fix", "a.txt", "foo");
@@ -684,30 +697,29 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
requestScopeOperations.setApiUser(user.id());
// Verify that user cannot see the first change.
- try {
- gApi.changes().id(changeResult.getChangeId()).get();
- fail("expected failure");
- } catch (ResourceNotFoundException e) {
- assertThat(e.getMessage()).isEqualTo("Not found: " + changeResult.getChangeId());
- }
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.changes().id(changeResult.getChangeId()).get());
+ assertThat(thrown).hasMessageThat().isEqualTo("Not found: " + changeResult.getChangeId());
// Submit is expected to fail.
- try {
- gApi.changes().id(change2Result.getChangeId()).current().submit();
- fail("expected failure");
- } catch (AuthException e) {
- assertThat(e.getMessage())
- .isEqualTo(
- "A change to be submitted with "
- + change2Result.getChange().getId().id
- + " is not visible");
- }
+ AuthException thrown2 =
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(change2Result.getChangeId()).current().submit());
+ assertThat(thrown2)
+ .hasMessageThat()
+ .isEqualTo(
+ "A change to be submitted with "
+ + change2Result.getChange().getId().get()
+ + " is not visible");
assertRefUpdatedEvents();
assertChangeMergedEvents();
}
@Test
- public void dependencyOnHiddenChangeUsingTopicPreventsMerge() throws Exception {
+ public void dependencyOnHiddenChangeUsingTopicPreventsMerge() throws Throwable {
// Construct a topic where a change included by topic depends on a private change that is not
// visible to the submitting user
// (c1) --- topic --- (c2b)
@@ -718,10 +730,18 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
Project.NameKey p1 = projectOperations.newProject().create();
Project.NameKey p2 = projectOperations.newProject().create();
- grantLabel("Code-Review", -2, 2, p1, "refs/heads/*", false, REGISTERED_USERS, false);
- grant(p1, "refs/*", Permission.SUBMIT, false, REGISTERED_USERS);
- grantLabel("Code-Review", -2, 2, p2, "refs/heads/*", false, REGISTERED_USERS, false);
- grant(p2, "refs/*", Permission.SUBMIT, false, REGISTERED_USERS);
+ projectOperations
+ .project(p1)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, 2))
+ .add(allow(Permission.SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(p2)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, 2))
+ .add(allow(Permission.SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .update();
TestRepository<?> repo1 = cloneProject(p1);
TestRepository<?> repo2 = cloneProject(p2);
@@ -744,30 +764,27 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
requestScopeOperations.setApiUser(user.id());
// Verify that user cannot see change2a
- try {
- gApi.changes().id(change2a.getChangeId()).get();
- fail("expected failure");
- } catch (ResourceNotFoundException e) {
- assertThat(e.getMessage()).isEqualTo("Not found: " + change2a.getChangeId());
- }
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.changes().id(change2a.getChangeId()).get());
+ assertThat(thrown).hasMessageThat().isEqualTo("Not found: " + change2a.getChangeId());
// Submit is expected to fail.
- try {
- gApi.changes().id(change1.getChangeId()).current().submit();
- fail("expected failure");
- } catch (AuthException e) {
- assertThat(e.getMessage())
- .isEqualTo(
- "A change to be submitted with "
- + change1.getChange().getId().id
- + " is not visible");
- }
+ AuthException thrown2 =
+ assertThrows(
+ AuthException.class, () -> gApi.changes().id(change1.getChangeId()).current().submit());
+ assertThat(thrown2)
+ .hasMessageThat()
+ .isEqualTo(
+ "A change to be submitted with "
+ + change1.getChange().getId().get()
+ + " is not visible");
assertRefUpdatedEvents();
assertChangeMergedEvents();
}
@Test
- public void testPreviewSubmitTgz() throws Exception {
+ public void testPreviewSubmitTgz() throws Throwable {
Project.NameKey p1 = projectOperations.newProject().create();
TestRepository<?> repo1 = cloneProject(p1);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
index eb8dea572d..388c4f4ca2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
@@ -15,35 +15,35 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.FooterConstants;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.config.UrlFormatter;
import com.google.gerrit.server.git.ChangeMessageModifier;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
-import java.util.ArrayDeque;
-import java.util.Deque;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class SubmitByRebaseAlwaysIT extends AbstractSubmitByRebase {
- @Inject private DynamicSet<ChangeMessageModifier> changeMessageModifiers;
@Inject private DynamicItem<UrlFormatter> urlFormatter;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
@Override
protected SubmitType getSubmitType() {
@@ -52,12 +52,12 @@ public class SubmitByRebaseAlwaysIT extends AbstractSubmitByRebase {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithPossibleFastForward() throws Exception {
- RevCommit oldHead = getRemoteHead();
+ public void submitWithPossibleFastForward() throws Throwable {
+ RevCommit oldHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
submit(change.getChangeId());
- RevCommit head = getRemoteHead();
+ RevCommit head = projectOperations.project(project).getHead("master");
assertThat(head.getId()).isNotEqualTo(change.getCommit());
assertThat(head.getParent(0)).isEqualTo(oldHead);
assertApproved(change.getChangeId());
@@ -72,7 +72,7 @@ public class SubmitByRebaseAlwaysIT extends AbstractSubmitByRebase {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void alwaysAddFooters() throws Exception {
+ public void alwaysAddFooters() throws Throwable {
PushOneCommit.Result change1 = createChange();
PushOneCommit.Result change2 = createChange();
@@ -89,21 +89,22 @@ public class SubmitByRebaseAlwaysIT extends AbstractSubmitByRebase {
}
@Test
- public void rebaseInvokesChangeMessageModifiers() throws Exception {
+ public void rebaseInvokesChangeMessageModifiers() throws Throwable {
ChangeMessageModifier modifier1 =
(msg, orig, tip, dest) -> msg + "This-change-before-rebase: " + orig.name() + "\n";
ChangeMessageModifier modifier2 =
(msg, orig, tip, dest) -> msg + "Previous-step-tip: " + tip.name() + "\n";
ChangeMessageModifier modifier3 =
- (msg, orig, tip, dest) -> msg + "Dest: " + dest.getShortName() + "\n";
+ (msg, orig, tip, dest) -> msg + "Dest: " + dest.shortName() + "\n";
- try (AutoCloseable ignored = installChangeMessageModifiers(modifier1, modifier2, modifier3)) {
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(modifier1).add(modifier2).add(modifier3)) {
ImmutableList<PushOneCommit.Result> changes = submitWithRebase(admin);
ChangeData cd1 = changes.get(0).getChange();
ChangeData cd2 = changes.get(1).getChange();
assertThat(cd2.patchSets()).hasSize(2);
- String change1CurrentCommit = cd1.currentPatchSet().getRevision().get();
- String change2Ps1Commit = cd2.patchSet(new PatchSet.Id(cd2.getId(), 1)).getRevision().get();
+ String change1CurrentCommit = cd1.currentPatchSet().commitId().name();
+ String change2Ps1Commit = cd2.patchSet(PatchSet.id(cd2.getId(), 1)).commitId().name();
assertThat(gApi.changes().id(cd2.getId().get()).revision(2).commit(false).message)
.isEqualTo(
@@ -120,65 +121,52 @@ public class SubmitByRebaseAlwaysIT extends AbstractSubmitByRebase {
}
@Test
- public void failingChangeMessageModifierShortCircuits() throws Exception {
+ public void failingChangeMessageModifierShortCircuits() throws Throwable {
ChangeMessageModifier modifier1 =
(msg, orig, tip, dest) -> {
throw new IllegalStateException("boom");
};
ChangeMessageModifier modifier2 = (msg, orig, tip, dest) -> msg + "A-footer: value\n";
- try (AutoCloseable ignored = installChangeMessageModifiers(modifier1, modifier2)) {
- try {
- submitWithRebase();
- assert_().fail("expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- Throwable cause = Throwables.getRootCause(e);
- assertThat(cause).isInstanceOf(RuntimeException.class);
- assertThat(cause).hasMessageThat().isEqualTo("boom");
- }
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(modifier1).add(modifier2)) {
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> submitWithRebase());
+ Throwable cause = Throwables.getRootCause(thrown);
+ assertThat(cause).isInstanceOf(RuntimeException.class);
+ assertThat(cause).hasMessageThat().isEqualTo("boom");
}
}
@Test
- public void changeMessageModifierReturningNullShortCircuits() throws Exception {
+ public void changeMessageModifierReturningNullShortCircuits() throws Throwable {
ChangeMessageModifier modifier1 = (msg, orig, tip, dest) -> null;
ChangeMessageModifier modifier2 = (msg, orig, tip, dest) -> msg + "A-footer: value\n";
- try (AutoCloseable ignored = installChangeMessageModifiers(modifier1, modifier2)) {
- try {
- submitWithRebase();
- assert_().fail("expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- Throwable cause = Throwables.getRootCause(e);
- assertThat(cause).isInstanceOf(RuntimeException.class);
- assertThat(cause)
- .hasMessageThat()
- .isEqualTo(
- modifier1.getClass().getName()
- + ".onSubmit from plugin modifier-1 returned null instead of new commit"
- + " message");
- }
- }
- }
-
- private AutoCloseable installChangeMessageModifiers(ChangeMessageModifier... modifiers) {
- Deque<RegistrationHandle> handles = new ArrayDeque<>(modifiers.length);
- for (int i = 0; i < modifiers.length; i++) {
- handles.push(changeMessageModifiers.add("modifier-" + (i + 1), modifiers[i]));
+ try (Registration registration =
+ extensionRegistry
+ .newRegistration()
+ .add(modifier1, "modifier-1")
+ .add(modifier2, "modifier-2")) {
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> submitWithRebase());
+ Throwable cause = Throwables.getRootCause(thrown);
+ assertThat(cause).isInstanceOf(RuntimeException.class);
+ assertThat(cause)
+ .hasMessageThat()
+ .isEqualTo(
+ modifier1.getClass().getName()
+ + ".onSubmit from plugin modifier-1 returned null instead of new commit"
+ + " message");
}
- return () -> {
- while (!handles.isEmpty()) {
- handles.pop().remove();
- }
- };
}
- private void assertLatestRevisionHasFooters(PushOneCommit.Result change) throws Exception {
+ private void assertLatestRevisionHasFooters(PushOneCommit.Result change) throws Throwable {
RevCommit c = getCurrentCommit(change);
assertThat(c.getFooterLines(FooterConstants.CHANGE_ID)).isNotEmpty();
assertThat(c.getFooterLines(FooterConstants.REVIEWED_BY)).isNotEmpty();
assertThat(c.getFooterLines(FooterConstants.REVIEWED_ON)).isNotEmpty();
}
- private RevCommit getCurrentCommit(PushOneCommit.Result change) throws Exception {
+ private RevCommit getCurrentCommit(PushOneCommit.Result change) throws Throwable {
testRepo.git().fetch().setRemote("origin").call();
ChangeInfo info = get(change.getChangeId(), CURRENT_REVISION);
RevCommit c = testRepo.getRevWalk().parseCommit(ObjectId.fromString(info.currentRevision));
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
index 7bb31a0197..01b58eea5c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
@@ -18,12 +18,15 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.SubmitType;
+import com.google.inject.Inject;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class SubmitByRebaseIfNecessaryIT extends AbstractSubmitByRebase {
+ @Inject private ProjectOperations projectOperations;
@Override
protected SubmitType getSubmitType() {
@@ -32,11 +35,11 @@ public class SubmitByRebaseIfNecessaryIT extends AbstractSubmitByRebase {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithFastForward() throws Exception {
- RevCommit oldHead = getRemoteHead();
+ public void submitWithFastForward() throws Throwable {
+ RevCommit oldHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange();
submit(change.getChangeId());
- RevCommit head = getRemoteHead();
+ RevCommit head = projectOperations.project(project).getHead("master");
assertThat(head.getId()).isEqualTo(change.getCommit());
assertThat(head.getParent(0)).isEqualTo(oldHead);
assertApproved(change.getChangeId());
@@ -50,20 +53,20 @@ public class SubmitByRebaseIfNecessaryIT extends AbstractSubmitByRebase {
@Test
@TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void submitWithContentMerge() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ public void submitWithContentMerge() throws Throwable {
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
PushOneCommit.Result change = createChange("Change 1", "a.txt", "aaa\nbbb\nccc\n");
submit(change.getChangeId());
- RevCommit headAfterFirstSubmit = getRemoteHead();
+ RevCommit headAfterFirstSubmit = projectOperations.project(project).getHead("master");
PushOneCommit.Result change2 = createChange("Change 2", "a.txt", "aaa\nbbb\nccc\nddd\n");
submit(change2.getChangeId());
- RevCommit headAfterSecondSubmit = getRemoteHead();
+ RevCommit headAfterSecondSubmit = projectOperations.project(project).getHead("master");
testRepo.reset(change.getCommit());
PushOneCommit.Result change3 = createChange("Change 3", "a.txt", "bbb\nccc\n");
submit(change3.getChangeId());
assertRebase(testRepo, true);
- RevCommit headAfterThirdSubmit = getRemoteHead();
+ RevCommit headAfterThirdSubmit = projectOperations.project(project).getHead("master");
assertThat(headAfterThirdSubmit.getParent(0)).isEqualTo(headAfterSecondSubmit);
assertApproved(change3.getChangeId());
assertCurrentRevision(change3.getChangeId(), 2, headAfterThirdSubmit);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
index 87fc9f6b72..73f10e5475 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
@@ -21,8 +21,8 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ChangeStatus;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.restapi.change.Submit;
@@ -186,8 +186,8 @@ public class SubmitResolvingMergeCommitIT extends AbstractDaemonTest {
String project2Name = name("Project2");
gApi.projects().create(project1Name);
gApi.projects().create(project2Name);
- TestRepository<InMemoryRepository> project1 = cloneProject(new Project.NameKey(project1Name));
- TestRepository<InMemoryRepository> project2 = cloneProject(new Project.NameKey(project2Name));
+ TestRepository<InMemoryRepository> project1 = cloneProject(Project.nameKey(project1Name));
+ TestRepository<InMemoryRepository> project2 = cloneProject(Project.nameKey(project2Name));
PushOneCommit.Result a = createChange(project1, "A");
PushOneCommit.Result b =
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index 9bbe1dd9c4..8e0042cf89 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -16,7 +16,12 @@ package com.google.gerrit.acceptance.rest.change;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.common.data.Permission.READ;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
@@ -29,14 +34,17 @@ import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.accounts.EmailInput;
+import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import java.util.List;
import java.util.Set;
@@ -130,6 +138,48 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
}
@Test
+ @GerritConfig(name = "index.maxTerms", value = "10")
+ public void suggestReviewersTooManyQueryTerms() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ // Do a query which doesn't exceed index.maxTerms succeeds (add only 9 terms, since on
+ // 'inactive:1' term is implicitly added) and assert that a result is returned
+ StringBuilder query = new StringBuilder();
+ for (int i = 1; i <= 9; i++) {
+ query.append(name("u")).append(" ");
+ }
+ assertThat(suggestReviewers(changeId, query.toString())).isNotEmpty();
+
+ // Do a query which exceed index.maxTerms succeeds (10 terms plus 'inactive:1' term which is
+ // implicitly added).
+ query.append(name("u"));
+ BadRequestException exception =
+ assertThrows(BadRequestException.class, () -> suggestReviewers(changeId, query.toString()));
+ assertThat(exception).hasMessageThat().isEqualTo("too many terms in query");
+ }
+
+ @Test
+ public void suggestReviewersWithExcludeGroups() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ // by default groups are included
+ List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, name("user"));
+ assertReviewers(
+ reviewers, ImmutableList.of(user1, user2, user3), ImmutableList.of(group1, group2, group3));
+
+ // exclude groups
+ reviewers =
+ gApi.changes().id(changeId).suggestReviewers(name("user")).excludeGroups(true).get();
+ assertReviewers(reviewers, ImmutableList.of(user1, user2, user3), ImmutableList.of());
+
+ // explicitly include groups
+ reviewers =
+ gApi.changes().id(changeId).suggestReviewers(name("user")).excludeGroups(false).get();
+ assertReviewers(
+ reviewers, ImmutableList.of(user1, user2, user3), ImmutableList.of(group1, group2, group3));
+ }
+
+ @Test
@GerritConfig(name = "accounts.visibility", value = "SAME_GROUP")
public void suggestReviewersSameGroupVisibility() throws Exception {
String changeId = createChange().getChangeId();
@@ -160,8 +210,12 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
List<SuggestedReviewerInfo> reviewers;
requestScopeOperations.setApiUser(user3.id());
- block("refs/*", "read", ANONYMOUS_USERS);
- allow("refs/*", "read", group1);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(READ).ref("refs/*").group(ANONYMOUS_USERS))
+ .add(allow(READ).ref("refs/*").group(group1))
+ .update();
reviewers = suggestReviewers(changeId, user2.username(), 2);
assertThat(reviewers).isEmpty();
}
@@ -178,7 +232,10 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
// Clear cached group info.
requestScopeOperations.setApiUser(user1.id());
- allowGlobalCapabilities(group1, GlobalCapability.VIEW_ALL_ACCOUNTS);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.VIEW_ALL_ACCOUNTS).group(group1))
+ .update();
reviewers = suggestReviewers(changeId, user2.username(), 2);
assertThat(reviewers).hasSize(1);
assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
@@ -376,109 +433,101 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
}
@Test
- @GerritConfig(name = "suggest.maxSuggestedReviewers", value = "10")
- public void reviewerRanking() throws Exception {
- // Assert that user are ranked by the number of times they have applied a
- // a label to a change (highest), added comments (medium) or owned a
- // change (low).
- String fullName = "Primum Finalis";
- TestAccount userWhoOwns = user("customuser1", fullName);
- TestAccount reviewer1 = user("customuser2", fullName);
- TestAccount reviewer2 = user("customuser3", fullName);
- TestAccount userWhoComments = user("customuser4", fullName);
- TestAccount userWhoLooksForSuggestions = user("customuser5", fullName);
-
- // Create a change as userWhoOwns and add some reviews
- requestScopeOperations.setApiUser(userWhoOwns.id());
- String changeId1 = createChangeFromApi();
-
- requestScopeOperations.setApiUser(reviewer1.id());
- reviewChange(changeId1);
-
- requestScopeOperations.setApiUser(user1.id());
- String changeId2 = createChangeFromApi();
-
- requestScopeOperations.setApiUser(reviewer1.id());
- reviewChange(changeId2);
+ public void suggestNoInactiveAccounts() throws Exception {
+ requestScopeOperations.setApiUser(user.id());
+ String changeIdReviewed = createChangeFromApi();
+ String changeId = createChangeFromApi();
- requestScopeOperations.setApiUser(reviewer2.id());
- reviewChange(changeId2);
+ String name = name("foo");
+ TestAccount foo1 = accountCreator.create(name + "-1");
+ requestScopeOperations.setApiUser(foo1.id());
+ reviewChange(changeIdReviewed);
+ assertThat(gApi.accounts().id(foo1.username()).getActive()).isTrue();
- // Create a comment as a different user
- requestScopeOperations.setApiUser(userWhoComments.id());
- ReviewInput ri = new ReviewInput();
- ri.message = "Test";
- gApi.changes().id(changeId1).revision(1).review(ri);
+ TestAccount foo2 = accountCreator.create(name + "-2");
+ requestScopeOperations.setApiUser(foo2.id());
+ reviewChange(changeIdReviewed);
+ assertThat(gApi.accounts().id(foo2.username()).getActive()).isTrue();
- // Create a change as a new user to assert that we receive the correct
- // ranking
+ assertReviewers(
+ suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
- requestScopeOperations.setApiUser(userWhoLooksForSuggestions.id());
- List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "Pri", 4);
- assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
- .containsExactly(
- reviewer1.id().get(),
- reviewer2.id().get(),
- userWhoOwns.id().get(),
- userWhoComments.id().get())
- .inOrder();
+ requestScopeOperations.setApiUser(user.id());
+ gApi.accounts().id(foo2.username()).setActive(false);
+ assertThat(gApi.accounts().id(foo2.id().get()).getActive()).isFalse();
+ assertReviewers(suggestReviewers(changeId, name), ImmutableList.of(foo1), ImmutableList.of());
}
@Test
- public void reviewerRankingProjectIsolation() throws Exception {
- // Create new project
- Project.NameKey newProject = projectOperations.newProject().create();
+ public void suggestNoExistingReviewers() throws Exception {
+ requestScopeOperations.setApiUser(user.id());
+ String changeId = createChangeFromApi();
+ String changeIdReviewed = createChangeFromApi();
- // Create users who review changes in both the default and the new project
- String fullName = "Primum Finalis";
- TestAccount userWhoOwns = user("customuser1", fullName);
- TestAccount reviewer1 = user("customuser2", fullName);
- TestAccount reviewer2 = user("customuser3", fullName);
+ String name = name("foo");
+ TestAccount foo1 = accountCreator.create(name + "-1");
+ requestScopeOperations.setApiUser(foo1.id());
+ reviewChange(changeIdReviewed);
- requestScopeOperations.setApiUser(userWhoOwns.id());
- String changeId1 = createChangeFromApi();
+ TestAccount foo2 = accountCreator.create(name + "-2");
+ requestScopeOperations.setApiUser(foo2.id());
+ reviewChange(changeIdReviewed);
- requestScopeOperations.setApiUser(reviewer1.id());
- reviewChange(changeId1);
+ assertReviewers(
+ suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
- requestScopeOperations.setApiUser(userWhoOwns.id());
- String changeId2 = createChangeFromApi(newProject);
+ gApi.changes().id(changeId).addReviewer(foo2.id().toString());
+ assertReviewers(suggestReviewers(changeId, name), ImmutableList.of(foo1), ImmutableList.of());
+ }
- requestScopeOperations.setApiUser(reviewer2.id());
- reviewChange(changeId2);
+ @Test
+ public void suggestCcAsReviewer() throws Exception {
+ requestScopeOperations.setApiUser(user.id());
+ String changeId = createChangeFromApi();
+ String changeIdReviewed = createChangeFromApi();
- requestScopeOperations.setApiUser(userWhoOwns.id());
- String changeId3 = createChangeFromApi(newProject);
+ String name = name("foo");
+ TestAccount foo1 = accountCreator.create(name + "-1");
+ requestScopeOperations.setApiUser(foo1.id());
+ reviewChange(changeIdReviewed);
- requestScopeOperations.setApiUser(reviewer2.id());
- reviewChange(changeId3);
+ TestAccount foo2 = accountCreator.create(name + "-2");
+ requestScopeOperations.setApiUser(foo2.id());
+ reviewChange(changeIdReviewed);
- requestScopeOperations.setApiUser(userWhoOwns.id());
- List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "Prim", 4);
+ assertReviewers(
+ suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
- // Assert that reviewer1 is on top, even though reviewer2 has more reviews
- // in other projects
- assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
- .containsExactly(reviewer1.id().get(), reviewer2.id().get())
- .inOrder();
+ AddReviewerInput reviewerInput = new AddReviewerInput();
+ reviewerInput.reviewer = foo2.id().toString();
+ reviewerInput.state = ReviewerState.CC;
+ gApi.changes().id(changeId).addReviewer(reviewerInput);
+ assertReviewers(
+ suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
}
@Test
- public void suggestNoInactiveAccounts() throws Exception {
+ public void suggestReviewerAsCc() throws Exception {
+ requestScopeOperations.setApiUser(user.id());
+ String changeId = createChangeFromApi();
+ String changeIdReviewed = createChangeFromApi();
+
String name = name("foo");
TestAccount foo1 = accountCreator.create(name + "-1");
- assertThat(gApi.accounts().id(foo1.username()).getActive()).isTrue();
+ requestScopeOperations.setApiUser(foo1.id());
+ reviewChange(changeIdReviewed);
TestAccount foo2 = accountCreator.create(name + "-2");
- assertThat(gApi.accounts().id(foo2.username()).getActive()).isTrue();
+ requestScopeOperations.setApiUser(foo2.id());
+ reviewChange(changeIdReviewed);
- String changeId = createChange().getChangeId();
- assertReviewers(
- suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
+ assertReviewers(suggestCcs(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
- gApi.accounts().id(foo2.username()).setActive(false);
- assertThat(gApi.accounts().id(foo2.id().get()).getActive()).isFalse();
- assertReviewers(suggestReviewers(changeId, name), ImmutableList.of(foo1), ImmutableList.of());
+ AddReviewerInput reviewerInput = new AddReviewerInput();
+ reviewerInput.reviewer = foo2.id().toString();
+ reviewerInput.state = ReviewerState.REVIEWER;
+ gApi.changes().id(changeId).addReviewer(reviewerInput);
+ assertReviewers(suggestCcs(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
}
@Test
@@ -486,25 +535,17 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
String secondaryEmail = "foo.secondary@example.com";
TestAccount foo = createAccountWithSecondaryEmail("foo", secondaryEmail);
- List<SuggestedReviewerInfo> reviewers =
- suggestReviewers(createChange().getChangeId(), secondaryEmail, 4);
- assertReviewers(reviewers, ImmutableList.of(foo), ImmutableList.of());
-
- reviewers = suggestReviewers(createChange().getChangeId(), "secondary", 4);
+ List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "secondary", 4);
assertReviewers(reviewers, ImmutableList.of(foo), ImmutableList.of());
}
@Test
public void cannotSuggestBySecondaryEmailWithoutModifyAccount() throws Exception {
- String secondaryEmail = "foo.secondary@example.com";
- createAccountWithSecondaryEmail("foo", secondaryEmail);
-
+ // Test that even if the account exists, the result is still empty since
+ // it shouldn't match to that account based only on the secondary email.
+ createAccountWithSecondaryEmail("foo", "foo.secondary@example.com");
requestScopeOperations.setApiUser(user.id());
- List<SuggestedReviewerInfo> reviewers =
- suggestReviewers(createChange().getChangeId(), secondaryEmail, 4);
- assertThat(reviewers).isEmpty();
-
- reviewers = suggestReviewers(createChange().getChangeId(), "secondary2", 4);
+ List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "secondary", 4);
assertThat(reviewers).isEmpty();
}
@@ -525,6 +566,24 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
assertThat(Iterables.getOnlyElement(reviewers).account.secondaryEmails).isNull();
}
+ @Test
+ public void suggestsPeopleWithNoReviewsWhenExplicitlyQueried() throws Exception {
+ TestAccount newTeamMember = accountCreator.create("newTeamMember");
+
+ requestScopeOperations.setApiUser(user.id());
+ String changeId = createChangeFromApi();
+ String changeIdReviewed = createChangeFromApi();
+
+ TestAccount reviewer = accountCreator.create("newReviewer");
+ requestScopeOperations.setApiUser(reviewer.id());
+ reviewChange(changeIdReviewed);
+
+ List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, "new", 4);
+ assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
+ .containsExactly(reviewer.id().get(), newTeamMember.id().get())
+ .inOrder();
+ }
+
private TestAccount createAccountWithSecondaryEmail(String name, String secondaryEmail)
throws Exception {
TestAccount foo = accountCreator.create(name(name), "foo.primary@example.com", "Foo");
@@ -545,6 +604,10 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
return gApi.changes().id(changeId).suggestReviewers(query).withLimit(n).get();
}
+ private List<SuggestedReviewerInfo> suggestCcs(String changeId, String query) throws Exception {
+ return gApi.changes().id(changeId).suggestCcs(query).get();
+ }
+
private AccountGroup.UUID createGroupWithArbitraryMembers(int numMembers) {
Set<Account.Id> members =
IntStream.rangeClosed(1, numMembers)
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java b/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
index 49692dde42..525f5d5888 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
@@ -15,127 +15,194 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
+import static com.google.gerrit.acceptance.GitUtil.pushHead;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
-import org.junit.After;
-import org.junit.Before;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.PushResult;
import org.junit.Test;
public class WorkInProgressByDefaultIT extends AbstractDaemonTest {
@Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
- private Project.NameKey project1;
- private Project.NameKey project2;
-
- @Before
- public void setUp() throws Exception {
- project1 = projectOperations.newProject().create();
- project2 = projectOperations.newProject().parent(project1).create();
- }
-
- @After
- public void tearDown() throws Exception {
- requestScopeOperations.setApiUser(admin.id());
- GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
- prefs.workInProgressByDefault = false;
- gApi.accounts().id(admin.id().get()).setPreferences(prefs);
- }
-
@Test
public void createChangeWithWorkInProgressByDefaultForProjectDisabled() throws Exception {
+ Project.NameKey project = projectOperations.newProject().create();
ChangeInfo info =
- gApi.changes().create(new ChangeInput(project2.get(), "master", "empty change")).get();
+ gApi.changes().create(new ChangeInput(project.get(), "master", "empty change")).get();
assertThat(info.workInProgress).isNull();
}
@Test
public void createChangeWithWorkInProgressByDefaultForProjectEnabled() throws Exception {
- setWorkInProgressByDefaultForProject(project2);
- ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
+ Project.NameKey project = projectOperations.newProject().create();
+ setWorkInProgressByDefaultForProject(project);
+ ChangeInput input = new ChangeInput(project.get(), "master", "empty change");
assertThat(gApi.changes().create(input).get().workInProgress).isTrue();
}
@Test
public void createChangeWithWorkInProgressByDefaultForUserEnabled() throws Exception {
+ Project.NameKey project = projectOperations.newProject().create();
setWorkInProgressByDefaultForUser();
- ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
+ ChangeInput input = new ChangeInput(project.get(), "master", "empty change");
assertThat(gApi.changes().create(input).get().workInProgress).isTrue();
}
@Test
public void createChangeBypassWorkInProgressByDefaultForProjectEnabled() throws Exception {
- setWorkInProgressByDefaultForProject(project2);
- ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
+ Project.NameKey project = projectOperations.newProject().create();
+ setWorkInProgressByDefaultForProject(project);
+ ChangeInput input = new ChangeInput(project.get(), "master", "empty change");
input.workInProgress = false;
assertThat(gApi.changes().create(input).get().workInProgress).isNull();
}
@Test
public void createChangeBypassWorkInProgressByDefaultForUserEnabled() throws Exception {
+ Project.NameKey project = projectOperations.newProject().create();
setWorkInProgressByDefaultForUser();
- ChangeInput input = new ChangeInput(project2.get(), "master", "empty change");
+ ChangeInput input = new ChangeInput(project.get(), "master", "empty change");
input.workInProgress = false;
assertThat(gApi.changes().create(input).get().workInProgress).isNull();
}
@Test
public void createChangeWithWorkInProgressByDefaultForProjectInherited() throws Exception {
- setWorkInProgressByDefaultForProject(project1);
+ Project.NameKey parentProject = projectOperations.newProject().create();
+ Project.NameKey childProject = projectOperations.newProject().parent(parentProject).create();
+ setWorkInProgressByDefaultForProject(parentProject);
ChangeInfo info =
- gApi.changes().create(new ChangeInput(project2.get(), "master", "empty change")).get();
+ gApi.changes().create(new ChangeInput(childProject.get(), "master", "empty change")).get();
assertThat(info.workInProgress).isTrue();
}
@Test
public void pushWithWorkInProgressByDefaultForProjectEnabled() throws Exception {
- setWorkInProgressByDefaultForProject(project2);
- assertThat(createChange(project2).getChange().change().isWorkInProgress()).isTrue();
+ Project.NameKey project = projectOperations.newProject().create();
+ setWorkInProgressByDefaultForProject(project);
+ assertThat(createChange(project).getChange().change().isWorkInProgress()).isTrue();
}
@Test
public void pushWithWorkInProgressByDefaultForUserEnabled() throws Exception {
+ Project.NameKey project = projectOperations.newProject().create();
setWorkInProgressByDefaultForUser();
- assertThat(createChange(project2).getChange().change().isWorkInProgress()).isTrue();
+ assertThat(createChange(project).getChange().change().isWorkInProgress()).isTrue();
}
@Test
public void pushBypassWorkInProgressByDefaultForProjectEnabled() throws Exception {
- setWorkInProgressByDefaultForProject(project2);
+ Project.NameKey project = projectOperations.newProject().create();
+ setWorkInProgressByDefaultForProject(project);
assertThat(
- createChange(project2, "refs/for/master%ready").getChange().change().isWorkInProgress())
+ createChange(project, "refs/for/master%ready").getChange().change().isWorkInProgress())
.isFalse();
}
@Test
public void pushBypassWorkInProgressByDefaultForUserEnabled() throws Exception {
+ Project.NameKey project = projectOperations.newProject().create();
setWorkInProgressByDefaultForUser();
assertThat(
- createChange(project2, "refs/for/master%ready").getChange().change().isWorkInProgress())
+ createChange(project, "refs/for/master%ready").getChange().change().isWorkInProgress())
.isFalse();
}
@Test
public void pushWithWorkInProgressByDefaultForProjectDisabled() throws Exception {
- assertThat(createChange(project2).getChange().change().isWorkInProgress()).isFalse();
+ Project.NameKey project = projectOperations.newProject().create();
+ assertThat(createChange(project).getChange().change().isWorkInProgress()).isFalse();
}
@Test
public void pushWorkInProgressByDefaultForProjectInherited() throws Exception {
- setWorkInProgressByDefaultForProject(project1);
- assertThat(createChange(project2).getChange().change().isWorkInProgress()).isTrue();
+ Project.NameKey parentProject = projectOperations.newProject().create();
+ Project.NameKey childProject = projectOperations.newProject().parent(parentProject).create();
+ setWorkInProgressByDefaultForProject(parentProject);
+ assertThat(createChange(childProject).getChange().change().isWorkInProgress()).isTrue();
+ }
+
+ @Test
+ public void pushNewPatchSetWithWorkInProgressByDefaultForUserEnabled() throws Exception {
+ Project.NameKey project = projectOperations.newProject().create();
+
+ // Create change.
+ TestRepository<InMemoryRepository> testRepo = cloneProject(project);
+ PushOneCommit.Result result =
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
+ result.assertOkStatus();
+
+ String changeId = result.getChangeId();
+ assertThat(gApi.changes().id(changeId).get().workInProgress).isNull();
+
+ setWorkInProgressByDefaultForUser();
+
+ // Create new patch set on existing change, this shouldn't mark the change as WIP.
+ result = pushFactory.create(admin.newIdent(), testRepo, changeId).to("refs/for/master");
+ result.assertOkStatus();
+ assertThat(gApi.changes().id(changeId).get().workInProgress).isNull();
+ }
+
+ @Test
+ public void pushNewPatchSetAndNewChangeAtOnceWithWorkInProgressByDefaultForUserEnabled()
+ throws Exception {
+ Project.NameKey project = projectOperations.newProject().create();
+
+ // Create change.
+ TestRepository<InMemoryRepository> testRepo = cloneProject(project);
+ RevCommit initialHead = getHead(testRepo.getRepository(), "HEAD");
+ RevCommit commit1a =
+ testRepo.commit().parent(initialHead).message("Change 1").insertChangeId().create();
+ String changeId1 = GitUtil.getChangeId(testRepo, commit1a).get();
+ testRepo.reset(commit1a);
+ PushResult result = pushHead(testRepo, "refs/for/master", false);
+ assertPushOk(result, "refs/for/master");
+ assertThat(gApi.changes().id(changeId1).get().workInProgress).isNull();
+
+ setWorkInProgressByDefaultForUser();
+
+ // Clone the repo again. The test connection keeps an AccountState internally, so we need to
+ // create a new connection after changing account properties.
+ PatchSet.Id ps1OfChange1 =
+ PatchSet.id(Change.id(gApi.changes().id(changeId1).get()._number), 1);
+ testRepo = cloneProject(project);
+ testRepo.git().fetch().setRefSpecs(RefNames.patchSetRef(ps1OfChange1) + ":c1").call();
+ testRepo.reset("c1");
+
+ // Create a new patch set on the existing change and in the same push create a new successor
+ // change.
+ RevCommit commit1b = testRepo.amend(commit1a).create();
+ testRepo.reset(commit1b);
+ RevCommit commit2 =
+ testRepo.commit().parent(commit1b).message("Change 2").insertChangeId().create();
+ String changeId2 = GitUtil.getChangeId(testRepo, commit2).get();
+ testRepo.reset(commit2);
+ result = pushHead(testRepo, "refs/for/master", false);
+ assertPushOk(result, "refs/for/master");
+
+ // Check that the existing change (changeId1) is not marked as WIP, but only the newly created
+ // change (changeId2).
+ assertThat(gApi.changes().id(changeId1).get().workInProgress).isNull();
+ assertThat(gApi.changes().id(changeId2).get().workInProgress).isTrue();
}
private void setWorkInProgressByDefaultForProject(Project.NameKey p) throws Exception {
@@ -145,9 +212,12 @@ public class WorkInProgressByDefaultIT extends AbstractDaemonTest {
}
private void setWorkInProgressByDefaultForUser() throws Exception {
- GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
+ GeneralPreferencesInfo prefs = new GeneralPreferencesInfo();
prefs.workInProgressByDefault = true;
gApi.accounts().id(admin.id().get()).setPreferences(prefs);
+ // Generate a new API scope. User preferences are stored in IdentifiedUser, so we need to flush
+ // that entity.
+ requestScopeOperations.resetCurrentApiUser();
}
private PushOneCommit.Result createChange(Project.NameKey p) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java b/javatests/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java
index 7ef915b958..daeb032a82 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/CacheOperationsIT.java
@@ -15,19 +15,24 @@
package com.google.gerrit.acceptance.rest.config;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.capabilityKey;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.restapi.config.PostCaches.Operation.FLUSH;
import static com.google.gerrit.server.restapi.config.PostCaches.Operation.FLUSH_ALL;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.server.restapi.config.ListCaches.CacheInfo;
import com.google.gerrit.server.restapi.config.PostCaches;
+import com.google.inject.Inject;
import java.util.Arrays;
import org.junit.Test;
public class CacheOperationsIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Test
public void flushAll() throws Exception {
@@ -124,8 +129,11 @@ public class CacheOperationsIT extends AbstractDaemonTest {
@Test
public void flushWebSessions_Forbidden() throws Exception {
- allowGlobalCapabilities(
- REGISTERED_USERS, GlobalCapability.FLUSH_CACHES, GlobalCapability.VIEW_CACHES);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.FLUSH_CACHES).group(REGISTERED_USERS))
+ .add(allowCapability(GlobalCapability.VIEW_CACHES).group(REGISTERED_USERS))
+ .update();
try {
RestResponse r =
userRestSession.post(
@@ -138,8 +146,11 @@ public class CacheOperationsIT extends AbstractDaemonTest {
"/config/server/caches/", new PostCaches.Input(FLUSH, Arrays.asList("web_sessions")))
.assertForbidden();
} finally {
- removeGlobalCapabilities(
- REGISTERED_USERS, GlobalCapability.FLUSH_CACHES, GlobalCapability.VIEW_CACHES);
+ projectOperations
+ .allProjectsForUpdate()
+ .remove(capabilityKey(GlobalCapability.FLUSH_CACHES).group(REGISTERED_USERS))
+ .remove(capabilityKey(GlobalCapability.VIEW_CACHES).group(REGISTERED_USERS))
+ .update();
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java b/javatests/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java
index caecefade0..a161ec4eb7 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/FlushCacheIT.java
@@ -15,15 +15,20 @@
package com.google.gerrit.acceptance.rest.config;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.capabilityKey;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.server.restapi.config.ListCaches.CacheInfo;
+import com.google.inject.Inject;
import org.junit.Test;
public class FlushCacheIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Test
public void flushCache() throws Exception {
@@ -65,8 +70,11 @@ public class FlushCacheIT extends AbstractDaemonTest {
@Test
public void flushWebSessionsCache_Forbidden() throws Exception {
- allowGlobalCapabilities(
- REGISTERED_USERS, GlobalCapability.VIEW_CACHES, GlobalCapability.FLUSH_CACHES);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.FLUSH_CACHES).group(REGISTERED_USERS))
+ .add(allowCapability(GlobalCapability.VIEW_CACHES).group(REGISTERED_USERS))
+ .update();
try {
RestResponse r = userRestSession.post("/config/server/caches/accounts/flush");
r.assertOK();
@@ -74,8 +82,11 @@ public class FlushCacheIT extends AbstractDaemonTest {
userRestSession.post("/config/server/caches/web_sessions/flush").assertForbidden();
} finally {
- removeGlobalCapabilities(
- REGISTERED_USERS, GlobalCapability.VIEW_CACHES, GlobalCapability.FLUSH_CACHES);
+ projectOperations
+ .allProjectsForUpdate()
+ .remove(capabilityKey(GlobalCapability.FLUSH_CACHES).group(REGISTERED_USERS))
+ .remove(capabilityKey(GlobalCapability.VIEW_CACHES).group(REGISTERED_USERS))
+ .update();
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java b/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
index fa35d19813..a3c1722982 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/IndexChangesIT.java
@@ -15,84 +15,90 @@
package com.google.gerrit.acceptance.rest.config;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.ChangeIndexedCounter;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.events.ChangeIndexedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.server.restapi.config.IndexChanges;
import com.google.inject.Inject;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
public class IndexChangesIT extends AbstractDaemonTest {
- @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
-
- private ChangeIndexedCounter changeIndexedCounter;
- private RegistrationHandle changeIndexedCounterHandle;
-
- @Before
- public void addChangeIndexedCounter() {
- changeIndexedCounter = new ChangeIndexedCounter();
- changeIndexedCounterHandle = changeIndexedListeners.add("gerrit", changeIndexedCounter);
- }
-
- @After
- public void removeChangeIndexedCounter() {
- if (changeIndexedCounterHandle != null) {
- changeIndexedCounterHandle.remove();
- }
- }
+ @Inject private ProjectOperations projectOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
@Test
public void indexRequestFromNonAdminRejected() throws Exception {
- String changeId = createChange().getChangeId();
- IndexChanges.Input in = new IndexChanges.Input();
- in.changes = ImmutableSet.of(changeId);
- changeIndexedCounter.clear();
- userRestSession.post("/config/server/index.changes", in).assertForbidden();
- assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(0);
+ ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedCounter)) {
+ String changeId = createChange().getChangeId();
+ IndexChanges.Input in = new IndexChanges.Input();
+ in.changes = ImmutableSet.of(changeId);
+ changeIndexedCounter.clear();
+ userRestSession.post("/config/server/index.changes", in).assertForbidden();
+ assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(0);
+ }
}
@Test
public void indexVisibleChange() throws Exception {
- String changeId = createChange().getChangeId();
- IndexChanges.Input in = new IndexChanges.Input();
- in.changes = ImmutableSet.of(changeId);
- changeIndexedCounter.clear();
- adminRestSession.post("/config/server/index.changes", in).assertOK();
- assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+ ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedCounter)) {
+ String changeId = createChange().getChangeId();
+ IndexChanges.Input in = new IndexChanges.Input();
+ in.changes = ImmutableSet.of(changeId);
+ changeIndexedCounter.clear();
+ adminRestSession.post("/config/server/index.changes", in).assertOK();
+ assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+ }
}
@Test
public void indexNonVisibleChange() throws Exception {
- String changeId = createChange().getChangeId();
- ChangeInfo changeInfo = info(changeId);
- blockRead("refs/heads/master");
- IndexChanges.Input in = new IndexChanges.Input();
- changeIndexedCounter.clear();
- in.changes = ImmutableSet.of(changeId);
- adminRestSession.post("/config/server/index.changes", in).assertOK();
- assertThat(changeIndexedCounter.getCount(changeInfo)).isEqualTo(1);
+ ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedCounter)) {
+ String changeId = createChange().getChangeId();
+ ChangeInfo changeInfo = info(changeId);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
+ IndexChanges.Input in = new IndexChanges.Input();
+ changeIndexedCounter.clear();
+ in.changes = ImmutableSet.of(changeId);
+ adminRestSession.post("/config/server/index.changes", in).assertOK();
+ assertThat(changeIndexedCounter.getCount(changeInfo)).isEqualTo(1);
+ }
}
@Test
public void indexMultipleChanges() throws Exception {
- ImmutableSet.Builder<String> changeIds = ImmutableSet.builder();
- for (int i = 0; i < 10; i++) {
- changeIds.add(createChange().getChangeId());
- }
- IndexChanges.Input in = new IndexChanges.Input();
- in.changes = changeIds.build();
- changeIndexedCounter.clear();
- adminRestSession.post("/config/server/index.changes", in).assertOK();
- for (String changeId : in.changes) {
- assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+ ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedCounter)) {
+ ImmutableSet.Builder<String> changeIds = ImmutableSet.builder();
+ for (int i = 0; i < 10; i++) {
+ changeIds.add(createChange().getChangeId());
+ }
+ IndexChanges.Input in = new IndexChanges.Input();
+ in.changes = changeIds.build();
+ changeIndexedCounter.clear();
+ adminRestSession.post("/config/server/index.changes", in).assertOK();
+ for (String changeId : in.changes) {
+ assertThat(changeIndexedCounter.getCount(info(changeId))).isEqualTo(1);
+ }
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java b/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
index 4a740182e2..1d87ca1af0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
@@ -168,6 +168,8 @@ public class ServerInfoIT extends AbstractDaemonTest {
assertThat(i.change.replyLabel).isEqualTo("Reply\u2026");
assertThat(i.change.updateDelay).isEqualTo(300);
assertThat(i.change.disablePrivateChanges).isNull();
+ assertThat(i.change.submitWholeTopic).isNull();
+ assertThat(i.change.excludeMergeableInChangeInfo).isNull();
// download
assertThat(i.download.archives).containsExactly("tar", "tbz2", "tgz", "txz");
@@ -190,4 +192,18 @@ public class ServerInfoIT extends AbstractDaemonTest {
// user
assertThat(i.user.anonymousCowardName).isEqualTo(AnonymousCowardNameProvider.DEFAULT);
}
+
+ @Test
+ @GerritConfig(name = "change.submitWholeTopic", value = "true")
+ public void serverConfigWithSubmitWholeTopic() throws Exception {
+ ServerInfo i = gApi.config().server().getInfo();
+ assertThat(i.change.submitWholeTopic).isTrue();
+ }
+
+ @Test
+ @GerritConfig(name = "change.api.excludeMergeableInChangeInfo", value = "true")
+ public void serverConfigWithExcludeMergeableInChangeInfo() throws Exception {
+ ServerInfo i = gApi.config().server().getInfo();
+ assertThat(i.change.excludeMergeableInChangeInfo).isTrue();
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java b/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
index 9420304681..d70d120c19 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
@@ -14,20 +14,24 @@
package com.google.gerrit.acceptance.rest.project;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.GitUtil.createAnnotatedTag;
import static com.google.gerrit.acceptance.GitUtil.deleteRef;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.acceptance.GitUtil.updateAnnotatedTag;
import static com.google.gerrit.acceptance.rest.project.AbstractPushTag.TagType.ANNOTATED;
import static com.google.gerrit.acceptance.rest.project.AbstractPushTag.TagType.LIGHTWEIGHT;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.base.MoreObjects;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GitUtil;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.RefNames;
+import com.google.inject.Inject;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.PushResult;
@@ -48,12 +52,14 @@ public abstract class AbstractPushTag extends AbstractDaemonTest {
}
}
+ @Inject private ProjectOperations projectOperations;
+
private RevCommit initialHead;
private TagType tagType;
@Before
public void setUpTestEnvironment() throws Exception {
- initialHead = getRemoteHead();
+ initialHead = projectOperations.project(project).getHead("master");
tagType = getTagType();
blockAnonymousRead();
}
@@ -203,7 +209,11 @@ public abstract class AbstractPushTag extends AbstractDaemonTest {
}
if (!newCommit) {
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/master").group(REGISTERED_USERS))
+ .update();
pushHead(testRepo, "refs/for/master%submit");
}
@@ -213,7 +223,7 @@ public abstract class AbstractPushTag extends AbstractDaemonTest {
? pushHead(testRepo, tagRef, false, force)
: GitUtil.pushTag(testRepo, tagName, !createTag);
RemoteRefUpdate refUpdate = r.getRemoteUpdate(tagRef);
- assertThat(refUpdate.getStatus()).named(tagType.name()).isEqualTo(expectedStatus);
+ assertWithMessage(tagType.name()).that(refUpdate.getStatus()).isEqualTo(expectedStatus);
return tagName;
}
@@ -221,30 +231,50 @@ public abstract class AbstractPushTag extends AbstractDaemonTest {
String tagRef = tagRef(tagName);
PushResult r = deleteRef(testRepo, tagRef);
RemoteRefUpdate refUpdate = r.getRemoteUpdate(tagRef);
- assertThat(refUpdate.getStatus()).named(tagType.name()).isEqualTo(expectedStatus);
+ assertWithMessage(tagType.name()).that(refUpdate.getStatus()).isEqualTo(expectedStatus);
}
private void allowTagCreation() throws Exception {
- grant(project, "refs/tags/*", tagType.createPermission, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(tagType.createPermission).ref("refs/tags/*").group(REGISTERED_USERS))
+ .update();
}
private void allowPushOnRefsTags() throws Exception {
removePushFromRefsTags();
- grant(project, "refs/tags/*", Permission.PUSH, false, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS))
+ .update();
}
private void allowForcePushOnRefsTags() throws Exception {
removePushFromRefsTags();
- grant(project, "refs/tags/*", Permission.PUSH, true, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS).force(true))
+ .update();
}
private void allowTagDeletion() throws Exception {
removePushFromRefsTags();
- grant(project, "refs/tags/*", Permission.DELETE, true, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE).ref("refs/tags/*").group(REGISTERED_USERS).force(true))
+ .update();
}
private void removePushFromRefsTags() throws Exception {
- removePermission(project, "refs/tags/*", Permission.PUSH);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(permissionKey(Permission.PUSH).ref("refs/tags/*"))
+ .update();
}
private void commit(PersonIdent ident, String subject) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
index 72af07583e..91a10caa6d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -15,10 +15,15 @@ package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.truth.ConfigSubject.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
@@ -26,26 +31,23 @@ import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.access.AccessSectionInfo;
import com.google.gerrit.extensions.api.access.PermissionInfo;
import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
-import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.ProjectApi;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.webui.FileHistoryWebLink;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.ProjectConfig;
@@ -70,9 +72,9 @@ public class AccessIT extends AbstractDaemonTest {
private static final String LABEL_CODE_REVIEW = "Code-Review";
- @Inject private DynamicSet<FileHistoryWebLink> fileHistoryWebLinkDynamicSet;
@Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
private Project.NameKey newProjectName;
@@ -87,45 +89,45 @@ public class AccessIT extends AbstractDaemonTest {
assertThat(inheritedName).isEqualTo(AllProjectsNameProvider.DEFAULT);
}
+ private Registration newFileHistoryWebLink() {
+ FileHistoryWebLink weblink =
+ new FileHistoryWebLink() {
+ @Override
+ public WebLinkInfo getFileHistoryWebLink(
+ String projectName, String revision, String fileName) {
+ return new WebLinkInfo(
+ "name", "imageURL", "http://view/" + projectName + "/" + fileName);
+ }
+ };
+ return extensionRegistry.newRegistration().add(weblink);
+ }
+
@Test
public void webLink() throws Exception {
- RegistrationHandle handle =
- fileHistoryWebLinkDynamicSet.add(
- "gerrit",
- (projectName, revision, fileName) ->
- new WebLinkInfo("name", "imageURL", "http://view/" + projectName + "/" + fileName));
- try {
+ try (Registration registration = newFileHistoryWebLink()) {
ProjectAccessInfo info = pApi().access();
assertThat(info.configWebLinks).hasSize(1);
assertThat(info.configWebLinks.get(0).url)
.isEqualTo("http://view/" + newProjectName + "/project.config");
- } finally {
- handle.remove();
}
}
@Test
public void webLinkNoRefsMetaConfig() throws Exception {
- RegistrationHandle handle =
- fileHistoryWebLinkDynamicSet.add(
- "gerrit",
- (projectName, revision, fileName) ->
- new WebLinkInfo("name", "imageURL", "http://view/" + projectName + "/" + fileName));
- try (Repository repo = repoManager.openRepository(newProjectName)) {
+ try (Repository repo = repoManager.openRepository(newProjectName);
+ Registration registration = newFileHistoryWebLink()) {
RefUpdate u = repo.updateRef(RefNames.REFS_CONFIG);
u.setForceUpdate(true);
assertThat(u.delete()).isEqualTo(Result.FORCED);
// This should not crash.
pApi().access();
- } finally {
- handle.remove();
}
}
@Test
public void addAccessSection() throws Exception {
- RevCommit initialHead = getRemoteHead(newProjectName, RefNames.REFS_CONFIG);
+ RevCommit initialHead = projectOperations.project(newProjectName).getHead(RefNames.REFS_CONFIG);
ProjectAccessInput accessInput = newProjectAccessInput();
AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo();
@@ -135,7 +137,7 @@ public class AccessIT extends AbstractDaemonTest {
assertThat(pApi().access().local).isEqualTo(accessInput.add);
- RevCommit updatedHead = getRemoteHead(newProjectName, RefNames.REFS_CONFIG);
+ RevCommit updatedHead = projectOperations.project(newProjectName).getHead(RefNames.REFS_CONFIG);
eventRecorder.assertRefUpdatedEvents(
newProjectName.get(), RefNames.REFS_CONFIG, null, initialHead, initialHead, updatedHead);
}
@@ -143,8 +145,7 @@ public class AccessIT extends AbstractDaemonTest {
@Test
public void createAccessChangeNop() throws Exception {
ProjectAccessInput accessInput = newProjectAccessInput();
- exception.expect(BadRequestException.class);
- pApi().accessChange(accessInput);
+ assertThrows(BadRequestException.class, () -> pApi().accessChange(accessInput));
}
@Test
@@ -169,7 +170,11 @@ public class AccessIT extends AbstractDaemonTest {
@Test
public void createAccessChange() throws Exception {
- allow(newProjectName, RefNames.REFS_CONFIG, Permission.READ, REGISTERED_USERS);
+ projectOperations
+ .project(newProjectName)
+ .forUpdate()
+ .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(REGISTERED_USERS))
+ .update();
// User can see the branch
requestScopeOperations.setApiUser(user.id());
pApi().branch("refs/heads/master").get();
@@ -206,12 +211,7 @@ public class AccessIT extends AbstractDaemonTest {
// check that the change took effect.
requestScopeOperations.setApiUser(user.id());
- try {
- BranchInfo info = pApi().branch("refs/heads/master").get();
- fail("wanted failure, got " + newGson().toJson(info));
- } catch (ResourceNotFoundException e) {
- // OK.
- }
+ assertThrows(ResourceNotFoundException.class, () -> pApi().branch("refs/heads/master").get());
// Restore.
accessInput.add.clear();
@@ -326,8 +326,7 @@ public class AccessIT extends AbstractDaemonTest {
pApi().access(accessInput);
requestScopeOperations.setApiUser(user.id());
- exception.expect(ResourceNotFoundException.class);
- pApi().access();
+ assertThrows(ResourceNotFoundException.class, () -> pApi().access());
}
@Test
@@ -346,8 +345,7 @@ public class AccessIT extends AbstractDaemonTest {
accessInfoToApply.add.put(REFS_HEADS, accessSectionInfoToApply);
requestScopeOperations.setApiUser(user.id());
- exception.expect(ResourceNotFoundException.class);
- pApi().access();
+ assertThrows(ResourceNotFoundException.class, () -> pApi().access());
}
@Test
@@ -409,9 +407,8 @@ public class AccessIT extends AbstractDaemonTest {
accessInput.parent = newParentProjectName;
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- exception.expectMessage("administrate server not permitted");
- pApi().access(accessInput);
+ AuthException thrown = assertThrows(AuthException.class, () -> pApi().access(accessInput));
+ assertThat(thrown).hasMessageThat().contains("administrate server not permitted");
}
@Test
@@ -436,8 +433,8 @@ public class AccessIT extends AbstractDaemonTest {
accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.projects().name(allProjects.get()).access(accessInput);
+ assertThrows(
+ AuthException.class, () -> gApi.projects().name(allProjects.get()).access(accessInput));
}
@Test
@@ -465,8 +462,7 @@ public class AccessIT extends AbstractDaemonTest {
accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
- exception.expect(BadRequestException.class);
- pApi().access(accessInput);
+ assertThrows(BadRequestException.class, () -> pApi().access(accessInput));
}
@Test
@@ -479,9 +475,9 @@ public class AccessIT extends AbstractDaemonTest {
accessSectionInfo.permissions.put(Permission.PUSH, permissionInfo);
accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
-
- exception.expect(BadRequestException.class);
- gApi.projects().name(allProjects.get()).access(accessInput);
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.projects().name(allProjects.get()).access(accessInput));
}
@Test
@@ -492,8 +488,8 @@ public class AccessIT extends AbstractDaemonTest {
accessInput.remove.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.projects().name(allProjects.get()).access(accessInput);
+ assertThrows(
+ AuthException.class, () -> gApi.projects().name(allProjects.get()).access(accessInput));
}
@Test
@@ -572,7 +568,7 @@ public class AccessIT extends AbstractDaemonTest {
.file(ProjectConfig.PROJECT_CONFIG)
.asString();
cfg.fromText(config);
- assertThat(cfg.getString(access, refsFor, unknownPermission)).isEqualTo(registeredUsers);
+ assertThat(cfg).stringValue(access, refsFor, unknownPermission).isEqualTo(registeredUsers);
// Make permission change through API
ProjectAccessInput accessInput = newProjectAccessInput();
@@ -591,16 +587,20 @@ public class AccessIT extends AbstractDaemonTest {
.file(ProjectConfig.PROJECT_CONFIG)
.asString();
cfg.fromText(config);
- assertThat(cfg.getString(access, refsFor, unknownPermission)).isEqualTo(registeredUsers);
+ assertThat(cfg).stringValue(access, refsFor, unknownPermission).isEqualTo(registeredUsers);
}
@Test
public void allUsersCanOnlyInheritFromAllProjects() throws Exception {
ProjectAccessInput accessInput = newProjectAccessInput();
accessInput.parent = project.get();
- exception.expect(BadRequestException.class);
- exception.expectMessage(allUsers.get() + " must inherit from " + allProjects.get());
- gApi.projects().name(allUsers.get()).access(accessInput);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.projects().name(allUsers.get()).access(accessInput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(allUsers.get() + " must inherit from " + allProjects.get());
}
@Test
@@ -650,9 +650,9 @@ public class AccessIT extends AbstractDaemonTest {
String invalidRef = Constants.R_HEADS + "stable_*";
accessInput.add.put(invalidRef, accessSectionInfo);
- exception.expect(BadRequestException.class);
- exception.expectMessage("Invalid Name: " + invalidRef);
- pApi().access(accessInput);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> pApi().access(accessInput));
+ assertThat(thrown).hasMessageThat().contains("Invalid Name: " + invalidRef);
}
@Test
@@ -664,9 +664,9 @@ public class AccessIT extends AbstractDaemonTest {
String invalidRef = Constants.R_HEADS + "stable_*";
accessInput.add.put(invalidRef, accessSectionInfo);
- exception.expect(BadRequestException.class);
- exception.expectMessage("Invalid Name: " + invalidRef);
- pApi().accessChange(accessInput);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> pApi().accessChange(accessInput));
+ assertThat(thrown).hasMessageThat().contains("Invalid Name: " + invalidRef);
}
private ProjectApi pApi() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/BUILD b/javatests/com/google/gerrit/acceptance/rest/project/BUILD
index 6e66aab4d4..ca187a341c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/project/BUILD
@@ -1,9 +1,9 @@
load("@rules_java//java:defs.bzl", "java_library")
load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
-acceptance_tests(
- srcs = glob(["*IT.java"]),
- group = "rest_project",
+[acceptance_tests(
+ srcs = [f],
+ group = f[:f.index(".")],
labels = ["rest"],
deps = [
":project",
@@ -11,7 +11,7 @@ acceptance_tests(
":refassert",
"//lib/commons:lang",
],
-)
+) for f in glob(["*IT.java"])]
java_library(
name = "refassert",
@@ -31,8 +31,8 @@ java_library(
"ProjectAssert.java",
],
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//lib:guava",
"//lib/truth",
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CheckMergeabilityIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CheckMergeabilityIT.java
index 7667fc0c9f..a2f976a3ff 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CheckMergeabilityIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CheckMergeabilityIT.java
@@ -20,12 +20,14 @@ import com.google.common.base.Strings;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.common.MergeableInfo;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.RefSpec;
@@ -34,20 +36,19 @@ import org.junit.Test;
public class CheckMergeabilityIT extends AbstractDaemonTest {
- private Branch.NameKey branch;
+ @Inject private ProjectOperations projectOperations;
+
+ private BranchNameKey branch;
@Before
public void setUp() throws Exception {
- branch = new Branch.NameKey(project, "test");
- gApi.projects()
- .name(branch.getParentKey().get())
- .branch(branch.get())
- .create(new BranchInput());
+ branch = BranchNameKey.create(project, "test");
+ gApi.projects().name(branch.project().get()).branch(branch.branch()).create(new BranchInput());
}
@Test
public void checkMergeableCommit() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo
.branch("HEAD")
.commit()
@@ -82,7 +83,7 @@ public class CheckMergeabilityIT extends AbstractDaemonTest {
@Test
public void checkUnMergeableCommit() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo
.branch("HEAD")
.commit()
@@ -117,7 +118,7 @@ public class CheckMergeabilityIT extends AbstractDaemonTest {
@Test
public void checkOursMergeStrategy() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo
.branch("HEAD")
.commit()
@@ -211,7 +212,7 @@ public class CheckMergeabilityIT extends AbstractDaemonTest {
cherry.current().review(ReviewInput.approve());
cherry.current().submit();
- ObjectId remoteId = getRemoteHead();
+ ObjectId remoteId = projectOperations.project(project).getHead("master");
assertThat(remoteId).isNotEqualTo(commitId);
assertContentMerged("master", commitId.getName(), "recursive");
}
@@ -237,7 +238,7 @@ public class CheckMergeabilityIT extends AbstractDaemonTest {
@Test
public void checkInvalidStrategy() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
testRepo
.branch("HEAD")
.commit()
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
index 698f7e03c0..85d383eb29 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
@@ -16,14 +16,22 @@ package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_HEADS;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.entities.RefNames.REFS_HEADS;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.BranchInput;
@@ -31,23 +39,20 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.inject.Inject;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
public class CreateBranchIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
- private Branch.NameKey testBranch;
+ private BranchNameKey testBranch;
@Before
public void setUp() throws Exception {
- testBranch = new Branch.NameKey(project, "test");
+ testBranch = BranchNameKey.create(project, "test");
}
@Test
@@ -104,17 +109,25 @@ public class CreateBranchIT extends AbstractDaemonTest {
@Test
public void createMetaBranch() throws Exception {
String metaRef = RefNames.REFS_META + "foo";
- allow(metaRef, Permission.CREATE, REGISTERED_USERS);
- allow(metaRef, Permission.PUSH, REGISTERED_USERS);
- assertCreateSucceeds(new Branch.NameKey(project, metaRef));
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(metaRef).group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(metaRef).group(REGISTERED_USERS))
+ .update();
+ assertCreateSucceeds(BranchNameKey.create(project, metaRef));
}
@Test
public void createUserBranch_Conflict() throws Exception {
- allow(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE, REGISTERED_USERS);
- allow(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH, REGISTERED_USERS);
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_USERS + "*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_USERS + "*").group(REGISTERED_USERS))
+ .update();
assertCreateFails(
- new Branch.NameKey(allUsers, RefNames.refsUsers(new Account.Id(1))),
+ BranchNameKey.create(allUsers, RefNames.refsUsers(Account.id(1))),
RefNames.refsUsers(admin.id()),
ResourceConflictException.class,
"Not allowed to create user branch.");
@@ -122,10 +135,14 @@ public class CreateBranchIT extends AbstractDaemonTest {
@Test
public void createGroupBranch_Conflict() throws Exception {
- allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.CREATE, REGISTERED_USERS);
- allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.PUSH, REGISTERED_USERS);
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .update();
assertCreateFails(
- new Branch.NameKey(allUsers, RefNames.refsGroups(new AccountGroup.UUID("foo"))),
+ BranchNameKey.create(allUsers, RefNames.refsGroups(AccountGroup.uuid("foo"))),
RefNames.refsGroups(adminGroupUuid()),
ResourceConflictException.class,
"Not allowed to create group branch.");
@@ -133,81 +150,85 @@ public class CreateBranchIT extends AbstractDaemonTest {
@Test
public void createWithRevision() throws Exception {
- RevCommit revision = getRemoteHead(project, "master");
+ RevCommit revision = projectOperations.project(project).getHead("master");
// update master so that points to a different revision than the revision on which we create the
// new branch
pushTo("refs/heads/master");
- assertThat(getRemoteHead(project, "master")).isNotEqualTo(revision);
+ assertThat(projectOperations.project(project).getHead("master")).isNotEqualTo(revision);
BranchInput input = new BranchInput();
input.revision = revision.name();
BranchInfo created = branch(testBranch).create(input).get();
- assertThat(created.ref).isEqualTo(testBranch.get());
+ assertThat(created.ref).isEqualTo(testBranch.branch());
assertThat(created.revision).isEqualTo(revision.name());
- assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(revision);
+ assertThat(projectOperations.project(project).getHead(testBranch.branch())).isEqualTo(revision);
}
@Test
public void createWithoutSpecifyingRevision() throws Exception {
// If revision is not specified, the branch is created based on HEAD, which points to master.
- RevCommit expectedRevision = getRemoteHead(project, "master");
+ RevCommit expectedRevision = projectOperations.project(project).getHead("master");
BranchInput input = new BranchInput();
input.revision = null;
BranchInfo created = branch(testBranch).create(input).get();
- assertThat(created.ref).isEqualTo(testBranch.get());
+ assertThat(created.ref).isEqualTo(testBranch.branch());
assertThat(created.revision).isEqualTo(expectedRevision.name());
- assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(expectedRevision);
+ assertThat(projectOperations.project(project).getHead(testBranch.branch()))
+ .isEqualTo(expectedRevision);
}
@Test
public void createWithEmptyRevision() throws Exception {
// If revision is not specified, the branch is created based on HEAD, which points to master.
- RevCommit expectedRevision = getRemoteHead(project, "master");
+ RevCommit expectedRevision = projectOperations.project(project).getHead("master");
BranchInput input = new BranchInput();
input.revision = "";
BranchInfo created = branch(testBranch).create(input).get();
- assertThat(created.ref).isEqualTo(testBranch.get());
+ assertThat(created.ref).isEqualTo(testBranch.branch());
assertThat(created.revision).isEqualTo(expectedRevision.name());
- assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(expectedRevision);
+ assertThat(projectOperations.project(project).getHead(testBranch.branch()))
+ .isEqualTo(expectedRevision);
}
@Test
public void createRevisionIsTrimmed() throws Exception {
- RevCommit revision = getRemoteHead(project, "master");
+ RevCommit revision = projectOperations.project(project).getHead("master");
BranchInput input = new BranchInput();
input.revision = "\t" + revision.name();
BranchInfo created = branch(testBranch).create(input).get();
- assertThat(created.ref).isEqualTo(testBranch.get());
+ assertThat(created.ref).isEqualTo(testBranch.branch());
assertThat(created.revision).isEqualTo(revision.name());
- assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(revision);
+ assertThat(projectOperations.project(project).getHead(testBranch.branch())).isEqualTo(revision);
}
@Test
public void createWithBranchNameAsRevision() throws Exception {
- RevCommit expectedRevision = getRemoteHead(project, "master");
+ RevCommit expectedRevision = projectOperations.project(project).getHead("master");
BranchInput input = new BranchInput();
input.revision = "master";
BranchInfo created = branch(testBranch).create(input).get();
- assertThat(created.ref).isEqualTo(testBranch.get());
+ assertThat(created.ref).isEqualTo(testBranch.branch());
assertThat(created.revision).isEqualTo(expectedRevision.name());
- assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(expectedRevision);
+ assertThat(projectOperations.project(project).getHead(testBranch.branch()))
+ .isEqualTo(expectedRevision);
}
@Test
public void createWithFullBranchNameAsRevision() throws Exception {
- RevCommit expectedRevision = getRemoteHead(project, "master");
+ RevCommit expectedRevision = projectOperations.project(project).getHead("master");
BranchInput input = new BranchInput();
input.revision = "refs/heads/master";
BranchInfo created = branch(testBranch).create(input).get();
- assertThat(created.ref).isEqualTo(testBranch.get());
+ assertThat(created.ref).isEqualTo(testBranch.branch());
assertThat(created.revision).isEqualTo(expectedRevision.name());
- assertThat(getRemoteHead(project, testBranch.getShortName())).isEqualTo(expectedRevision);
+ assertThat(projectOperations.project(project).getHead(testBranch.branch()))
+ .isEqualTo(expectedRevision);
}
@Test
@@ -238,44 +259,51 @@ public class CreateBranchIT extends AbstractDaemonTest {
}
private void blockCreateReference() throws Exception {
- block("refs/*", Permission.CREATE, ANONYMOUS_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.CREATE).ref("refs/*").group(ANONYMOUS_USERS))
+ .update();
}
private void grantOwner() throws Exception {
- allow("refs/*", Permission.OWNER, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(REGISTERED_USERS))
+ .update();
}
- private BranchApi branch(Branch.NameKey branch) throws Exception {
- return gApi.projects().name(branch.getParentKey().get()).branch(branch.get());
+ private BranchApi branch(BranchNameKey branch) throws Exception {
+ return gApi.projects().name(branch.project().get()).branch(branch.branch());
}
- private void assertCreateSucceeds(Branch.NameKey branch) throws Exception {
+ private void assertCreateSucceeds(BranchNameKey branch) throws Exception {
BranchInfo created = branch(branch).create(new BranchInput()).get();
- assertThat(created.ref).isEqualTo(branch.get());
+ assertThat(created.ref).isEqualTo(branch.branch());
}
private void assertCreateFails(
- Branch.NameKey branch, Class<? extends RestApiException> errType, String errMsg)
+ BranchNameKey branch, Class<? extends RestApiException> errType, String errMsg)
throws Exception {
assertCreateFails(branch, null, errType, errMsg);
}
private void assertCreateFails(
- Branch.NameKey branch,
+ BranchNameKey branch,
String revision,
Class<? extends RestApiException> errType,
String errMsg)
throws Exception {
BranchInput in = new BranchInput();
in.revision = revision;
+ RestApiException thrown = assertThrows(errType, () -> branch(branch).create(in));
if (errMsg != null) {
- exception.expectMessage(errMsg);
+ assertThat(thrown).hasMessageThat().contains(errMsg);
}
- exception.expect(errType);
- branch(branch).create(in);
}
- private void assertCreateFails(Branch.NameKey branch, Class<? extends RestApiException> errType)
+ private void assertCreateFails(BranchNameKey branch, Class<? extends RestApiException> errType)
throws Exception {
assertCreateFails(branch, errType, null);
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
index 463532f71e..6b2baa7b48 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -19,7 +19,10 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjectInfo;
import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjectOwners;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.capabilityKey;
import static com.google.gerrit.server.project.ProjectConfig.PROJECT_CONFIG;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
@@ -30,8 +33,13 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
@@ -43,10 +51,6 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
@@ -72,6 +76,7 @@ import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.Test;
public class CreateProjectIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Test
@@ -86,7 +91,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
// for more extensive coverage of the LabelTypeInfo.
assertThat(p.labels).hasSize(1);
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNotNull();
assertProjectInfo(projectState.getProject(), p);
assertHead(newProjectName, "refs/heads/master");
@@ -162,7 +167,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
String newProjectName = name("newProject");
ProjectInfo p = gApi.projects().create(newProjectName).get();
assertThat(p.name).isEqualTo(newProjectName);
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNotNull();
assertProjectInfo(projectState.getProject(), p);
assertHead(newProjectName, "refs/heads/master");
@@ -175,7 +180,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
String newProjectName = name("newProject");
ProjectInfo p = gApi.projects().create(newProjectName + ".git").get();
assertThat(p.name).isEqualTo(newProjectName);
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNotNull();
assertProjectInfo(projectState.getProject(), p);
assertHead(newProjectName, "refs/heads/master");
@@ -186,7 +191,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
String newProjectName = name("newProject");
ProjectInfo p = gApi.projects().create(newProjectName + "/").get();
assertThat(p.name).isEqualTo(newProjectName);
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNotNull();
assertProjectInfo(projectState.getProject(), p);
assertHead(newProjectName, "refs/heads/master");
@@ -197,7 +202,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
String newProjectName = name("newProject/newProject");
ProjectInfo p = gApi.projects().create(newProjectName).get();
assertThat(p.name).isEqualTo(newProjectName);
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNotNull();
assertProjectInfo(projectState.getProject(), p);
assertHead(newProjectName, "refs/heads/master");
@@ -216,7 +221,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
in.requireChangeId = InheritableBoolean.TRUE;
ProjectInfo p = gApi.projects().create(in).get();
assertThat(p.name).isEqualTo(newProjectName);
- Project project = projectCache.get(new Project.NameKey(newProjectName)).getProject();
+ Project project = projectCache.get(Project.nameKey(newProjectName)).getProject();
assertProjectInfo(project, p);
assertThat(project.getDescription()).isEqualTo(in.description);
assertThat(project.getConfiguredSubmitType()).isEqualTo(in.submitType);
@@ -242,7 +247,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
in.name = childName;
in.parent = parentName;
gApi.projects().create(in);
- Project project = projectCache.get(new Project.NameKey(childName)).getProject();
+ Project project = projectCache.get(Project.nameKey(childName)).getProject();
assertThat(project.getParentName()).isEqualTo(in.parent);
}
@@ -265,12 +270,12 @@ public class CreateProjectIT extends AbstractDaemonTest {
in.owners.add(
Integer.toString(
groupCache
- .get(new AccountGroup.NameKey("Administrators"))
+ .get(AccountGroup.nameKey("Administrators"))
.orElse(null)
.getId()
.get())); // by ID
gApi.projects().create(in);
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
Set<AccountGroup.UUID> expectedOwnerIds = Sets.newHashSetWithExpectedSize(3);
expectedOwnerIds.add(SystemGroupBackend.ANONYMOUS_USERS);
expectedOwnerIds.add(SystemGroupBackend.REGISTERED_USERS);
@@ -323,7 +328,12 @@ public class CreateProjectIT extends AbstractDaemonTest {
@Test
public void createProjectWithCapability() throws Exception {
- allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(
+ allowCapability(GlobalCapability.CREATE_PROJECT)
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
try {
requestScopeOperations.setApiUser(user.id());
ProjectInput in = new ProjectInput();
@@ -331,8 +341,12 @@ public class CreateProjectIT extends AbstractDaemonTest {
ProjectInfo p = gApi.projects().create(in).get();
assertThat(p.name).isEqualTo(in.name);
} finally {
- removeGlobalCapabilities(
- SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
+ projectOperations
+ .allProjectsForUpdate()
+ .remove(
+ capabilityKey(GlobalCapability.CREATE_PROJECT)
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
}
}
@@ -355,7 +369,12 @@ public class CreateProjectIT extends AbstractDaemonTest {
public void createProjectWithCreateProjectCapabilityAndParentNotVisible() throws Exception {
Project parent = projectCache.get(allProjects).getProject();
parent.setState(com.google.gerrit.extensions.client.ProjectState.HIDDEN);
- allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(
+ allowCapability(GlobalCapability.CREATE_PROJECT)
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
try {
requestScopeOperations.setApiUser(user.id());
ProjectInput in = new ProjectInput();
@@ -364,8 +383,12 @@ public class CreateProjectIT extends AbstractDaemonTest {
assertThat(p.name).isEqualTo(in.name);
} finally {
parent.setState(com.google.gerrit.extensions.client.ProjectState.ACTIVE);
- removeGlobalCapabilities(
- SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
+ projectOperations
+ .allProjectsForUpdate()
+ .remove(
+ capabilityKey(GlobalCapability.CREATE_PROJECT)
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
}
}
@@ -447,13 +470,13 @@ public class CreateProjectIT extends AbstractDaemonTest {
}
private void assertHead(String projectName, String expectedRef) throws Exception {
- try (Repository repo = repoManager.openRepository(new Project.NameKey(projectName))) {
+ try (Repository repo = repoManager.openRepository(Project.nameKey(projectName))) {
assertThat(repo.exactRef(Constants.HEAD).getTarget().getName()).isEqualTo(expectedRef);
}
}
private void assertEmptyCommit(String projectName, String... refs) throws Exception {
- Project.NameKey projectKey = new Project.NameKey(projectName);
+ Project.NameKey projectKey = Project.nameKey(projectName);
try (Repository repo = repoManager.openRepository(projectKey);
RevWalk rw = new RevWalk(repo);
TreeWalk tw = new TreeWalk(rw.getObjectReader())) {
@@ -469,12 +492,11 @@ public class CreateProjectIT extends AbstractDaemonTest {
private void assertCreateFails(ProjectInput in, Class<? extends RestApiException> errType)
throws Exception {
- exception.expect(errType);
- gApi.projects().create(in);
+ assertThrows(errType, () -> gApi.projects().create(in));
}
private Optional<String> readProjectConfig(String projectName) throws Exception {
- try (Repository repo = repoManager.openRepository(new Project.NameKey(projectName));
+ try (Repository repo = repoManager.openRepository(Project.nameKey(projectName));
TestRepository<Repository> tr = new TestRepository<>(repo)) {
RevWalk rw = tr.getRevWalk();
Ref ref = repo.exactRef(RefNames.REFS_CONFIG);
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
index ccdc4973dd..5636014f91 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
@@ -15,6 +15,8 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
@@ -25,6 +27,8 @@ import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -32,8 +36,6 @@ import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.inject.Inject;
import org.junit.Before;
import org.junit.Test;
@@ -42,12 +44,12 @@ public class DeleteBranchIT extends AbstractDaemonTest {
@Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
- private Branch.NameKey testBranch;
+ private BranchNameKey testBranch;
@Before
public void setUp() throws Exception {
project = projectOperations.newProject().create();
- testBranch = new Branch.NameKey(project, "test");
+ testBranch = BranchNameKey.create(project, "test");
branch(testBranch).create(new BranchInput());
}
@@ -100,7 +102,7 @@ public class DeleteBranchIT extends AbstractDaemonTest {
@Test
public void deleteBranchByRestWithoutRefsHeadsPrefix() throws Exception {
grantDelete();
- String ref = testBranch.getShortName();
+ String ref = testBranch.shortName();
assertThat(ref).doesNotMatch(R_HEADS);
assertDeleteByRestSucceeds(testBranch, ref);
}
@@ -108,14 +110,14 @@ public class DeleteBranchIT extends AbstractDaemonTest {
@Test
public void deleteBranchByRestWithFullName() throws Exception {
grantDelete();
- assertDeleteByRestSucceeds(testBranch, testBranch.get());
+ assertDeleteByRestSucceeds(testBranch, testBranch.branch());
}
@Test
public void deleteBranchByRestFailsWithUnencodedFullName() throws Exception {
grantDelete();
RestResponse r =
- userRestSession.delete("/projects/" + project.get() + "/branches/" + testBranch.get());
+ userRestSession.delete("/projects/" + project.get() + "/branches/" + testBranch.branch());
r.assertNotFound();
branch(testBranch).get();
}
@@ -123,10 +125,14 @@ public class DeleteBranchIT extends AbstractDaemonTest {
@Test
public void deleteMetaBranch() throws Exception {
String metaRef = RefNames.REFS_META + "foo";
- allow(metaRef, Permission.CREATE, REGISTERED_USERS);
- allow(metaRef, Permission.PUSH, REGISTERED_USERS);
-
- Branch.NameKey metaBranch = new Branch.NameKey(project, metaRef);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(metaRef).group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(metaRef).group(REGISTERED_USERS))
+ .update();
+
+ BranchNameKey metaBranch = BranchNameKey.create(project, metaRef);
branch(metaBranch).create(new BranchInput());
grantDelete();
@@ -135,22 +141,36 @@ public class DeleteBranchIT extends AbstractDaemonTest {
@Test
public void deleteUserBranch_Conflict() throws Exception {
- allow(allUsers, RefNames.REFS_USERS + "*", Permission.CREATE, REGISTERED_USERS);
- allow(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH, REGISTERED_USERS);
-
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Not allowed to delete user branch.");
- branch(new Branch.NameKey(allUsers, RefNames.refsUsers(admin.id()))).delete();
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_USERS + "*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_USERS + "*").group(REGISTERED_USERS))
+ .update();
+
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () -> branch(BranchNameKey.create(allUsers, RefNames.refsUsers(admin.id()))).delete());
+ assertThat(thrown).hasMessageThat().contains("Not allowed to delete user branch.");
}
@Test
public void deleteGroupBranch_Conflict() throws Exception {
- allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.CREATE, REGISTERED_USERS);
- allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.PUSH, REGISTERED_USERS);
-
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Not allowed to delete group branch.");
- branch(new Branch.NameKey(allUsers, RefNames.refsGroups(adminGroupUuid()))).delete();
+ projectOperations
+ .project(allUsers)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS))
+ .update();
+
+ ResourceConflictException thrown =
+ assertThrows(
+ ResourceConflictException.class,
+ () ->
+ branch(BranchNameKey.create(allUsers, RefNames.refsGroups(adminGroupUuid())))
+ .delete());
+ assertThat(thrown).hasMessageThat().contains("Not allowed to delete group branch.");
}
@Test
@@ -158,7 +178,7 @@ public class DeleteBranchIT extends AbstractDaemonTest {
MethodNotAllowedException thrown =
assertThrows(
MethodNotAllowedException.class,
- () -> branch(new Branch.NameKey(allUsers, RefNames.REFS_CONFIG)).delete());
+ () -> branch(BranchNameKey.create(allUsers, RefNames.REFS_CONFIG)).delete());
assertThat(thrown).hasMessageThat().contains("not allowed to delete branch refs/meta/config");
}
@@ -167,31 +187,47 @@ public class DeleteBranchIT extends AbstractDaemonTest {
MethodNotAllowedException thrown =
assertThrows(
MethodNotAllowedException.class,
- () -> branch(new Branch.NameKey(allUsers, RefNames.HEAD)).delete());
+ () -> branch(BranchNameKey.create(allUsers, RefNames.HEAD)).delete());
assertThat(thrown).hasMessageThat().contains("not allowed to delete HEAD");
}
private void blockForcePush() throws Exception {
- block("refs/heads/*", Permission.PUSH, ANONYMOUS_USERS).setForce(true);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS).force(true))
+ .update();
}
private void grantForcePush() throws Exception {
- grant(project, "refs/heads/*", Permission.PUSH, true, ANONYMOUS_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS).force(true))
+ .update();
}
private void grantDelete() throws Exception {
- allow("refs/*", Permission.DELETE, ANONYMOUS_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE).ref("refs/*").group(ANONYMOUS_USERS))
+ .update();
}
private void grantOwner() throws Exception {
- allow("refs/*", Permission.OWNER, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(REGISTERED_USERS))
+ .update();
}
- private BranchApi branch(Branch.NameKey branch) throws Exception {
- return gApi.projects().name(branch.getParentKey().get()).branch(branch.get());
+ private BranchApi branch(BranchNameKey branch) throws Exception {
+ return gApi.projects().name(branch.project().get()).branch(branch.branch());
}
- private void assertDeleteByRestSucceeds(Branch.NameKey branch, String ref) throws Exception {
+ private void assertDeleteByRestSucceeds(BranchNameKey branch, String ref) throws Exception {
RestResponse r =
userRestSession.delete(
"/projects/"
@@ -199,24 +235,21 @@ public class DeleteBranchIT extends AbstractDaemonTest {
+ "/branches/"
+ IdString.fromDecoded(ref).encoded());
r.assertNoContent();
- exception.expect(ResourceNotFoundException.class);
- branch(branch).get();
+ assertThrows(ResourceNotFoundException.class, () -> branch(branch).get());
}
- private void assertDeleteSucceeds(Branch.NameKey branch) throws Exception {
+ private void assertDeleteSucceeds(BranchNameKey branch) throws Exception {
assertThat(branch(branch).get().canDelete).isTrue();
String branchRev = branch(branch).get().revision;
branch(branch).delete();
eventRecorder.assertRefUpdatedEvents(
- project.get(), branch.get(), null, branchRev, branchRev, null);
- exception.expect(ResourceNotFoundException.class);
- branch(branch).get();
+ project.get(), branch.branch(), null, branchRev, branchRev, null);
+ assertThrows(ResourceNotFoundException.class, () -> branch(branch).get());
}
- private void assertDeleteForbidden(Branch.NameKey branch) throws Exception {
+ private void assertDeleteForbidden(BranchNameKey branch) throws Exception {
assertThat(branch(branch).get().canDelete).isNull();
- exception.expect(AuthException.class);
- exception.expectMessage("not permitted: delete");
- branch(branch).delete();
+ AuthException thrown = assertThrows(AuthException.class, () -> branch(branch).delete());
+ assertThat(thrown).hasMessageThat().contains("not permitted: delete");
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
index 4d7212312d..ad90109642 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
@@ -25,8 +26,10 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.DeleteBranchesInput;
import com.google.gerrit.extensions.api.projects.ProjectApi;
@@ -34,7 +37,6 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.inject.Inject;
import java.util.HashMap;
import java.util.List;
@@ -48,12 +50,17 @@ public class DeleteBranchesIT extends AbstractDaemonTest {
private static final ImmutableList<String> BRANCHES =
ImmutableList.of("refs/heads/test-1", "refs/heads/test-2", "test-3", "refs/meta/foo");
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Before
public void setUp() throws Exception {
- allow("refs/*", Permission.CREATE, REGISTERED_USERS);
- allow("refs/*", Permission.PUSH, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.PUSH).ref("refs/*").group(REGISTERED_USERS))
+ .update();
for (String name : BRANCHES) {
project().branch(name).create(new BranchInput());
}
@@ -77,12 +84,8 @@ public class DeleteBranchesIT extends AbstractDaemonTest {
DeleteBranchesInput input = new DeleteBranchesInput();
input.branches = branchToDelete;
requestScopeOperations.setApiUser(user.id());
- try {
- project().deleteBranches(input);
- fail("Expected AuthException");
- } catch (AuthException e) {
- assertThat(e).hasMessageThat().isEqualTo("not permitted: delete on refs/heads/test-1");
- }
+ AuthException thrown = assertThrows(AuthException.class, () -> project().deleteBranches(input));
+ assertThat(thrown).hasMessageThat().isEqualTo("not permitted: delete on refs/heads/test-1");
requestScopeOperations.setApiUser(admin.id());
assertBranches(BRANCHES);
}
@@ -92,12 +95,9 @@ public class DeleteBranchesIT extends AbstractDaemonTest {
DeleteBranchesInput input = new DeleteBranchesInput();
input.branches = BRANCHES;
requestScopeOperations.setApiUser(user.id());
- try {
- project().deleteBranches(input);
- fail("Expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e).hasMessageThat().isEqualTo(errorMessageForBranches(BRANCHES));
- }
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> project().deleteBranches(input));
+ assertThat(thrown).hasMessageThat().isEqualTo(errorMessageForBranches(BRANCHES));
requestScopeOperations.setApiUser(admin.id());
assertBranches(BRANCHES);
}
@@ -108,14 +108,11 @@ public class DeleteBranchesIT extends AbstractDaemonTest {
List<String> branches = Lists.newArrayList(BRANCHES);
branches.add("refs/heads/does-not-exist");
input.branches = branches;
- try {
- project().deleteBranches(input);
- fail("Expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(errorMessageForBranches(ImmutableList.of("refs/heads/does-not-exist")));
- }
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> project().deleteBranches(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(errorMessageForBranches(ImmutableList.of("refs/heads/does-not-exist")));
assertBranchesDeleted(BRANCHES);
}
@@ -127,40 +124,37 @@ public class DeleteBranchesIT extends AbstractDaemonTest {
List<String> branches = Lists.newArrayList("refs/heads/does-not-exist");
branches.addAll(BRANCHES);
input.branches = branches;
- try {
- project().deleteBranches(input);
- fail("Expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(errorMessageForBranches(ImmutableList.of("refs/heads/does-not-exist")));
- }
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> project().deleteBranches(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(errorMessageForBranches(ImmutableList.of("refs/heads/does-not-exist")));
assertBranchesDeleted(BRANCHES);
}
@Test
public void missingInput() throws Exception {
DeleteBranchesInput input = null;
- exception.expect(BadRequestException.class);
- exception.expectMessage("branches must be specified");
- project().deleteBranches(input);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> project().deleteBranches(input));
+ assertThat(thrown).hasMessageThat().contains("branches must be specified");
}
@Test
public void missingBranchList() throws Exception {
DeleteBranchesInput input = new DeleteBranchesInput();
- exception.expect(BadRequestException.class);
- exception.expectMessage("branches must be specified");
- project().deleteBranches(input);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> project().deleteBranches(input));
+ assertThat(thrown).hasMessageThat().contains("branches must be specified");
}
@Test
public void emptyBranchList() throws Exception {
DeleteBranchesInput input = new DeleteBranchesInput();
input.branches = Lists.newArrayList();
- exception.expect(BadRequestException.class);
- exception.expectMessage("branches must be specified");
- project().deleteBranches(input);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> project().deleteBranches(input));
+ assertThat(thrown).hasMessageThat().contains("branches must be specified");
}
@Test
@@ -198,7 +192,7 @@ public class DeleteBranchesIT extends AbstractDaemonTest {
private HashMap<String, RevCommit> initialRevisions(List<String> branches) throws Exception {
HashMap<String, RevCommit> result = new HashMap<>();
for (String branch : branches) {
- result.put(branch, getRemoteHead(project, branch));
+ result.put(branch, projectOperations.project(project).getHead(branch));
}
return result;
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
index 07bb2b1d30..9770031200 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
@@ -15,12 +15,16 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.TagApi;
@@ -35,6 +39,7 @@ import org.junit.Test;
public class DeleteTagIT extends AbstractDaemonTest {
private static final String TAG = "refs/tags/test";
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Before
@@ -97,19 +102,35 @@ public class DeleteTagIT extends AbstractDaemonTest {
}
private void blockForcePush() throws Exception {
- block("refs/tags/*", Permission.PUSH, ANONYMOUS_USERS).setForce(true);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.PUSH).ref("refs/tags/*").group(ANONYMOUS_USERS).force(true))
+ .update();
}
private void grantForcePush() throws Exception {
- grant(project, "refs/tags/*", Permission.PUSH, true, ANONYMOUS_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.PUSH).ref("refs/tags/*").group(ANONYMOUS_USERS).force(true))
+ .update();
}
private void grantDelete() throws Exception {
- allow("refs/tags/*", Permission.DELETE, ANONYMOUS_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.DELETE).ref("refs/tags/*").group(ANONYMOUS_USERS))
+ .update();
}
private void grantOwner() throws Exception {
- allow("refs/tags/*", Permission.OWNER, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/tags/*").group(REGISTERED_USERS))
+ .update();
}
private TagApi tag() throws Exception {
@@ -122,14 +143,12 @@ public class DeleteTagIT extends AbstractDaemonTest {
String tagRev = tagInfo.revision;
tag().delete();
eventRecorder.assertRefUpdatedEvents(project.get(), TAG, null, tagRev, tagRev, null);
- exception.expect(ResourceNotFoundException.class);
- tag().get();
+ assertThrows(ResourceNotFoundException.class, () -> tag().get());
}
private void assertDeleteForbidden() throws Exception {
assertThat(tag().get().canDelete).isNull();
- exception.expect(AuthException.class);
- exception.expectMessage("not permitted: delete");
- tag().delete();
+ AuthException thrown = assertThrows(AuthException.class, () -> tag().delete());
+ assertThat(thrown).hasMessageThat().contains("not permitted: delete");
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
index fae9d00d4e..46e2345243 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -23,6 +24,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.extensions.api.projects.DeleteTagsInput;
import com.google.gerrit.extensions.api.projects.ProjectApi;
@@ -41,6 +43,7 @@ public class DeleteTagsIT extends AbstractDaemonTest {
private static final ImmutableList<String> TAGS =
ImmutableList.of("refs/tags/test-1", "refs/tags/test-2", "refs/tags/test-3", "test-4");
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Before
@@ -66,12 +69,9 @@ public class DeleteTagsIT extends AbstractDaemonTest {
DeleteTagsInput input = new DeleteTagsInput();
input.tags = TAGS;
requestScopeOperations.setApiUser(user.id());
- try {
- project().deleteTags(input);
- fail("Expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e).hasMessageThat().isEqualTo(errorMessageForTags(TAGS));
- }
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> project().deleteTags(input));
+ assertThat(thrown).hasMessageThat().isEqualTo(errorMessageForTags(TAGS));
requestScopeOperations.setApiUser(admin.id());
assertTags(TAGS);
}
@@ -82,14 +82,11 @@ public class DeleteTagsIT extends AbstractDaemonTest {
List<String> tags = Lists.newArrayList(TAGS);
tags.add("refs/tags/does-not-exist");
input.tags = tags;
- try {
- project().deleteTags(input);
- fail("Expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(errorMessageForTags(ImmutableList.of("refs/tags/does-not-exist")));
- }
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> project().deleteTags(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(errorMessageForTags(ImmutableList.of("refs/tags/does-not-exist")));
assertTagsDeleted();
}
@@ -101,14 +98,11 @@ public class DeleteTagsIT extends AbstractDaemonTest {
List<String> tags = Lists.newArrayList("refs/tags/does-not-exist");
tags.addAll(TAGS);
input.tags = tags;
- try {
- project().deleteTags(input);
- fail("Expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(errorMessageForTags(ImmutableList.of("refs/tags/does-not-exist")));
- }
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> project().deleteTags(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(errorMessageForTags(ImmutableList.of("refs/tags/does-not-exist")));
assertTagsDeleted();
}
@@ -128,7 +122,7 @@ public class DeleteTagsIT extends AbstractDaemonTest {
HashMap<String, RevCommit> result = new HashMap<>();
for (String tag : tags) {
String ref = prefixRef(tag);
- result.put(ref, getRemoteHead(project, ref));
+ result.put(ref, projectOperations.project(project).getHead(ref));
}
return result;
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java
index 63f41ad34b..a7f3174f03 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java
@@ -15,25 +15,26 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Branch;
import org.junit.Before;
import org.junit.Test;
@NoHttpd
public class FileBranchIT extends AbstractDaemonTest {
- private Branch.NameKey branch;
+ private BranchNameKey branch;
@Before
public void setUp() throws Exception {
- branch = new Branch.NameKey(project, "master");
+ branch = BranchNameKey.create(project, "master");
PushOneCommit.Result change = createChange();
approve(change.getChangeId());
revision(change).submit();
@@ -45,12 +46,12 @@ public class FileBranchIT extends AbstractDaemonTest {
assertThat(content.asString()).isEqualTo(PushOneCommit.FILE_CONTENT);
}
- @Test(expected = ResourceNotFoundException.class)
+ @Test
public void getNonExistingFile() throws Exception {
- branch().file("does-not-exist");
+ assertThrows(ResourceNotFoundException.class, () -> branch().file("does-not-exist"));
}
private BranchApi branch() throws Exception {
- return gApi.projects().name(branch.getParentKey().get()).branch(branch.get());
+ return gApi.projects().name(branch.project().get()).branch(branch.branch());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
index 48527af552..0bdaad02e0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
@@ -19,7 +19,7 @@ import com.google.gerrit.acceptance.GcAssert;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.inject.Inject;
import org.junit.Before;
import org.junit.Test;
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
index d7365781b0..8911163da4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
@@ -14,14 +14,16 @@
package com.google.gerrit.acceptance.rest.project;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProjectInfo;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import org.junit.Test;
@@ -69,8 +71,10 @@ public class GetChildProjectIT extends AbstractDaemonTest {
}
private void assertChildNotFound(Project.NameKey parent, String child) throws Exception {
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage(child);
- gApi.projects().name(parent.get()).child(child).get();
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.projects().name(parent.get()).child(child).get());
+ assertThat(thrown).hasMessageThat().contains(child);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
index 18c706b9f5..b18db81256 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
@@ -15,14 +15,18 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.inject.Inject;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@@ -32,12 +36,14 @@ import org.junit.Before;
import org.junit.Test;
public class GetCommitIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+
private TestRepository<Repository> repo;
@Before
public void setUp() throws Exception {
repo = GitUtil.newTestRepository(repoManager.openRepository(project));
- blockRead("refs/*");
+ blockRead();
}
@After
@@ -107,8 +113,17 @@ public class GetCommitIT extends AbstractDaemonTest {
@Test
public void getOpenChange_NotFound() throws Exception {
+ // Need to unblock read to allow the push operation to succeed if not, when retrieving the
+ // advertised refs during
+ // the push, the client won't be sent the initial commit and will send it again as part of the
+ // change.
+ unblockRead();
+
PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
r.assertOkStatus();
+
+ // Re-blocking the read
+ blockRead();
assertNotFound(r.getCommit());
}
@@ -119,6 +134,14 @@ public class GetCommitIT extends AbstractDaemonTest {
}
}
+ private void blockRead() {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ }
+
private void assertNotFound(ObjectId id) throws Exception {
userRestSession.get("/projects/" + project.get() + "/commits/" + id.name()).assertNotFound();
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
index 989050cc30..e9aa589b38 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -54,8 +55,9 @@ public class GetProjectIT extends AbstractDaemonTest {
assertThat(p.name).isEqualTo(name);
}
- @Test(expected = ResourceNotFoundException.class)
+ @Test
public void getProjectNotExisting() throws Exception {
- gApi.projects().name("does-not-exist").get();
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.projects().name("does-not-exist").get());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
index ec1c708c20..91a2c4bd83 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
@@ -15,36 +15,48 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.gerrit.acceptance.rest.project.RefAssert.assertRefs;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.inject.Inject;
import org.junit.Test;
@NoHttpd
public class ListBranchesIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Test
public void listBranchesOfNonExistingProject_NotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.projects().name("non-existing").branches().get();
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.projects().name("non-existing").branches().get());
}
@Test
public void listBranchesOfNonVisibleProject_NotFound() throws Exception {
- blockRead("refs/*");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
- exception.expect(ResourceNotFoundException.class);
- gApi.projects().name(project.get()).branches().get();
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.projects().name(project.get()).branches().get());
}
@Test
@@ -59,7 +71,7 @@ public class ListBranchesIT extends AbstractDaemonTest {
public void listBranches() throws Exception {
String master = pushTo("refs/heads/master").getCommit().name();
String dev = pushTo("refs/heads/dev").getCommit().name();
- String refsConfig = getRemoteHead(project, RefNames.REFS_CONFIG).name();
+ String refsConfig = projectOperations.project(project).getHead(RefNames.REFS_CONFIG).name();
assertRefs(
ImmutableList.of(
branch("HEAD", "master", false),
@@ -71,7 +83,11 @@ public class ListBranchesIT extends AbstractDaemonTest {
@Test
public void listBranchesSomeHidden() throws Exception {
- blockRead("refs/heads/dev");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/dev").group(REGISTERED_USERS))
+ .update();
String master = pushTo("refs/heads/master").getCommit().name();
pushTo("refs/heads/dev");
requestScopeOperations.setApiUser(user.id());
@@ -84,7 +100,11 @@ public class ListBranchesIT extends AbstractDaemonTest {
@Test
public void listBranchesHeadHidden() throws Exception {
- blockRead("refs/heads/master");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS))
+ .update();
pushTo("refs/heads/master");
String dev = pushTo("refs/heads/dev").getCommit().name();
requestScopeOperations.setApiUser(user.id());
@@ -96,7 +116,10 @@ public class ListBranchesIT extends AbstractDaemonTest {
public void listBranchesUsingPagination() throws Exception {
BranchInfo head = branch("HEAD", "master", false);
BranchInfo refsConfig =
- branch(RefNames.REFS_CONFIG, getRemoteHead(project, RefNames.REFS_CONFIG).name(), false);
+ branch(
+ RefNames.REFS_CONFIG,
+ projectOperations.project(project).getHead(RefNames.REFS_CONFIG).name(),
+ false);
BranchInfo master =
branch("refs/heads/master", pushTo("refs/heads/master").getCommit().getName(), false);
BranchInfo branch1 =
@@ -170,11 +193,6 @@ public class ListBranchesIT extends AbstractDaemonTest {
}
private void assertBadRequest(ListRefsRequest<BranchInfo> req) throws Exception {
- try {
- req.get();
- fail("Expected BadRequestException");
- } catch (BadRequestException e) {
- // Expected
- }
+ assertThrows(BadRequestException.class, () -> req.get());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
index 7746820203..7535deac46 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
@@ -14,13 +14,15 @@
package com.google.gerrit.acceptance.rest.project;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertThatNameList;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.Test;
@@ -31,9 +33,11 @@ public class ListChildProjectsIT extends AbstractDaemonTest {
@Test
public void listChildrenOfNonExistingProject_NotFound() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("non-existing");
- gApi.projects().name(name("non-existing")).child("children");
+ ResourceNotFoundException thrown =
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.projects().name(name("non-existing")).child("children"));
+ assertThat(thrown).hasMessageThat().contains("non-existing");
}
@Test
@@ -47,7 +51,7 @@ public class ListChildProjectsIT extends AbstractDaemonTest {
Project.NameKey child1_1 = projectOperations.newProject().parent(child1).create();
Project.NameKey child1_2 = projectOperations.newProject().parent(child1).create();
- assertThatNameList(gApi.projects().name(child1.get()).children()).isOrdered();
+ assertThatNameList(gApi.projects().name(child1.get()).children()).isInOrder();
assertThatNameList(gApi.projects().name(child1.get()).children())
.containsExactly(child1_1, child1_2);
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
index 9dba8cfe81..29d3eb2363 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
@@ -16,7 +16,9 @@ package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertThatNameList;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
@@ -31,6 +33,7 @@ import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.api.projects.Projects.ListRequest;
@@ -39,9 +42,7 @@ import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.json.OutputFormat;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.project.ProjectCacheImpl;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.restapi.project.ListProjects;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
@@ -65,7 +66,7 @@ public class ListProjectsIT extends AbstractDaemonTest {
Project.NameKey someProject = projectOperations.newProject().create();
assertThatNameList(gApi.projects().list().get())
.containsExactly(allProjects, allUsers, project, someProject);
- assertThatNameList(gApi.projects().list().get()).isOrdered();
+ assertThatNameList(gApi.projects().list().get()).isInOrder();
}
@Test
@@ -73,10 +74,11 @@ public class ListProjectsIT extends AbstractDaemonTest {
requestScopeOperations.setApiUser(user.id());
assertThatNameList(gApi.projects().list().get()).contains(project);
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.block(u.getConfig(), Permission.READ, REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
assertThatNameList(gApi.projects().list().get()).doesNotContain(project);
}
@@ -243,7 +245,7 @@ public class ListProjectsIT extends AbstractDaemonTest {
int n = 5;
assertThat(all).hasSize(n);
assertThatNameList(gApi.projects().list().withPrefix(pre).withStart(n - 1).get())
- .containsExactly(new Project.NameKey(Iterables.getLast(all).name));
+ .containsExactly(Project.nameKey(Iterables.getLast(all).name));
}
@Test
@@ -337,11 +339,6 @@ public class ListProjectsIT extends AbstractDaemonTest {
}
private void assertBadRequest(ListRequest req) throws Exception {
- try {
- req.get();
- fail("Expected BadRequestException");
- } catch (BadRequestException expected) {
- // Expected.
- }
+ assertThrows(BadRequestException.class, () -> req.get());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java b/javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java
index 3b5a3a4e76..3f583a220a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ProjectAssert.java
@@ -21,10 +21,10 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.truth.IterableSubject;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.project.ProjectState;
import java.util.List;
import java.util.Set;
@@ -38,7 +38,7 @@ public class ProjectAssert {
.that(Url.decode(info.id))
.isEqualTo(info.name);
}
- return assertThat(Iterables.transform(actual, p -> new Project.NameKey(p.name)));
+ return assertThat(Iterables.transform(actual, p -> Project.nameKey(p.name)));
}
public static void assertProjectInfo(Project project, ProjectInfo info) {
@@ -47,7 +47,7 @@ public class ProjectAssert {
assertThat(info.name).isEqualTo(project.getName());
}
assertThat(Url.decode(info.id)).isEqualTo(project.getName());
- Project.NameKey parentName = project.getParent(new Project.NameKey("All-Projects"));
+ Project.NameKey parentName = project.getParent(Project.nameKey("All-Projects"));
if (parentName != null) {
assertThat(info.parent).isEqualTo(parentName.get());
} else {
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
index bf2a5342b2..76c30a956b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
@@ -20,8 +20,8 @@ import static com.google.gerrit.acceptance.GitUtil.fetch;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
import java.util.Arrays;
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/RefAssert.java b/javatests/com/google/gerrit/acceptance/rest/project/RefAssert.java
index b3e3d2f8cf..a93fc0f177 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/RefAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/RefAssert.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.collect.Iterables;
import com.google.gerrit.extensions.api.projects.RefInfo;
@@ -38,10 +39,12 @@ public class RefAssert {
public static void assertRefInfo(RefInfo expected, RefInfo actual) {
assertThat(actual.ref).isEqualTo(expected.ref);
if (expected.revision != null) {
- assertThat(actual.revision).named("revision of " + actual.ref).isEqualTo(expected.revision);
+ assertWithMessage("revision of " + actual.ref)
+ .that(actual.revision)
+ .isEqualTo(expected.revision);
}
- assertThat(toBoolean(actual.canDelete))
- .named("can delete " + actual.ref)
+ assertWithMessage("can delete " + actual.ref)
+ .that(toBoolean(actual.canDelete))
.isEqualTo(toBoolean(expected.canDelete));
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
index c945a2b359..f5d2db464e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
@@ -15,7 +15,10 @@
package com.google.gerrit.acceptance.rest.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.common.collect.FluentIterable;
@@ -23,6 +26,7 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
@@ -62,6 +66,7 @@ public class TagsIT extends AbstractDaemonTest {
+ "=XFeC\n"
+ "-----END PGP SIGNATURE-----";
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Before
@@ -76,29 +81,39 @@ public class TagsIT extends AbstractDaemonTest {
@Test
public void listTagsOfNonExistingProject() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.projects().name("does-not-exist").tags().get();
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.projects().name("does-not-exist").tags().get());
}
@Test
public void getTagOfNonExistingProject() throws Exception {
- exception.expect(ResourceNotFoundException.class);
- gApi.projects().name("does-not-exist").tag("tag").get();
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.projects().name("does-not-exist").tag("tag").get());
}
@Test
public void listTagsOfNonVisibleProject() throws Exception {
- blockRead("refs/*");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
requestScopeOperations.setApiUser(user.id());
- exception.expect(ResourceNotFoundException.class);
- gApi.projects().name(project.get()).tags().get();
+ assertThrows(
+ ResourceNotFoundException.class, () -> gApi.projects().name(project.get()).tags().get());
}
@Test
public void getTagOfNonVisibleProject() throws Exception {
- blockRead("refs/*");
- exception.expect(ResourceNotFoundException.class);
- gApi.projects().name(project.get()).tag("tag").get();
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> gApi.projects().name(project.get()).tag("tag").get());
}
@Test
@@ -173,7 +188,11 @@ public class TagsIT extends AbstractDaemonTest {
assertThat(tags.get(1).ref).isEqualTo(R_TAGS + tag2.ref);
assertThat(tags.get(1).revision).isEqualTo(tag2.revision);
- blockRead("refs/heads/hidden");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/hidden").group(REGISTERED_USERS))
+ .update();
tags = getTags().get();
assertThat(tags).hasSize(1);
assertThat(tags.get(0).ref).isEqualTo(R_TAGS + tag1.ref);
@@ -182,7 +201,7 @@ public class TagsIT extends AbstractDaemonTest {
@Test
public void lightweightTag() throws Exception {
- grant(project, R_TAGS + "*", Permission.CREATE);
+ grantTagPermissions();
PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/heads/master");
@@ -214,7 +233,7 @@ public class TagsIT extends AbstractDaemonTest {
@Test
public void annotatedTag() throws Exception {
- grant(project, R_TAGS + "*", Permission.CREATE_TAG);
+ grantTagPermissions();
PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/heads/master");
@@ -261,30 +280,38 @@ public class TagsIT extends AbstractDaemonTest {
assertThat(result.ref).isEqualTo(R_TAGS + "test");
input.ref = "refs/tags/test";
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("tag \"" + R_TAGS + "test\" already exists");
- tag(input.ref).create(input);
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> tag(input.ref).create(input));
+ assertThat(thrown).hasMessageThat().contains("tag \"" + R_TAGS + "test\" already exists");
}
@Test
public void createTagNotAllowed() throws Exception {
- block(R_TAGS + "*", Permission.CREATE, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.CREATE).ref(R_TAGS + "*").group(REGISTERED_USERS))
+ .update();
TagInput input = new TagInput();
input.ref = "test";
- exception.expect(AuthException.class);
- exception.expectMessage("not permitted: create");
- tag(input.ref).create(input);
+ AuthException thrown = assertThrows(AuthException.class, () -> tag(input.ref).create(input));
+ assertThat(thrown).hasMessageThat().contains("not permitted: create");
}
@Test
public void createAnnotatedTagNotAllowed() throws Exception {
- block(R_TAGS + "*", Permission.CREATE_TAG, REGISTERED_USERS);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.CREATE_TAG).ref(R_TAGS + "*").group(REGISTERED_USERS))
+ .update();
TagInput input = new TagInput();
input.ref = "test";
input.message = "annotation";
- exception.expect(AuthException.class);
- exception.expectMessage("Cannot create annotated tag \"" + R_TAGS + "test\"");
- tag(input.ref).create(input);
+ AuthException thrown = assertThrows(AuthException.class, () -> tag(input.ref).create(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Cannot create annotated tag \"" + R_TAGS + "test\"");
}
@Test
@@ -292,9 +319,9 @@ public class TagsIT extends AbstractDaemonTest {
TagInput input = new TagInput();
input.ref = "test";
input.message = SIGNED_ANNOTATION;
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("Cannot create signed tag \"" + R_TAGS + "test\"");
- tag(input.ref).create(input);
+ MethodNotAllowedException thrown =
+ assertThrows(MethodNotAllowedException.class, () -> tag(input.ref).create(input));
+ assertThat(thrown).hasMessageThat().contains("Cannot create signed tag \"" + R_TAGS + "test\"");
}
@Test
@@ -302,9 +329,9 @@ public class TagsIT extends AbstractDaemonTest {
TagInput input = new TagInput();
input.ref = "test";
- exception.expect(BadRequestException.class);
- exception.expectMessage("ref must match URL");
- tag("TEST").create(input);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> tag("TEST").create(input));
+ assertThat(thrown).hasMessageThat().contains("ref must match URL");
}
@Test
@@ -314,9 +341,9 @@ public class TagsIT extends AbstractDaemonTest {
TagInput input = new TagInput();
input.ref = "refs/heads/test";
- exception.expect(BadRequestException.class);
- exception.expectMessage("invalid tag name \"" + input.ref + "\"");
- tag(input.ref).create(input);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> tag(input.ref).create(input));
+ assertThat(thrown).hasMessageThat().contains("invalid tag name \"" + input.ref + "\"");
}
@Test
@@ -326,9 +353,9 @@ public class TagsIT extends AbstractDaemonTest {
TagInput input = new TagInput();
input.ref = "//";
- exception.expect(BadRequestException.class);
- exception.expectMessage("invalid tag name \"refs/tags/\"");
- tag(input.ref).create(input);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> tag(input.ref).create(input));
+ assertThat(thrown).hasMessageThat().contains("invalid tag name \"refs/tags/\"");
}
@Test
@@ -339,9 +366,9 @@ public class TagsIT extends AbstractDaemonTest {
input.ref = "test";
input.revision = "abcdefg";
- exception.expect(BadRequestException.class);
- exception.expectMessage("Invalid base revision");
- tag(input.ref).create(input);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> tag(input.ref).create(input));
+ assertThat(thrown).hasMessageThat().contains("Invalid base revision");
}
@Test
@@ -349,7 +376,7 @@ public class TagsIT extends AbstractDaemonTest {
grantTagPermissions();
// If revision is not specified, the tag is created based on HEAD, which points to master.
- RevCommit expectedRevision = getRemoteHead(project, "master");
+ RevCommit expectedRevision = projectOperations.project(project).getHead("master");
TagInput input = new TagInput();
input.ref = "test";
@@ -365,7 +392,7 @@ public class TagsIT extends AbstractDaemonTest {
grantTagPermissions();
// If revision is not specified, the tag is created based on HEAD, which points to master.
- RevCommit expectedRevision = getRemoteHead(project, "master");
+ RevCommit expectedRevision = projectOperations.project(project).getHead("master");
TagInput input = new TagInput();
input.ref = "test";
@@ -380,7 +407,7 @@ public class TagsIT extends AbstractDaemonTest {
public void baseRevisionIsTrimmed() throws Exception {
grantTagPermissions();
- RevCommit revision = getRemoteHead(project, "master");
+ RevCommit revision = projectOperations.project(project).getHead("master");
TagInput input = new TagInput();
input.ref = "test";
@@ -428,19 +455,18 @@ public class TagsIT extends AbstractDaemonTest {
}
private void assertBadRequest(ListRefsRequest<TagInfo> req) throws Exception {
- try {
- req.get();
- fail("Expected BadRequestException");
- } catch (BadRequestException e) {
- // Expected
- }
+ assertThrows(BadRequestException.class, () -> req.get());
}
private void grantTagPermissions() throws Exception {
- grant(project, R_TAGS + "*", Permission.CREATE);
- grant(project, R_TAGS + "", Permission.DELETE);
- grant(project, R_TAGS + "*", Permission.CREATE_TAG);
- grant(project, R_TAGS + "*", Permission.CREATE_SIGNED_TAG);
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.CREATE).ref(R_TAGS + "*").group(adminGroupUuid()))
+ .add(allow(Permission.DELETE).ref(R_TAGS + "").group(adminGroupUuid()))
+ .add(allow(Permission.CREATE_TAG).ref(R_TAGS + "*").group(adminGroupUuid()))
+ .add(allow(Permission.CREATE_SIGNED_TAG).ref(R_TAGS + "*").group(adminGroupUuid()))
+ .update();
}
private static void removeAllBranchPermissions(ProjectConfig cfg, String... permissions) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java b/javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java
index 52e72febf4..f98fb452b6 100644
--- a/javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java
+++ b/javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java
@@ -15,7 +15,6 @@
package com.google.gerrit.acceptance.rest.util;
import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.common.truth.Truth.assert_;
import static org.apache.http.HttpStatus.SC_FORBIDDEN;
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
@@ -76,7 +75,8 @@ public class RestApiCallHelper {
response = restSession.delete(uri);
break;
default:
- assert_().fail(String.format("unsupported method: %s", restCall.httpMethod().name()));
+ assertWithMessage(String.format("unsupported method: %s", restCall.httpMethod().name()))
+ .fail();
throw new IllegalStateException();
}
diff --git a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
index fa1b4678c0..e924143050 100644
--- a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
@@ -15,8 +15,9 @@
package com.google.gerrit.acceptance.server.account;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
@@ -25,8 +26,8 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.AccountVisibility;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountResolver;
@@ -81,26 +82,16 @@ public class AccountResolverIT extends AbstractDaemonTest {
}
private void checkBySelfFails() throws Exception {
- Result result = resolveAsResult("self");
- assertThat(result.asIdSet()).isEmpty();
- assertThat(result.isSelf()).isTrue();
- try {
- result.asUnique();
- assert_().fail("expected UnresolvableAccountException");
- } catch (UnresolvableAccountException e) {
- assertThat(e).hasMessageThat().isEqualTo("Resolving account 'self' requires login");
- assertThat(e.isSelf()).isTrue();
- }
-
- result = resolveAsResult("me");
- assertThat(result.asIdSet()).isEmpty();
- assertThat(result.isSelf()).isTrue();
- try {
- result.asUnique();
- assert_().fail("expected UnresolvableAccountException");
- } catch (UnresolvableAccountException e) {
- assertThat(e).hasMessageThat().isEqualTo("Resolving account 'me' requires login");
- assertThat(e.isSelf()).isTrue();
+ for (String input : ImmutableList.of("self", "me")) {
+ Result result = resolveAsResult(input);
+ assertThat(result.asIdSet()).isEmpty();
+ assertThat(result.isSelf()).isTrue();
+ UnresolvableAccountException thrown =
+ assertThrows(UnresolvableAccountException.class, () -> result.asUnique());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(String.format("Resolving account '%s' requires login", input));
+ assertThat(thrown.isSelf()).isTrue();
}
}
@@ -123,7 +114,7 @@ public class AccountResolverIT extends AbstractDaemonTest {
Account.Id idWithExistingIdAsFullname =
accountOperations.newAccount().fullname(existingId.toString()).create();
- Account.Id nonexistentId = new Account.Id(sequences.nextAccountId());
+ Account.Id nonexistentId = Account.id(sequences.nextAccountId());
accountOperations.newAccount().fullname(nonexistentId.toString()).create();
assertThat(resolve(existingId)).containsExactly(existingId);
@@ -137,7 +128,7 @@ public class AccountResolverIT extends AbstractDaemonTest {
Account.Id existingId = accountOperations.newAccount().fullname("Test User").create();
accountOperations.newAccount().fullname(existingId.toString()).create();
- Account.Id nonexistentId = new Account.Id(sequences.nextAccountId());
+ Account.Id nonexistentId = Account.id(sequences.nextAccountId());
accountOperations.newAccount().fullname("Any Name (" + nonexistentId + ")").create();
accountOperations.newAccount().fullname(nonexistentId.toString()).create();
@@ -259,31 +250,28 @@ public class AccountResolverIT extends AbstractDaemonTest {
assertThat(resolve(account.accountId())).containsExactly(id);
for (String input : inputs) {
- assertThat(resolve(input)).named("results for %s (active)", input).containsExactly(id);
+ assertWithMessage("results for %s (active)", input).that(resolve(input)).containsExactly(id);
}
gApi.accounts().id(id.get()).setActive(false);
assertThat(resolve(account.accountId())).containsExactly(id);
for (String input : inputs) {
Result result = accountResolver.resolve(input);
- assertThat(result.asIdSet()).named("results for %s (inactive)", input).isEmpty();
- try {
- result.asUnique();
- assert_().fail("expected UnresolvableAccountException");
- } catch (UnresolvableAccountException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo(
- "Account '"
- + input
- + "' only matches inactive accounts. To use an inactive account, retry"
- + " with one of the following exact account IDs:\n"
- + id
- + ": "
- + nameEmail);
- }
- assertThat(resolveByNameOrEmail(input))
- .named("results by name or email for %s (inactive)", input)
+ assertWithMessage("results for %s (inactive)", input).that(result.asIdSet()).isEmpty();
+ UnresolvableAccountException thrown =
+ assertThrows(UnresolvableAccountException.class, () -> result.asUnique());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ "Account '"
+ + input
+ + "' only matches inactive accounts. To use an inactive account, retry"
+ + " with one of the following exact account IDs:\n"
+ + id
+ + ": "
+ + nameEmail);
+ assertWithMessage("results by name or email for %s (inactive)", input)
+ .that(resolveByNameOrEmail(input))
.isEmpty();
}
}
@@ -352,6 +340,6 @@ public class AccountResolverIT extends AbstractDaemonTest {
accountsUpdateProvider
.get()
.update("Force set preferred email", id, (s, u) -> u.setPreferredEmail(email));
- assertThat(result.map(a -> a.getAccount().getPreferredEmail())).hasValue(email);
+ assertThat(result.map(a -> a.account().preferredEmail())).hasValue(email);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
index 35fe48f7d3..7ac803eae9 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -18,10 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
-import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
@@ -31,6 +31,9 @@ import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.DeleteCommentInput;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -45,9 +48,6 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
@@ -61,11 +61,11 @@ import com.google.inject.Provider;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -98,8 +98,9 @@ public class CommentsIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
String revId = r.getCommit().getName();
- exception.expect(ResourceNotFoundException.class);
- getPublishedComment(changeId, revId, "non-existing");
+ assertThrows(
+ ResourceNotFoundException.class,
+ () -> getPublishedComment(changeId, revId, "non-existing"));
}
@Test
@@ -140,12 +141,11 @@ public class CommentsIT extends AbstractDaemonTest {
addDraft(changeId, revId, c4);
Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
assertThat(result).hasSize(1);
- assertThat(Lists.transform(result.get(path), infoToDraft(path)))
- .containsExactly(c1, c2, c3, c4);
+ assertThat(result.get(path).stream().map(infoToDraft(path))).containsExactly(c1, c2, c3, c4);
List<CommentInfo> list = getDraftCommentsAsList(changeId);
assertThat(list).hasSize(4);
- assertThat(Lists.transform(list, infoToDraft(path))).containsExactly(c1, c2, c3, c4);
+ assertThat(list.stream().map(infoToDraft(path))).containsExactly(c1, c2, c3, c4);
}
}
@@ -246,11 +246,10 @@ public class CommentsIT extends AbstractDaemonTest {
revision(r).review(input);
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
assertThat(result).isNotEmpty();
- assertThat(Lists.transform(result.get(file), infoToInput(file)))
- .containsExactly(c1, c2, c3, c4);
+ assertThat(result.get(file).stream().map(infoToInput(file))).containsExactly(c1, c2, c3, c4);
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
- assertThat(Lists.transform(list, infoToInput(file))).containsExactly(c1, c2, c3, c4);
+ assertThat(list.stream().map(infoToInput(file))).containsExactly(c1, c2, c3, c4);
}
// for the commit message comments on the auto-merge are not possible
@@ -268,10 +267,10 @@ public class CommentsIT extends AbstractDaemonTest {
revision(r).review(input);
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
assertThat(result).isNotEmpty();
- assertThat(Lists.transform(result.get(file), infoToInput(file))).containsExactly(c1, c2, c3);
+ assertThat(result.get(file).stream().map(infoToInput(file))).containsExactly(c1, c2, c3);
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
- assertThat(Lists.transform(list, infoToInput(file))).containsExactly(c1, c2, c3);
+ assertThat(list.stream().map(infoToInput(file))).containsExactly(c1, c2, c3);
}
}
@@ -282,9 +281,40 @@ public class CommentsIT extends AbstractDaemonTest {
CommentInput c = newComment(Patch.COMMIT_MSG, Side.PARENT, 0, "comment on auto-merge", false);
input.comments = new HashMap<>();
input.comments.put(Patch.COMMIT_MSG, ImmutableList.of(c));
- exception.expect(BadRequestException.class);
- exception.expectMessage("cannot comment on " + Patch.COMMIT_MSG + " on auto-merge");
- revision(r).review(input);
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> revision(r).review(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("cannot comment on " + Patch.COMMIT_MSG + " on auto-merge");
+ }
+
+ @Test
+ public void postCommentsReplacingDrafts() throws Exception {
+ String file = "file";
+ PushOneCommit push =
+ pushFactory.create(admin.newIdent(), testRepo, "first subject", file, "contents");
+ PushOneCommit.Result r = push.to("refs/for/master");
+ String changeId = r.getChangeId();
+ String revId = r.getCommit().getName();
+
+ DraftInput draft = newDraft(file, Side.REVISION, 0, "comment");
+ addDraft(changeId, revId, draft);
+ Map<String, List<CommentInfo>> drafts = getDraftComments(changeId, revId);
+ CommentInfo draftInfo = Iterables.getOnlyElement(drafts.get(draft.path));
+
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.drafts = DraftHandling.KEEP;
+ reviewInput.message = "foo";
+ CommentInput comment = newComment(file, Side.REVISION, 0, "comment", false);
+ // Replace the existing draft.
+ comment.id = draftInfo.id;
+ reviewInput.comments = new HashMap<>();
+ reviewInput.comments.put(comment.path, ImmutableList.of(comment));
+ revision(r).review(reviewInput);
+
+ // DraftHandling.KEEP is ignored on publishing a comment.
+ drafts = getDraftComments(changeId, revId);
+ assertThat(drafts).isEmpty();
}
@Test
@@ -350,12 +380,11 @@ public class CommentsIT extends AbstractDaemonTest {
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
assertThat(result).isNotEmpty();
List<CommentInfo> actualComments = result.get(file);
- assertThat(Lists.transform(actualComments, infoToInput(file)))
+ assertThat(actualComments.stream().map(infoToInput(file)))
.containsExactlyElementsIn(expectedComments);
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
- assertThat(Lists.transform(list, infoToInput(file)))
- .containsExactlyElementsIn(expectedComments);
+ assertThat(list.stream().map(infoToInput(file))).containsExactlyElementsIn(expectedComments);
}
/**
@@ -434,7 +463,7 @@ public class CommentsIT extends AbstractDaemonTest {
Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
assertThat(result).isNotEmpty();
List<CommentInfo> actualComments = result.get(file);
- assertThat(Lists.transform(actualComments, infoToDraft(file)))
+ assertThat(actualComments.stream().map(infoToDraft(file)))
.containsExactlyElementsIn(expectedDrafts);
}
@@ -888,8 +917,9 @@ public class CommentsIT extends AbstractDaemonTest {
DeleteCommentInput input = new DeleteCommentInput("contains confidential information");
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.changes().id(result.getChangeId()).current().comment(uuid).delete(input);
+ assertThrows(
+ AuthException.class,
+ () -> gApi.changes().id(result.getChangeId()).current().comment(uuid).delete(input));
}
@Test
@@ -1047,20 +1077,6 @@ public class CommentsIT extends AbstractDaemonTest {
assertThat(getChangeSortedComments(id.get())).hasSize(3);
}
- @Test
- public void jsonCommentHasLegacyFormatFalse() throws Exception {
- PushOneCommit.Result result = createChange();
- Change.Id changeId = result.getChange().getId();
- addComment(result.getChangeId(), "comment");
-
- Collection<com.google.gerrit.reviewdb.client.Comment> comments =
- notesFactory.createChecked(project, changeId).getComments().values();
- assertThat(comments).hasSize(1);
- com.google.gerrit.reviewdb.client.Comment comment = comments.iterator().next();
- assertThat(comment.message).isEqualTo("comment");
- assertThat(comment.legacyFormat).isFalse();
- }
-
private List<CommentInfo> getRevisionComments(String changeId, String revId) throws Exception {
return getPublishedComments(changeId, revId).values().stream()
.flatMap(List::stream)
@@ -1101,17 +1117,16 @@ public class CommentsIT extends AbstractDaemonTest {
RevCommit commitBefore = beforeDelete.get(i);
RevCommit commitAfter = afterDelete.get(i);
- Map<String, com.google.gerrit.reviewdb.client.Comment> commentMapBefore =
+ Map<String, com.google.gerrit.entities.Comment> commentMapBefore =
DeleteCommentRewriter.getPublishedComments(
- noteUtil, changeId, reader, NoteMap.read(reader, commitBefore));
- Map<String, com.google.gerrit.reviewdb.client.Comment> commentMapAfter =
+ noteUtil, reader, NoteMap.read(reader, commitBefore));
+ Map<String, com.google.gerrit.entities.Comment> commentMapAfter =
DeleteCommentRewriter.getPublishedComments(
- noteUtil, changeId, reader, NoteMap.read(reader, commitAfter));
+ noteUtil, reader, NoteMap.read(reader, commitAfter));
if (commentMapBefore.containsKey(targetCommentUuid)) {
assertThat(commentMapAfter).containsKey(targetCommentUuid);
- com.google.gerrit.reviewdb.client.Comment comment =
- commentMapAfter.get(targetCommentUuid);
+ com.google.gerrit.entities.Comment comment = commentMapAfter.get(targetCommentUuid);
assertThat(comment.message).isEqualTo(expectedMessage);
comment.message = commentMapBefore.get(targetCommentUuid).message;
commentMapAfter.put(targetCommentUuid, comment);
diff --git a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index 7c375bd0d5..1e2d1ba7b8 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -25,14 +25,14 @@ import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.FixInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ProblemInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.ChangeInserter;
@@ -131,7 +131,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
assertProblems(
notes,
null,
- problem("Ref missing: " + ps.getId().toRefName()),
+ problem("Ref missing: " + ps.id().toRefName()),
problem("Object missing: patch set 2: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
}
@@ -142,7 +142,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
PatchSet ps = insertMissingPatchSet(notes, rev);
notes = reload(notes);
- String refName = ps.getId().toRefName();
+ String refName = ps.id().toRefName();
assertProblems(
notes,
new FixInput(),
@@ -153,8 +153,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void patchSetRefMissing() throws Exception {
ChangeNotes notes = insertChange();
- serverSideTestRepo.update(
- "refs/other/foo", ObjectId.fromString(psUtil.current(notes).getRevision().get()));
+ serverSideTestRepo.update("refs/other/foo", psUtil.current(notes).commitId());
String refName = notes.getChange().currentPatchSetId().toRefName();
deleteRef(refName);
@@ -164,15 +163,15 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void patchSetRefMissingWithFix() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(notes).getRevision().get();
- serverSideTestRepo.update("refs/other/foo", ObjectId.fromString(rev));
+ ObjectId commitId = psUtil.current(notes).commitId();
+ serverSideTestRepo.update("refs/other/foo", commitId);
String refName = notes.getChange().currentPatchSetId().toRefName();
deleteRef(refName);
assertProblems(
notes, new FixInput(), problem("Ref missing: " + refName, FIXED, "Repaired patch set ref"));
- assertThat(serverSideTestRepo.getRepository().exactRef(refName).getObjectId().name())
- .isEqualTo(rev);
+ assertThat(serverSideTestRepo.getRepository().exactRef(refName).getObjectId())
+ .isEqualTo(commitId);
}
@Test
@@ -189,13 +188,13 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
assertProblems(
notes,
fix,
- problem("Ref missing: " + ps2.getId().toRefName()),
+ problem("Ref missing: " + ps2.id().toRefName()),
problem("Object missing: patch set 2: " + rev2, FIXED, "Deleted patch set"));
notes = reload(notes);
assertThat(notes.getChange().currentPatchSetId().get()).isEqualTo(1);
- assertThat(psUtil.get(notes, ps1.getId())).isNotNull();
- assertThat(psUtil.get(notes, ps2.getId())).isNull();
+ assertThat(psUtil.get(notes, ps1.id())).isNotNull();
+ assertThat(psUtil.get(notes, ps2.id())).isNull();
}
@Test
@@ -218,17 +217,17 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
assertProblems(
notes,
fix,
- problem("Ref missing: " + ps2.getId().toRefName()),
+ problem("Ref missing: " + ps2.id().toRefName()),
problem("Object missing: patch set 2: " + rev2, FIXED, "Deleted patch set"),
- problem("Ref missing: " + ps4.getId().toRefName()),
+ problem("Ref missing: " + ps4.id().toRefName()),
problem("Object missing: patch set 4: " + rev4, FIXED, "Deleted patch set"));
notes = reload(notes);
assertThat(notes.getChange().currentPatchSetId().get()).isEqualTo(3);
- assertThat(psUtil.get(notes, ps1.getId())).isNotNull();
- assertThat(psUtil.get(notes, ps2.getId())).isNull();
- assertThat(psUtil.get(notes, ps3.getId())).isNotNull();
- assertThat(psUtil.get(notes, ps4.getId())).isNull();
+ assertThat(psUtil.get(notes, ps1.id())).isNotNull();
+ assertThat(psUtil.get(notes, ps2.id())).isNull();
+ assertThat(psUtil.get(notes, ps3.id())).isNotNull();
+ assertThat(psUtil.get(notes, ps4.id())).isNull();
}
@Test
@@ -245,7 +244,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
+ "\n"
+ "Patch-set: 1\n"
+ "Branch: "
- + c.getDest().get()
+ + c.getDest().branch()
+ "\n"
+ "Change-id: "
+ c.getKey().get()
@@ -265,7 +264,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
assertProblems(
notes,
fix,
- problem("Ref missing: " + ps.getId().toRefName()),
+ problem("Ref missing: " + ps.id().toRefName()),
problem(
"Object missing: patch set 1: " + rev,
FIX_FAILED,
@@ -280,13 +279,13 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
public void duplicatePatchSetRevisions() throws Exception {
ChangeNotes notes = insertChange();
PatchSet ps1 = psUtil.current(notes);
- String rev = ps1.getRevision().get();
- notes =
- incrementPatchSet(
- notes, serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
+ notes = incrementPatchSet(notes, serverSideTestRepo.getRevWalk().parseCommit(ps1.commitId()));
- assertProblems(notes, null, problem("Multiple patch sets pointing to " + rev + ": [1, 2]"));
+ assertProblems(
+ notes,
+ null,
+ problem("Multiple patch sets pointing to " + ps1.commitId().name() + ": [1, 2]"));
}
@Test
@@ -322,14 +321,13 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
}
notes = reload(notes);
- String rev = psUtil.current(notes).getRevision().get();
ObjectId tip = getDestRef(notes);
assertProblems(
notes,
null,
problem(
"Patch set 1 ("
- + rev
+ + psUtil.current(notes).commitId().name()
+ ") is not merged into destination ref"
+ " refs/heads/master ("
+ tip.name()
@@ -339,40 +337,40 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void newChangeIsMerged() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(notes).getRevision().get();
+ ObjectId commitId = psUtil.current(notes).commitId();
serverSideTestRepo
- .branch(notes.getChange().getDest().get())
- .update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
+ .branch(notes.getChange().getDest().branch())
+ .update(serverSideTestRepo.getRevWalk().parseCommit(commitId));
assertProblems(
notes,
null,
problem(
"Patch set 1 ("
- + rev
+ + commitId.name()
+ ") is merged into destination ref"
+ " refs/heads/master ("
- + rev
+ + commitId.name()
+ "), but change status is NEW"));
}
@Test
public void newChangeIsMergedWithFix() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(notes).getRevision().get();
+ ObjectId commitId = psUtil.current(notes).commitId();
serverSideTestRepo
- .branch(notes.getChange().getDest().get())
- .update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
+ .branch(notes.getChange().getDest().branch())
+ .update(serverSideTestRepo.getRevWalk().parseCommit(commitId));
assertProblems(
notes,
new FixInput(),
problem(
"Patch set 1 ("
- + rev
+ + commitId.name()
+ ") is merged into destination ref"
+ " refs/heads/master ("
- + rev
+ + commitId.name()
+ "), but change status is NEW",
FIXED,
"Marked change as merged"));
@@ -385,10 +383,10 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void extensionApiReturnsUpdatedValueAfterFix() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(notes).getRevision().get();
+ ObjectId commitId = psUtil.current(notes).commitId();
serverSideTestRepo
- .branch(notes.getChange().getDest().get())
- .update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
+ .branch(notes.getChange().getDest().branch())
+ .update(serverSideTestRepo.getRevWalk().parseCommit(commitId));
ChangeInfo info = gApi.changes().id(notes.getChangeId().get()).info();
assertThat(info.status).isEqualTo(ChangeStatus.NEW);
@@ -400,22 +398,22 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void expectedMergedCommitIsLatestPatchSet() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(notes).getRevision().get();
+ ObjectId commitId = psUtil.current(notes).commitId();
serverSideTestRepo
- .branch(notes.getChange().getDest().get())
- .update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
+ .branch(notes.getChange().getDest().branch())
+ .update(serverSideTestRepo.getRevWalk().parseCommit(commitId));
FixInput fix = new FixInput();
- fix.expectMergedAs = rev;
+ fix.expectMergedAs = commitId.name();
assertProblems(
notes,
fix,
problem(
"Patch set 1 ("
- + rev
+ + commitId.name()
+ ") is merged into destination ref"
+ " refs/heads/master ("
- + rev
+ + commitId.name()
+ "), but change status is NEW",
FIXED,
"Marked change as merged"));
@@ -428,9 +426,9 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void expectedMergedCommitNotMergedIntoDestination() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(notes).getRevision().get();
- RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
- serverSideTestRepo.branch(notes.getChange().getDest().get()).update(commit);
+ RevCommit commit =
+ serverSideTestRepo.getRevWalk().parseCommit(psUtil.current(notes).commitId());
+ serverSideTestRepo.branch(notes.getChange().getDest().branch()).update(commit);
FixInput fix = new FixInput();
RevCommit other = serverSideTestRepo.commit().message(commit.getFullMessage()).create();
@@ -450,9 +448,9 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void createNewPatchSetForExpectedMergeCommitWithNoChangeId() throws Exception {
ChangeNotes notes = insertChange();
- String dest = notes.getChange().getDest().get();
- String rev = psUtil.current(notes).getRevision().get();
- RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
+ String dest = notes.getChange().getDest().branch();
+ RevCommit commit =
+ serverSideTestRepo.getRevWalk().parseCommit(psUtil.current(notes).commitId());
RevCommit mergedAs =
serverSideTestRepo
@@ -481,9 +479,9 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
"Inserted as patch set 2"));
notes = reload(notes);
- PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
+ PatchSet.Id psId2 = PatchSet.id(notes.getChangeId(), 2);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
- assertThat(psUtil.get(notes, psId2).getRevision().get()).isEqualTo(mergedAs.name());
+ assertThat(psUtil.get(notes, psId2).commitId()).isEqualTo(mergedAs);
assertNoProblems(notes, null);
}
@@ -491,9 +489,9 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void createNewPatchSetForExpectedMergeCommitWithChangeId() throws Exception {
ChangeNotes notes = insertChange();
- String dest = notes.getChange().getDest().get();
- String rev = psUtil.current(notes).getRevision().get();
- RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
+ String dest = notes.getChange().getDest().branch();
+ RevCommit commit =
+ serverSideTestRepo.getRevWalk().parseCommit(psUtil.current(notes).commitId());
RevCommit mergedAs =
serverSideTestRepo
@@ -529,9 +527,9 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
"Inserted as patch set 2"));
notes = reload(notes);
- PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
+ PatchSet.Id psId2 = PatchSet.id(notes.getChangeId(), 2);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
- assertThat(psUtil.get(notes, psId2).getRevision().get()).isEqualTo(mergedAs.name());
+ assertThat(psUtil.get(notes, psId2).commitId()).isEqualTo(mergedAs);
assertNoProblems(notes, null);
}
@@ -539,41 +537,43 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void expectedMergedCommitIsOldPatchSetOfSameChange() throws Exception {
ChangeNotes notes = insertChange();
- PatchSet ps1 = psUtil.current(notes);
- String rev1 = ps1.getRevision().get();
+ ObjectId commitId1 = psUtil.current(notes).commitId();
notes = incrementPatchSet(notes);
PatchSet ps2 = psUtil.current(notes);
serverSideTestRepo
- .branch(notes.getChange().getDest().get())
- .update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev1)));
+ .branch(notes.getChange().getDest().branch())
+ .update(serverSideTestRepo.getRevWalk().parseCommit(commitId1));
FixInput fix = new FixInput();
- fix.expectMergedAs = rev1;
+ fix.expectMergedAs = commitId1.name();
assertProblems(
notes,
fix,
- problem("No patch set found for merged commit " + rev1, FIXED, "Marked change as merged"),
+ problem(
+ "No patch set found for merged commit " + commitId1.name(),
+ FIXED,
+ "Marked change as merged"),
problem(
"Expected merge commit "
- + rev1
+ + commitId1.name()
+ " corresponds to patch set 1,"
+ " not the current patch set 2",
FIXED,
"Deleted patch set"),
problem(
"Expected merge commit "
- + rev1
+ + commitId1.name()
+ " corresponds to patch set 1,"
+ " not the current patch set 2",
FIXED,
"Inserted as patch set 3"));
notes = reload(notes);
- PatchSet.Id psId3 = new PatchSet.Id(notes.getChangeId(), 3);
+ PatchSet.Id psId3 = PatchSet.id(notes.getChangeId(), 3);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId3);
assertThat(notes.getChange().isMerged()).isTrue();
- assertThat(psUtil.byChangeAsMap(notes).keySet()).containsExactly(ps2.getId(), psId3);
- assertThat(psUtil.get(notes, psId3).getRevision().get()).isEqualTo(rev1);
+ assertThat(psUtil.byChangeAsMap(notes).keySet()).containsExactly(ps2.id(), psId3);
+ assertThat(psUtil.get(notes, psId3).commitId()).isEqualTo(commitId1);
}
@Test
@@ -582,47 +582,46 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
PatchSet ps1 = psUtil.current(notes);
// Create dangling ref so next ID in the database becomes 3.
- PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
+ PatchSet.Id psId2 = PatchSet.id(notes.getChangeId(), 2);
RevCommit commit2 = patchSetCommit(psId2);
- String rev2 = commit2.name();
serverSideTestRepo.branch(psId2.toRefName()).update(commit2);
notes = incrementPatchSet(notes);
PatchSet ps3 = psUtil.current(notes);
- assertThat(ps3.getId().get()).isEqualTo(3);
+ assertThat(ps3.id().get()).isEqualTo(3);
- serverSideTestRepo
- .branch(notes.getChange().getDest().get())
- .update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev2)));
+ serverSideTestRepo.branch(notes.getChange().getDest().branch()).update(commit2);
FixInput fix = new FixInput();
- fix.expectMergedAs = rev2;
+ fix.expectMergedAs = commit2.name();
assertProblems(
notes,
fix,
- problem("No patch set found for merged commit " + rev2, FIXED, "Marked change as merged"),
+ problem(
+ "No patch set found for merged commit " + commit2.name(),
+ FIXED,
+ "Marked change as merged"),
problem(
"Expected merge commit "
- + rev2
+ + commit2.name()
+ " corresponds to patch set 2,"
+ " not the current patch set 3",
FIXED,
"Deleted patch set"),
problem(
"Expected merge commit "
- + rev2
+ + commit2.name()
+ " corresponds to patch set 2,"
+ " not the current patch set 3",
FIXED,
"Inserted as patch set 4"));
notes = reload(notes);
- PatchSet.Id psId4 = new PatchSet.Id(notes.getChangeId(), 4);
+ PatchSet.Id psId4 = PatchSet.id(notes.getChangeId(), 4);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId4);
assertThat(notes.getChange().isMerged()).isTrue();
- assertThat(psUtil.byChangeAsMap(notes).keySet())
- .containsExactly(ps1.getId(), ps3.getId(), psId4);
- assertThat(psUtil.get(notes, psId4).getRevision().get()).isEqualTo(rev2);
+ assertThat(psUtil.byChangeAsMap(notes).keySet()).containsExactly(ps1.id(), ps3.id(), psId4);
+ assertThat(psUtil.get(notes, psId4).commitId()).isEqualTo(commit2);
}
@Test
@@ -631,24 +630,24 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
PatchSet ps1 = psUtil.current(notes);
// Create dangling ref with no patch set.
- PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
+ PatchSet.Id psId2 = PatchSet.id(notes.getChangeId(), 2);
RevCommit commit2 = patchSetCommit(psId2);
- String rev2 = commit2.name();
serverSideTestRepo.branch(psId2.toRefName()).update(commit2);
- serverSideTestRepo
- .branch(notes.getChange().getDest().get())
- .update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev2)));
+ serverSideTestRepo.branch(notes.getChange().getDest().branch()).update(commit2);
FixInput fix = new FixInput();
- fix.expectMergedAs = rev2;
+ fix.expectMergedAs = commit2.name();
assertProblems(
notes,
fix,
- problem("No patch set found for merged commit " + rev2, FIXED, "Marked change as merged"),
+ problem(
+ "No patch set found for merged commit " + commit2.name(),
+ FIXED,
+ "Marked change as merged"),
problem(
"Expected merge commit "
- + rev2
+ + commit2.name()
+ " corresponds to patch set 2,"
+ " not the current patch set 1",
FIXED,
@@ -657,17 +656,17 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
assertThat(notes.getChange().isMerged()).isTrue();
- assertThat(psUtil.byChangeAsMap(notes).keySet()).containsExactly(ps1.getId(), psId2);
- assertThat(psUtil.get(notes, psId2).getRevision().get()).isEqualTo(rev2);
+ assertThat(psUtil.byChangeAsMap(notes).keySet()).containsExactly(ps1.id(), psId2);
+ assertThat(psUtil.get(notes, psId2).commitId()).isEqualTo(commit2);
}
@Test
public void expectedMergedCommitWithMismatchedChangeId() throws Exception {
ChangeNotes notes = insertChange();
- String dest = notes.getChange().getDest().get();
+ String dest = notes.getChange().getDest().branch();
RevCommit parent = serverSideTestRepo.branch(dest).commit().message("parent").create();
- String rev = psUtil.current(notes).getRevision().get();
- RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
+ RevCommit commit =
+ serverSideTestRepo.getRevWalk().parseCommit(psUtil.current(notes).commitId());
serverSideTestRepo.branch(dest).update(commit);
String badId = "I0000000000000000000000000000000000000000";
@@ -700,19 +699,19 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void expectedMergedCommitMatchesMultiplePatchSets() throws Exception {
ChangeNotes notes1 = insertChange();
- PatchSet.Id psId1 = psUtil.current(notes1).getId();
- String dest = notes1.getChange().getDest().get();
- String rev = psUtil.current(notes1).getRevision().get();
- RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
+ PatchSet.Id psId1 = psUtil.current(notes1).id();
+ String dest = notes1.getChange().getDest().branch();
+ RevCommit commit =
+ serverSideTestRepo.getRevWalk().parseCommit(psUtil.current(notes1).commitId());
serverSideTestRepo.branch(dest).update(commit);
ChangeNotes notes2 = insertChange();
notes2 = incrementPatchSet(notes2, commit);
- PatchSet.Id psId2 = psUtil.current(notes2).getId();
+ PatchSet.Id psId2 = psUtil.current(notes2).id();
ChangeNotes notes3 = insertChange();
notes3 = incrementPatchSet(notes3, commit);
- PatchSet.Id psId3 = psUtil.current(notes3).getId();
+ PatchSet.Id psId3 = psUtil.current(notes3).id();
FixInput fix = new FixInput();
fix.expectMergedAs = commit.name();
@@ -744,10 +743,10 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
}
private ChangeNotes insertChange(TestAccount owner, String dest) throws Exception {
- Change.Id id = new Change.Id(sequences.nextChangeId());
+ Change.Id id = Change.id(sequences.nextChangeId());
ChangeInserter ins;
try (BatchUpdate bu = newUpdate(owner.id())) {
- RevCommit commit = patchSetCommit(new PatchSet.Id(id, 1));
+ RevCommit commit = patchSetCommit(PatchSet.id(id, 1));
bu.setNotify(NotifyResolver.Result.none());
ins =
changeInserterFactory
@@ -842,14 +841,14 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
private ObjectId getDestRef(ChangeNotes notes) throws Exception {
return serverSideTestRepo
.getRepository()
- .exactRef(notes.getChange().getDest().get())
+ .exactRef(notes.getChange().getDest().branch())
.getObjectId();
}
private ChangeNotes mergeChange(ChangeNotes notes) throws Exception {
- final ObjectId oldId = getDestRef(notes);
- final ObjectId newId = ObjectId.fromString(psUtil.current(notes).getRevision().get());
- final String dest = notes.getChange().getDest().get();
+ ObjectId oldId = getDestRef(notes);
+ ObjectId newId = psUtil.current(notes).commitId();
+ String dest = notes.getChange().getDest().branch();
try (BatchUpdate bu = newUpdate(adminId)) {
bu.addOp(
diff --git a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
index b9b7ab39c8..e369d1b4c3 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -15,35 +15,36 @@
package com.google.gerrit.acceptance.server.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
-import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.truth.Correspondence;
-import com.google.common.truth.Correspondence.BinaryPredicate;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.UseTimezone;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.RawInputUtil;
import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.api.changes.RelatedChangeAndCommitInfo;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.index.IndexConfig;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.restapi.change.ChangesCollection;
import com.google.gerrit.server.restapi.change.GetRelated;
@@ -52,7 +53,6 @@ import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
@@ -64,11 +64,11 @@ import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
@NoHttpd
+@UseClockStep
+@UseTimezone(timezone = "US/Eastern")
public class GetRelatedIT extends AbstractDaemonTest {
private static final int MAX_TERMS = 10;
@@ -81,22 +81,9 @@ public class GetRelatedIT extends AbstractDaemonTest {
@Inject private AccountOperations accountOperations;
@Inject private GroupOperations groupOperations;
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
- private String systemTimeZone;
-
- @Before
- public void setTimeForTesting() {
- systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @After
- public void resetTime() {
- TestTimeUtil.useSystemTime();
- System.setProperty("user.timezone", systemTimeZone);
- }
-
@Inject private IndexConfig indexConfig;
@Inject private ChangesCollection changes;
@@ -129,14 +116,14 @@ public class GetRelatedIT extends AbstractDaemonTest {
testRepo.reset(c1_1);
pushHead(testRepo, "refs/for/master", false);
PatchSet.Id ps1_1 = getPatchSetId(c1_1);
- String oldETag = changes.parse(ps1_1.getParentKey()).getETag();
+ String oldETag = changes.parse(ps1_1.changeId()).getETag();
testRepo.reset(c2_1);
pushHead(testRepo, "refs/for/master", false);
PatchSet.Id ps2_1 = getPatchSetId(c2_1);
// Push of change 2 should not affect groups (or anything else) of change 1.
- assertThat(changes.parse(ps1_1.getParentKey()).getETag()).isEqualTo(oldETag);
+ assertThat(changes.parse(ps1_1.changeId()).getETag()).isEqualTo(oldETag);
for (PatchSet.Id ps : ImmutableList.of(ps2_1, ps1_1)) {
assertRelated(ps, changeAndCommit(ps2_1, c2_1, 1), changeAndCommit(ps1_1, c1_1, 1));
@@ -498,7 +485,7 @@ public class GetRelatedIT extends AbstractDaemonTest {
PatchSet.Id ps1_1 = getPatchSetId(c1_1);
PatchSet.Id ps2_1 = getPatchSetId(c2_1);
- PatchSet.Id ps2_edit = new PatchSet.Id(ch2.getId(), 0);
+ PatchSet.Id ps2_edit = PatchSet.id(ch2.getId(), 0);
PatchSet.Id ps3_1 = getPatchSetId(c3_1);
for (PatchSet.Id ps : ImmutableList.of(ps1_1, ps2_1, ps3_1)) {
@@ -512,7 +499,7 @@ public class GetRelatedIT extends AbstractDaemonTest {
assertRelated(
ps2_edit,
changeAndCommit(ps3_1, c3_1, 1),
- changeAndCommit(new PatchSet.Id(ch2.getId(), 0), editRev, 1),
+ changeAndCommit(PatchSet.id(ch2.getId(), 0), editRev, 1),
changeAndCommit(ps1_1, c1_1, 1));
}
@@ -533,7 +520,7 @@ public class GetRelatedIT extends AbstractDaemonTest {
// Pretend PS1,1 was pushed before the groups field was added.
clearGroups(psId1_1);
- indexer.index(changeDataFactory.create(project, psId1_1.getParentKey()));
+ indexer.index(changeDataFactory.create(project, psId1_1.changeId()));
// PS1,1 has no groups, so disappeared from related changes.
assertRelated(psId2_1);
@@ -568,7 +555,7 @@ public class GetRelatedIT extends AbstractDaemonTest {
PatchSet.Id psId1_1 = getPatchSetId(c1_1);
PatchSet.Id psId2_1 = getPatchSetId(c2_1);
- PatchSet.Id psId2_2 = new PatchSet.Id(psId2_1.changeId, psId2_1.get() + 1);
+ PatchSet.Id psId2_2 = PatchSet.id(psId2_1.changeId(), psId2_1.get() + 1);
assertRelated(psId2_2, changeAndCommit(psId2_2, c2_2, 2), changeAndCommit(psId1_1, c1_1, 1));
}
@@ -611,11 +598,10 @@ public class GetRelatedIT extends AbstractDaemonTest {
Account.Id accountId = accountOperations.newAccount().create();
AccountGroup.UUID groupUuid = groupOperations.newGroup().addMember(accountId).create();
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- PermissionRule rule = Util.allow(u.getConfig(), GlobalCapability.QUERY_LIMIT, groupUuid);
- rule.setRange(0, 2);
- u.save();
- }
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(GlobalCapability.QUERY_LIMIT).group(groupUuid).range(0, 2))
+ .update();
requestScopeOperations.setApiUser(accountId);
assertRelated(lastPsId, expected);
@@ -651,13 +637,8 @@ public class GetRelatedIT extends AbstractDaemonTest {
private static Correspondence<RelatedChangeAndCommitInfo, String>
getRelatedChangeToStatusCorrespondence() {
return Correspondence.from(
- new BinaryPredicate<RelatedChangeAndCommitInfo, String>() {
- @Override
- public boolean apply(
- RelatedChangeAndCommitInfo relatedChangeAndCommitInfo, String status) {
- return Objects.equals(relatedChangeAndCommitInfo.status, status);
- }
- },
+ (relatedChangeAndCommitInfo, status) ->
+ Objects.equals(relatedChangeAndCommitInfo.status, status),
"has status");
}
@@ -678,7 +659,7 @@ public class GetRelatedIT extends AbstractDaemonTest {
PatchSet.Id psId, ObjectId commitId, int currentRevisionNum) {
RelatedChangeAndCommitInfo result = new RelatedChangeAndCommitInfo();
result.project = project.get();
- result._changeNumber = psId.getParentKey().get();
+ result._changeNumber = psId.changeId().get();
result.commit = new CommitInfo();
result.commit.commit = commitId.name();
result._revisionNumber = psId.get();
@@ -690,12 +671,11 @@ public class GetRelatedIT extends AbstractDaemonTest {
private void clearGroups(PatchSet.Id psId) throws Exception {
try (BatchUpdate bu = batchUpdateFactory.create(project, user(user), TimeUtil.nowTs())) {
bu.addOp(
- psId.getParentKey(),
+ psId.changeId(),
new BatchUpdateOp() {
@Override
public boolean updateChange(ChangeContext ctx) {
- PatchSet ps = psUtil.get(ctx.getNotes(), psId);
- psUtil.setGroups(ctx.getUpdate(psId), ps, ImmutableList.of());
+ ctx.getUpdate(psId).setGroups(ImmutableList.of());
return true;
}
});
@@ -711,19 +691,19 @@ public class GetRelatedIT extends AbstractDaemonTest {
private void assertRelated(PatchSet.Id psId, List<RelatedChangeAndCommitInfo> expected)
throws Exception {
List<RelatedChangeAndCommitInfo> actual =
- gApi.changes().id(psId.getParentKey().get()).revision(psId.get()).related().changes;
- assertThat(actual).named("related to " + psId).hasSize(expected.size());
+ gApi.changes().id(psId.changeId().get()).revision(psId.get()).related().changes;
+ assertWithMessage("related to " + psId).that(actual).hasSize(expected.size());
for (int i = 0; i < actual.size(); i++) {
String name = "index " + i + " related to " + psId;
RelatedChangeAndCommitInfo a = actual.get(i);
RelatedChangeAndCommitInfo e = expected.get(i);
- assertThat(a.project).named("project of " + name).isEqualTo(e.project);
- assertThat(a._changeNumber).named("change ID of " + name).isEqualTo(e._changeNumber);
+ assertWithMessage("project of " + name).that(a.project).isEqualTo(e.project);
+ assertWithMessage("change ID of " + name).that(a._changeNumber).isEqualTo(e._changeNumber);
// Don't bother checking changeId; assume _changeNumber is sufficient.
- assertThat(a._revisionNumber).named("revision of " + name).isEqualTo(e._revisionNumber);
- assertThat(a.commit.commit).named("commit of " + name).isEqualTo(e.commit.commit);
- assertThat(a._currentRevisionNumber)
- .named("current revision of " + name)
+ assertWithMessage("revision of " + name).that(a._revisionNumber).isEqualTo(e._revisionNumber);
+ assertWithMessage("commit of " + name).that(a.commit.commit).isEqualTo(e.commit.commit);
+ assertWithMessage("current revision of " + name)
+ .that(a._currentRevisionNumber)
.isEqualTo(e._currentRevisionNumber);
assertThat(a.status).isEqualTo(e.status);
}
diff --git a/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java b/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
index 42fdae42b7..b23f9a3c95 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
@@ -25,9 +25,9 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.Patch.ChangeType;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
import com.google.gerrit.server.patch.IntraLineDiff;
import com.google.gerrit.server.patch.IntraLineDiffArgs;
import com.google.gerrit.server.patch.IntraLineDiffKey;
diff --git a/javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java b/javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java
index 389859cde2..445f7875db 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java
@@ -23,13 +23,13 @@ import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Inject;
import java.util.EnumSet;
@@ -113,7 +113,7 @@ public class SubmittedTogetherIT extends AbstractDaemonTest {
@Test
public void respectWholeTopic() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
// Create two independent commits and push.
RevCommit c1_1 = commitBuilder().add("a.txt", "1").message("subject: 1").create();
String id1 = getChangeId(c1_1);
@@ -135,7 +135,7 @@ public class SubmittedTogetherIT extends AbstractDaemonTest {
@Test
public void anonymousWholeTopic() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
RevCommit a = commitBuilder().add("a", "1").message("change 1").create();
pushHead(testRepo, "refs/for/master/" + name("topic"), false);
String id1 = getChangeId(a);
@@ -157,7 +157,7 @@ public class SubmittedTogetherIT extends AbstractDaemonTest {
@Test
public void topicChaining() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
RevCommit c1_1 = commitBuilder().add("a.txt", "1").message("subject: 1").create();
String id1 = getChangeId(c1_1);
@@ -185,7 +185,7 @@ public class SubmittedTogetherIT extends AbstractDaemonTest {
@Test
public void respectTopicsOnAncestors() throws Exception {
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = projectOperations.project(project).getHead("master");
RevCommit c1_1 = commitBuilder().add("a.txt", "1").message("subject: 1").create();
String id1 = getChangeId(c1_1);
diff --git a/javatests/com/google/gerrit/acceptance/server/config/BUILD b/javatests/com/google/gerrit/acceptance/server/config/BUILD
new file mode 100644
index 0000000000..17802bdffc
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/config/BUILD
@@ -0,0 +1,7 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+
+acceptance_tests(
+ srcs = glob(["*IT.java"]),
+ group = "server_config",
+ labels = ["server"],
+)
diff --git a/javatests/com/google/gerrit/acceptance/server/config/GerritIsReplicaIT.java b/javatests/com/google/gerrit/acceptance/server/config/GerritIsReplicaIT.java
new file mode 100644
index 0000000000..d01a81dd73
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/config/GerritIsReplicaIT.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.server.config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.server.config.GerritIsReplicaProvider;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Inject;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+
+public class GerritIsReplicaIT extends AbstractDaemonTest {
+ @ConfigSuite.Default
+ public static Config defaultConfig() {
+ return new Config();
+ }
+
+ @Inject GerritIsReplicaProvider isReplicaProvider;
+
+ @Test
+ public void isNotReplica() {
+ assertThat(isReplicaProvider.get()).isFalse();
+ }
+
+ @Test
+ @Sandboxed
+ public void isReplica() throws Exception {
+ restartAsSlave();
+ assertThat(isReplicaProvider.get()).isTrue();
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java b/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
index 4f98dd0683..8469fffb25 100644
--- a/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
@@ -15,71 +15,48 @@
package com.google.gerrit.acceptance.server.event;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
-import static com.google.gerrit.server.project.testing.Util.category;
-import static com.google.gerrit.server.project.testing.Util.value;
+import static com.google.gerrit.server.project.testing.TestLabels.label;
+import static com.google.gerrit.server.project.testing.TestLabels.value;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.events.CommentAddedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.project.testing.Util;
import com.google.inject.Inject;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@NoHttpd
public class CommentAddedEventIT extends AbstractDaemonTest {
- @Inject private DynamicSet<CommentAddedListener> source;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
private final LabelType label =
- category("CustomLabel", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
+ label("CustomLabel", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
private final LabelType pLabel =
- category("CustomLabel2", value(1, "Positive"), value(0, "No score"));
-
- private RegistrationHandle eventListenerRegistration;
- private CommentAddedListener.Event lastCommentAddedEvent;
+ label("CustomLabel2", value(1, "Positive"), value(0, "No score"));
@Before
public void setUp() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- AccountGroup.UUID anonymousUsers = systemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID();
- Util.allow(
- u.getConfig(),
- Permission.forLabel(label.getName()),
- -1,
- 1,
- anonymousUsers,
- "refs/heads/*");
- Util.allow(
- u.getConfig(),
- Permission.forLabel(pLabel.getName()),
- 0,
- 1,
- anonymousUsers,
- "refs/heads/*");
- u.save();
- }
-
- eventListenerRegistration = source.add("gerrit", event -> lastCommentAddedEvent = event);
- }
-
- @After
- public void cleanup() {
- eventListenerRegistration.remove();
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(label.getName()).ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, 1))
+ .add(allowLabel(pLabel.getName()).ref("refs/heads/*").group(ANONYMOUS_USERS).range(0, 1))
+ .update();
}
private void saveLabelConfig() throws Exception {
@@ -90,16 +67,30 @@ public class CommentAddedEventIT extends AbstractDaemonTest {
}
}
+ private static class TestListener implements CommentAddedListener {
+ private CommentAddedListener.Event lastCommentAddedEvent;
+
+ @Override
+ public void onCommentAdded(Event event) {
+ lastCommentAddedEvent = event;
+ }
+
+ public CommentAddedListener.Event getLastCommentAddedEvent() {
+ assertThat(lastCommentAddedEvent).isNotNull();
+ return lastCommentAddedEvent;
+ }
+ }
+
/* Need to lookup info for the label under test since there can be multiple
* labels defined. By default Gerrit already has a Code-Review label.
*/
- private ApprovalValues getApprovalValues(LabelType label) {
+ private ApprovalValues getApprovalValues(LabelType label, TestListener listener) {
ApprovalValues res = new ApprovalValues();
- ApprovalInfo info = lastCommentAddedEvent.getApprovals().get(label.getName());
+ ApprovalInfo info = listener.getLastCommentAddedEvent().getApprovals().get(label.getName());
if (info != null) {
res.value = info.value;
}
- info = lastCommentAddedEvent.getOldApprovals().get(label.getName());
+ info = listener.getLastCommentAddedEvent().getOldApprovals().get(label.getName());
if (info != null) {
res.oldValue = info.value;
}
@@ -110,15 +101,18 @@ public class CommentAddedEventIT extends AbstractDaemonTest {
public void newChangeWithVote() throws Exception {
saveLabelConfig();
- // push a new change with -1 vote
- PushOneCommit.Result r = createChange();
- ReviewInput reviewInput = new ReviewInput().label(label.getName(), (short) -1);
- revision(r).review(reviewInput);
- ApprovalValues attr = getApprovalValues(label);
- assertThat(attr.oldValue).isEqualTo(0);
- assertThat(attr.value).isEqualTo(-1);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1: %s-1", label.getName()));
+ TestListener listener = new TestListener();
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
+ // push a new change with -1 vote
+ PushOneCommit.Result r = createChange();
+ ReviewInput reviewInput = new ReviewInput().label(label.getName(), (short) -1);
+ revision(r).review(reviewInput);
+ ApprovalValues attr = getApprovalValues(label, listener);
+ assertThat(attr.oldValue).isEqualTo(0);
+ assertThat(attr.value).isEqualTo(-1);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1: %s-1", label.getName()));
+ }
}
@Test
@@ -129,17 +123,19 @@ public class CommentAddedEventIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
ReviewInput reviewInput = new ReviewInput().message(label.getName());
revision(r).review(reviewInput);
-
- // push a new revision with +1 vote
- ChangeInfo c = info(r.getChangeId());
- r = amendChange(c.changeId);
- reviewInput = new ReviewInput().label(label.getName(), (short) 1);
- revision(r).review(reviewInput);
- ApprovalValues attr = getApprovalValues(label);
- assertThat(attr.oldValue).isEqualTo(0);
- assertThat(attr.value).isEqualTo(1);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 2: %s+1", label.getName()));
+ TestListener listener = new TestListener();
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
+ // push a new revision with +1 vote
+ ChangeInfo c = info(r.getChangeId());
+ r = amendChange(c.changeId);
+ reviewInput = new ReviewInput().label(label.getName(), (short) 1);
+ revision(r).review(reviewInput);
+ ApprovalValues attr = getApprovalValues(label, listener);
+ assertThat(attr.oldValue).isEqualTo(0);
+ assertThat(attr.value).isEqualTo(1);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 2: %s+1", label.getName()));
+ }
}
@Test
@@ -149,114 +145,120 @@ public class CommentAddedEventIT extends AbstractDaemonTest {
// push a change
PushOneCommit.Result r = createChange();
- // review with message only, do not apply votes
- ReviewInput reviewInput = new ReviewInput().message(label.getName());
- revision(r).review(reviewInput);
- // reply message only so vote is shown as 0
- ApprovalValues attr = getApprovalValues(label);
- assertThat(attr.oldValue).isNull();
- assertThat(attr.value).isEqualTo(0);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1:\n\n%s", label.getName()));
-
- // transition from un-voted to -1 vote
- reviewInput = new ReviewInput().label(label.getName(), -1);
- revision(r).review(reviewInput);
- attr = getApprovalValues(label);
- assertThat(attr.oldValue).isEqualTo(0);
- assertThat(attr.value).isEqualTo(-1);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1: %s-1", label.getName()));
-
- // transition vote from -1 to 0
- reviewInput = new ReviewInput().label(label.getName(), 0);
- revision(r).review(reviewInput);
- attr = getApprovalValues(label);
- assertThat(attr.oldValue).isEqualTo(-1);
- assertThat(attr.value).isEqualTo(0);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1: -%s", label.getName()));
-
- // transition vote from 0 to 1
- reviewInput = new ReviewInput().label(label.getName(), 1);
- revision(r).review(reviewInput);
- attr = getApprovalValues(label);
- assertThat(attr.oldValue).isEqualTo(0);
- assertThat(attr.value).isEqualTo(1);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1: %s+1", label.getName()));
-
- // transition vote from 1 to -1
- reviewInput = new ReviewInput().label(label.getName(), -1);
- revision(r).review(reviewInput);
- attr = getApprovalValues(label);
- assertThat(attr.oldValue).isEqualTo(1);
- assertThat(attr.value).isEqualTo(-1);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1: %s-1", label.getName()));
-
- // review with message only, do not apply votes
- reviewInput = new ReviewInput().message(label.getName());
- revision(r).review(reviewInput);
- attr = getApprovalValues(label);
- assertThat(attr.oldValue).isNull(); // no vote change so not included
- assertThat(attr.value).isEqualTo(-1);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1:\n\n%s", label.getName()));
+ TestListener listener = new TestListener();
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
+ // review with message only, do not apply votes
+ ReviewInput reviewInput = new ReviewInput().message(label.getName());
+ revision(r).review(reviewInput);
+ // reply message only so vote is shown as 0
+ ApprovalValues attr = getApprovalValues(label, listener);
+ assertThat(attr.oldValue).isNull();
+ assertThat(attr.value).isEqualTo(0);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1:\n\n%s", label.getName()));
+
+ // transition from un-voted to -1 vote
+ reviewInput = new ReviewInput().label(label.getName(), -1);
+ revision(r).review(reviewInput);
+ attr = getApprovalValues(label, listener);
+ assertThat(attr.oldValue).isEqualTo(0);
+ assertThat(attr.value).isEqualTo(-1);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1: %s-1", label.getName()));
+
+ // transition vote from -1 to 0
+ reviewInput = new ReviewInput().label(label.getName(), 0);
+ revision(r).review(reviewInput);
+ attr = getApprovalValues(label, listener);
+ assertThat(attr.oldValue).isEqualTo(-1);
+ assertThat(attr.value).isEqualTo(0);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1: -%s", label.getName()));
+
+ // transition vote from 0 to 1
+ reviewInput = new ReviewInput().label(label.getName(), 1);
+ revision(r).review(reviewInput);
+ attr = getApprovalValues(label, listener);
+ assertThat(attr.oldValue).isEqualTo(0);
+ assertThat(attr.value).isEqualTo(1);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1: %s+1", label.getName()));
+
+ // transition vote from 1 to -1
+ reviewInput = new ReviewInput().label(label.getName(), -1);
+ revision(r).review(reviewInput);
+ attr = getApprovalValues(label, listener);
+ assertThat(attr.oldValue).isEqualTo(1);
+ assertThat(attr.value).isEqualTo(-1);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1: %s-1", label.getName()));
+
+ // review with message only, do not apply votes
+ reviewInput = new ReviewInput().message(label.getName());
+ revision(r).review(reviewInput);
+ attr = getApprovalValues(label, listener);
+ assertThat(attr.oldValue).isNull(); // no vote change so not included
+ assertThat(attr.value).isEqualTo(-1);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1:\n\n%s", label.getName()));
+ }
}
@Test
public void reviewChange_MultipleVotes() throws Exception {
- saveLabelConfig();
- PushOneCommit.Result r = createChange();
- ReviewInput reviewInput = new ReviewInput().label(label.getName(), -1);
- reviewInput.message = label.getName();
- revision(r).review(reviewInput);
-
- ChangeInfo c = get(r.getChangeId(), DETAILED_LABELS);
- LabelInfo q = c.labels.get(label.getName());
- assertThat(q.all).hasSize(1);
- ApprovalValues labelAttr = getApprovalValues(label);
- assertThat(labelAttr.oldValue).isEqualTo(0);
- assertThat(labelAttr.value).isEqualTo(-1);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1: %s-1\n\n%s", label.getName(), label.getName()));
-
- // there should be 3 approval labels (label, pLabel, and CRVV)
- assertThat(lastCommentAddedEvent.getApprovals()).hasSize(3);
-
- // check the approvals that were not voted on
- ApprovalValues pLabelAttr = getApprovalValues(pLabel);
- assertThat(pLabelAttr.oldValue).isNull();
- assertThat(pLabelAttr.value).isEqualTo(0);
-
- LabelType crLabel = LabelType.withDefaultValues("Code-Review");
- ApprovalValues crlAttr = getApprovalValues(crLabel);
- assertThat(crlAttr.oldValue).isNull();
- assertThat(crlAttr.value).isEqualTo(0);
-
- // update pLabel approval
- reviewInput = new ReviewInput().label(pLabel.getName(), 1);
- reviewInput.message = pLabel.getName();
- revision(r).review(reviewInput);
-
- c = get(r.getChangeId(), DETAILED_LABELS);
- q = c.labels.get(label.getName());
- assertThat(q.all).hasSize(1);
- pLabelAttr = getApprovalValues(pLabel);
- assertThat(pLabelAttr.oldValue).isEqualTo(0);
- assertThat(pLabelAttr.value).isEqualTo(1);
- assertThat(lastCommentAddedEvent.getComment())
- .isEqualTo(String.format("Patch Set 1: %s+1\n\n%s", pLabel.getName(), pLabel.getName()));
-
- // check the approvals that were not voted on
- labelAttr = getApprovalValues(label);
- assertThat(labelAttr.oldValue).isNull();
- assertThat(labelAttr.value).isEqualTo(-1);
-
- crlAttr = getApprovalValues(crLabel);
- assertThat(crlAttr.oldValue).isNull();
- assertThat(crlAttr.value).isEqualTo(0);
+ TestListener listener = new TestListener();
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
+ saveLabelConfig();
+ PushOneCommit.Result r = createChange();
+ ReviewInput reviewInput = new ReviewInput().label(label.getName(), -1);
+ reviewInput.message = label.getName();
+ revision(r).review(reviewInput);
+
+ ChangeInfo c = get(r.getChangeId(), DETAILED_LABELS);
+ LabelInfo q = c.labels.get(label.getName());
+ assertThat(q.all).hasSize(1);
+ ApprovalValues labelAttr = getApprovalValues(label, listener);
+ assertThat(labelAttr.oldValue).isEqualTo(0);
+ assertThat(labelAttr.value).isEqualTo(-1);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1: %s-1\n\n%s", label.getName(), label.getName()));
+
+ // there should be 3 approval labels (label, pLabel, and CRVV)
+ assertThat(listener.getLastCommentAddedEvent().getApprovals()).hasSize(3);
+
+ // check the approvals that were not voted on
+ ApprovalValues pLabelAttr = getApprovalValues(pLabel, listener);
+ assertThat(pLabelAttr.oldValue).isNull();
+ assertThat(pLabelAttr.value).isEqualTo(0);
+
+ LabelType crLabel = LabelType.withDefaultValues("Code-Review");
+ ApprovalValues crlAttr = getApprovalValues(crLabel, listener);
+ assertThat(crlAttr.oldValue).isNull();
+ assertThat(crlAttr.value).isEqualTo(0);
+
+ // update pLabel approval
+ reviewInput = new ReviewInput().label(pLabel.getName(), 1);
+ reviewInput.message = pLabel.getName();
+ revision(r).review(reviewInput);
+
+ c = get(r.getChangeId(), DETAILED_LABELS);
+ q = c.labels.get(label.getName());
+ assertThat(q.all).hasSize(1);
+ pLabelAttr = getApprovalValues(pLabel, listener);
+ assertThat(pLabelAttr.oldValue).isEqualTo(0);
+ assertThat(pLabelAttr.value).isEqualTo(1);
+ assertThat(listener.getLastCommentAddedEvent().getComment())
+ .isEqualTo(String.format("Patch Set 1: %s+1\n\n%s", pLabel.getName(), pLabel.getName()));
+
+ // check the approvals that were not voted on
+ labelAttr = getApprovalValues(label, listener);
+ assertThat(labelAttr.oldValue).isNull();
+ assertThat(labelAttr.value).isEqualTo(-1);
+
+ crlAttr = getApprovalValues(crLabel, listener);
+ assertThat(crlAttr.oldValue).isNull();
+ assertThat(crlAttr.value).isEqualTo(0);
+ }
}
private static class ApprovalValues {
diff --git a/javatests/com/google/gerrit/acceptance/server/event/EventPayloadIT.java b/javatests/com/google/gerrit/acceptance/server/event/EventPayloadIT.java
new file mode 100644
index 0000000000..8744cfadbd
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/event/EventPayloadIT.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.server.event;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.extensions.events.RevisionCreatedListener;
+import com.google.inject.Inject;
+import org.junit.Test;
+
+@NoHttpd
+public class EventPayloadIT extends AbstractDaemonTest {
+ @Inject private ExtensionRegistry extensionRegistry;
+
+ @Test
+ public void defaultOptions() throws Exception {
+ RevisionCreatedListener listener =
+ new RevisionCreatedListener() {
+ @Override
+ public void onRevisionCreated(Event event) {
+ assertThat(event.getChange().submittable).isNotNull();
+ assertThat(event.getRevision().files).isNotEmpty();
+ }
+ };
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
+ createChange();
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "event.payload.listChangeOptions", value = "SKIP_MERGEABLE")
+ public void configuredOptions() throws Exception {
+ RevisionCreatedListener listener =
+ new RevisionCreatedListener() {
+ @Override
+ public void onRevisionCreated(Event event) {
+ assertThat(event.getChange().submittable).isNull();
+ assertThat(event.getChange().mergeable).isNull();
+ assertThat(event.getRevision().files).isNull();
+ assertThat(event.getChange().subject).isNotEmpty();
+ }
+ };
+ try (Registration registration = extensionRegistry.newRegistration().add(listener)) {
+ createChange();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/git/receive/BUILD b/javatests/com/google/gerrit/acceptance/server/git/receive/BUILD
new file mode 100644
index 0000000000..760e7f416e
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/git/receive/BUILD
@@ -0,0 +1,8 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+
+acceptance_tests(
+ srcs = glob(["*IT.java"]),
+ group = "receive",
+ labels = ["server"],
+ deps = [],
+)
diff --git a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
new file mode 100644
index 0000000000..6677583a38
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
@@ -0,0 +1,136 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.server.git.receive;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.PushOneCommit.Result;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.api.changes.DraftInput;
+import com.google.gerrit.extensions.client.Side;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.validators.CommentForValidation;
+import com.google.gerrit.extensions.validators.CommentForValidation.CommentType;
+import com.google.gerrit.extensions.validators.CommentValidator;
+import com.google.gerrit.testing.TestCommentHelper;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+
+/**
+ * Tests for comment validation when publishing drafts via the {@code --publish-comments} option.
+ */
+public class ReceiveCommitsCommentValidationIT extends AbstractDaemonTest {
+ @Inject private CommentValidator mockCommentValidator;
+ @Inject private TestCommentHelper testCommentHelper;
+
+ private static final String COMMENT_TEXT = "The comment text";
+
+ @Captor private ArgumentCaptor<ImmutableList<CommentForValidation>> capture;
+
+ @Override
+ public Module createModule() {
+ return new FactoryModule() {
+ @Override
+ public void configure() {
+ CommentValidator mockCommentValidator = mock(CommentValidator.class);
+ bind(CommentValidator.class)
+ .annotatedWith(Exports.named(mockCommentValidator.getClass()))
+ .toInstance(mockCommentValidator);
+ bind(CommentValidator.class).toInstance(mockCommentValidator);
+ }
+ };
+ }
+
+ @Before
+ public void resetMock() {
+ initMocks(this);
+ clearInvocations(mockCommentValidator);
+ }
+
+ @Test
+ public void validateComments_commentOK() throws Exception {
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(
+ CommentForValidation.create(
+ CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of());
+ PushOneCommit.Result result = createChange();
+ String changeId = result.getChangeId();
+ String revId = result.getCommit().getName();
+ DraftInput comment = testCommentHelper.newDraft(COMMENT_TEXT);
+ testCommentHelper.addDraft(changeId, revId, comment);
+ assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).isEmpty();
+ Result amendResult = amendChange(changeId, "refs/for/master%publish-comments", admin, testRepo);
+ amendResult.assertOkStatus();
+ amendResult.assertNotMessage("Comment validation failure:");
+ assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).hasSize(1);
+ }
+
+ @Test
+ public void validateComments_commentRejected() throws Exception {
+ CommentForValidation commentForValidation =
+ CommentForValidation.create(CommentType.FILE_COMMENT, COMMENT_TEXT);
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(
+ CommentForValidation.create(
+ CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!")));
+ PushOneCommit.Result result = createChange();
+ String changeId = result.getChangeId();
+ String revId = result.getCommit().getName();
+ DraftInput comment = testCommentHelper.newDraft(COMMENT_TEXT);
+ testCommentHelper.addDraft(changeId, revId, comment);
+ assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).isEmpty();
+ Result amendResult = amendChange(changeId, "refs/for/master%publish-comments", admin, testRepo);
+ amendResult.assertOkStatus();
+ amendResult.assertMessage("Comment validation failure:");
+ assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).isEmpty();
+ }
+
+ @Test
+ public void validateComments_inlineVsFileComments_allOK() throws Exception {
+ when(mockCommentValidator.validateComments(capture.capture())).thenReturn(ImmutableList.of());
+ PushOneCommit.Result result = createChange();
+ String changeId = result.getChangeId();
+ String revId = result.getCommit().getName();
+ DraftInput draftFile = testCommentHelper.newDraft(COMMENT_TEXT);
+ testCommentHelper.addDraft(changeId, revId, draftFile);
+ DraftInput draftInline =
+ testCommentHelper.newDraft(
+ result.getChange().currentFilePaths().get(0), Side.REVISION, 1, COMMENT_TEXT);
+ testCommentHelper.addDraft(changeId, revId, draftInline);
+ assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).isEmpty();
+ amendChange(changeId, "refs/for/master%publish-comments", admin, testRepo);
+ assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).hasSize(2);
+ assertThat(capture.getAllValues()).hasSize(1);
+ assertThat(capture.getValue())
+ .containsExactly(
+ CommentForValidation.create(
+ CommentForValidation.CommentType.INLINE_COMMENT, draftInline.message),
+ CommentForValidation.create(
+ CommentForValidation.CommentType.FILE_COMMENT, draftFile.message));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/httpd/BUILD b/javatests/com/google/gerrit/acceptance/server/httpd/BUILD
new file mode 100644
index 0000000000..d1a64c01ed
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/httpd/BUILD
@@ -0,0 +1,7 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+
+acceptance_tests(
+ srcs = glob(["*IT.java"]),
+ group = "server_httpd",
+ labels = ["server"],
+)
diff --git a/javatests/com/google/gerrit/acceptance/server/httpd/HttpLogoutServletIT.java b/javatests/com/google/gerrit/acceptance/server/httpd/HttpLogoutServletIT.java
new file mode 100644
index 0000000000..1dea800849
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/httpd/HttpLogoutServletIT.java
@@ -0,0 +1,112 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.server.httpd;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.StandaloneSiteTest;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.transport.URIish;
+import org.junit.Before;
+import org.junit.Test;
+
+public class HttpLogoutServletIT extends StandaloneSiteTest {
+ private static final String LOCALHOST = InetAddress.getLoopbackAddress().getHostName();
+
+ @ConfigSuite.Config
+ public static Config secondConfig() throws IOException {
+ Config cfg = new Config();
+ cfg.setString("auth", null, "logouturl", "/test-logout");
+ cfg.setString("gerrit", null, "canonicalWebUrl", "https://" + LOCALHOST + ":8443/");
+ cfg.setString("httpd", null, "listenUrl", "proxy-https://" + LOCALHOST + ":" + getFreePort());
+ return cfg;
+ }
+
+ @Inject @GerritServerConfig private Config gerritConfig;
+
+ private HttpClient httpClient;
+
+ @Before
+ public void setUp() {
+ httpClient = HttpClientBuilder.create().disableRedirectHandling().build();
+ }
+
+ @Test
+ public void shouldHonourCanonicalWebUrlProxyWhenRedirectAfterLogout() throws Exception {
+ try (ServerContext ctx = startServer()) {
+ ctx.getInjector().injectMembers(this);
+
+ URIish listenUrl = new URIish(gerritConfig.getString("httpd", null, "listenUrl"));
+ URIish canonicalWebUrl =
+ new URIish(gerritConfig.getString("gerrit", null, "canonicalWebUrl"));
+
+ String logoutPath =
+ Optional.ofNullable(baseConfig.getString("auth", null, "logouturl")).orElse("/");
+
+ HttpGet getLogout = new HttpGet("/logout");
+ getLogout.addHeader("X-Forwarded-Host", canonicalWebUrl.getHost());
+ getLogout.addHeader("X-Forwarded-Port", "" + canonicalWebUrl.getPort());
+ getLogout.addHeader("X-Forwarded-Proto", canonicalWebUrl.getScheme());
+
+ HttpResponse logoutResponse =
+ httpClient.execute(new HttpHost(listenUrl.getHost(), listenUrl.getPort()), getLogout);
+
+ assertThat(logoutResponse.getStatusLine().getStatusCode())
+ .isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);
+ assertThat(getLocationHeaderURIish(logoutResponse))
+ .containsExactly(canonicalWebUrl.setPath(logoutPath));
+ }
+ }
+
+ private List<URIish> getLocationHeaderURIish(HttpResponse logoutResponse) {
+ return Arrays.stream(logoutResponse.getHeaders("Location"))
+ .map(h -> h.getValue())
+ .map(HttpLogoutServletIT::unsafeNewURIish)
+ .filter(u -> u.isPresent())
+ .map(u -> u.get())
+ .collect(Collectors.toList());
+ }
+
+ private static Optional<URIish> unsafeNewURIish(String uri) {
+ try {
+ return Optional.of(new URIish(uri));
+ } catch (URISyntaxException e) {
+ return Optional.empty();
+ }
+ }
+
+ private static int getFreePort() throws IOException {
+ try (ServerSocket s = new ServerSocket(0)) {
+ return s.getLocalPort();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/BUILD b/javatests/com/google/gerrit/acceptance/server/mail/BUILD
index 5d7e65e10c..f90924b3d7 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/BUILD
+++ b/javatests/com/google/gerrit/acceptance/server/mail/BUILD
@@ -7,18 +7,18 @@ DEPS = [
"//java/com/google/gerrit/mail",
]
-acceptance_tests(
- srcs = glob(
- ["*IT.java"],
- exclude = ["AbstractMailIT.java"],
- ),
- group = "server_mail",
+[acceptance_tests(
+ srcs = [f],
+ group = f[:f.index(".")],
labels = [
"no_windows",
"server",
],
deps = DEPS + [":util"],
-)
+) for f in glob(
+ ["*IT.java"],
+ exclude = ["AbstractMailIT.java"],
+)]
java_library(
name = "util",
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
index 25e07085f9..2dc1e246c7 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
@@ -14,6 +14,9 @@
package com.google.gerrit.acceptance.server.mail;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.extensions.api.changes.NotifyHandling.ALL;
import static com.google.gerrit.extensions.api.changes.NotifyHandling.NONE;
import static com.google.gerrit.extensions.api.changes.NotifyHandling.OWNER;
@@ -31,9 +34,11 @@ import com.google.common.collect.ImmutableList;
import com.google.common.truth.Truth;
import com.google.gerrit.acceptance.AbstractNotificationTest;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.AbandonInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AssigneeInput;
@@ -50,9 +55,6 @@ import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.CommitMessageInput;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.restapi.change.PostReview;
import com.google.inject.Inject;
import org.eclipse.jgit.junit.TestRepository;
@@ -61,6 +63,7 @@ import org.junit.Before;
import org.junit.Test;
public class ChangeNotificationsIT extends AbstractNotificationTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
/*
@@ -80,14 +83,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
@Before
public void grantPermissions() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- ProjectConfig cfg = u.getConfig();
- Util.allow(cfg, Permission.FORGE_COMMITTER, REGISTERED_USERS, "refs/*");
- Util.allow(cfg, Permission.SUBMIT, REGISTERED_USERS, "refs/*");
- Util.allow(cfg, Permission.ABANDON, REGISTERED_USERS, "refs/*");
- Util.allow(cfg, Permission.forLabel("Code-Review"), -2, +2, REGISTERED_USERS, "refs/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.FORGE_COMMITTER).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(Permission.ABANDON).ref("refs/*").group(REGISTERED_USERS))
+ .add(allowLabel("Code-Review").ref("refs/*").group(REGISTERED_USERS).range(-2, +2))
+ .update();
}
/*
@@ -599,7 +602,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
if (notify != null) {
in.notify = notify;
}
- gApi.changes().id(changeId).revision("current").review(in);
+ gApi.changes().id(changeId).current().review(in);
};
}
@@ -858,7 +861,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
public void noCommentAndSetWorkInProgress() throws Exception {
StagedChange sc = stageReviewableChange();
ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
- gApi.changes().id(sc.changeId).revision("current").review(in);
+ gApi.changes().id(sc.changeId).current().review(in);
assertThat(sender).didNotSend();
}
@@ -866,7 +869,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
public void commentAndSetWorkInProgress() throws Exception {
StagedChange sc = stageReviewableChange();
ReviewInput in = ReviewInput.noScore().message("ok").setWorkInProgress(true);
- gApi.changes().id(sc.changeId).revision("current").review(in);
+ gApi.changes().id(sc.changeId).current().review(in);
assertThat(sender)
.sent("comment", sc)
.cc(sc.reviewer, sc.ccer)
@@ -881,7 +884,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
public void commentOnWipChangeAndStartReview() throws Exception {
StagedChange sc = stageWipChange();
ReviewInput in = ReviewInput.noScore().message("ok").setWorkInProgress(false);
- gApi.changes().id(sc.changeId).revision("current").review(in);
+ gApi.changes().id(sc.changeId).current().review(in);
assertThat(sender)
.sent("comment", sc)
.cc(sc.reviewer, sc.ccer)
@@ -896,7 +899,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
public void addReviewerOnWipChangeAndStartReview() throws Exception {
StagedChange sc = stageWipChange();
ReviewInput in = ReviewInput.noScore().reviewer(other.email()).setWorkInProgress(false);
- gApi.changes().id(sc.changeId).revision("current").review(in);
+ gApi.changes().id(sc.changeId).current().review(in);
assertThat(sender)
.sent("comment", sc)
.cc(sc.reviewer, sc.ccer, other)
@@ -920,7 +923,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageWipChange();
ReviewInput in =
ReviewInput.noScore().message(PostReview.START_REVIEW_MESSAGE).setWorkInProgress(false);
- gApi.changes().id(sc.changeId).revision("current").review(in);
+ gApi.changes().id(sc.changeId).current().review(in);
Truth.assertThat(sender.getMessages()).isNotEmpty();
String body = sender.getMessages().get(0).body();
int idx = body.indexOf(PostReview.START_REVIEW_MESSAGE);
@@ -950,7 +953,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
ReviewInput in = ReviewInput.recommend();
in.notify = notify;
in.tag = tag;
- gApi.changes().id(changeId).revision("current").review(in);
+ gApi.changes().id(changeId).current().review(in);
}
/*
@@ -1253,7 +1256,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
private void recommend(StagedChange sc, TestAccount by) throws Exception {
requestScopeOperations.setApiUser(by.id());
- gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.recommend());
+ gApi.changes().id(sc.changeId).current().review(ReviewInput.recommend());
}
private interface Stager {
@@ -1267,7 +1270,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.reviewer(extraReviewer.email())
.reviewer(extraCcer.email(), ReviewerState.CC, false);
requestScopeOperations.setApiUser(extraReviewer.id());
- gApi.changes().id(sc.changeId).revision("current").review(in);
+ gApi.changes().id(sc.changeId).current().review(in);
sender.clear();
return sc;
}
@@ -1516,15 +1519,16 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
}
merge(sc.changeId, sc.owner);
- assertThat(sender)
- .named(name)
+ assertWithMessage(name)
+ .about(fakeEmailSenders())
+ .that(sender)
.sent("merged", sc)
.cc(sc.reviewer, sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.bcc(sc.starrer)
.bcc(ALL_COMMENTS, SUBMITTED_CHANGES)
.noOneElse();
- assertThat(sender).named(name).didNotSend();
+ assertWithMessage(name).about(fakeEmailSenders()).that(sender).didNotSend();
}
}
@@ -1626,7 +1630,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
throws Exception {
setEmailStrategy(by, emailStrategy);
requestScopeOperations.setApiUser(by.id());
- gApi.changes().id(changeId).revision("current").submit();
+ gApi.changes().id(changeId).current().submit();
}
private void merge(String changeId, TestAccount by, NotifyHandling notify) throws Exception {
@@ -1640,13 +1644,13 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
requestScopeOperations.setApiUser(by.id());
SubmitInput in = new SubmitInput();
in.notify = notify;
- gApi.changes().id(changeId).revision("current").submit(in);
+ gApi.changes().id(changeId).current().submit(in);
}
private StagedChange stageChangeReadyForMerge() throws Exception {
StagedChange sc = stageReviewableChange();
requestScopeOperations.setApiUser(sc.reviewer.id());
- gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.approve());
+ gApi.changes().id(sc.changeId).current().review(ReviewInput.approve());
sender.clear();
return sc;
}
@@ -2039,7 +2043,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc, TestAccount by, @Nullable NotifyHandling notify, EmailStrategy emailStrategy)
throws Exception {
setEmailStrategy(by, emailStrategy);
- CommitInfo commit = gApi.changes().id(sc.changeId).revision("current").commit(false);
+ CommitInfo commit = gApi.changes().id(sc.changeId).current().commit(false);
CommitMessageInput in = new CommitMessageInput();
in.message = "update\n" + commit.message;
in.notify = notify;
@@ -2254,8 +2258,8 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
private StagedChange stageChange() throws Exception {
StagedChange sc = stageReviewableChange();
requestScopeOperations.setApiUser(admin.id());
- gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.approve());
- gApi.changes().id(sc.changeId).revision("current").submit();
+ gApi.changes().id(sc.changeId).current().review(ReviewInput.approve());
+ gApi.changes().id(sc.changeId).current().submit();
sender.clear();
return sc;
}
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
index 1386aec56f..0826c166ab 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
@@ -15,11 +15,12 @@
package com.google.gerrit.acceptance.server.mail;
import static com.google.common.truth.Truth.assertThat;
-import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.UseTimezone;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
@@ -27,7 +28,6 @@ import com.google.gerrit.mail.EmailHeader;
import com.google.gerrit.mail.MailProcessingUtil;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.testing.FakeEmailSender;
-import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.sql.Timestamp;
import java.time.ZoneId;
@@ -37,28 +37,14 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
/** Tests the presence of required metadata in email headers, text and html. */
+@UseClockStep
+@UseTimezone(timezone = "US/Eastern")
public class MailMetadataIT extends AbstractDaemonTest {
@Inject private RequestScopeOperations requestScopeOperations;
- private String systemTimeZone;
-
- @Before
- public void setTimeForTesting() {
- systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- }
-
- @After
- public void resetTime() {
- TestTimeUtil.useSystemTime();
- System.setProperty("user.timezone", systemTimeZone);
- }
-
@Test
public void metadataOnNewChange() throws Exception {
PushOneCommit.Result newChange = createChange();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
index f917fd894b..5531709f0b 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
@@ -15,26 +15,68 @@
package com.google.gerrit.acceptance.server.mail;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommentInfo;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.validators.CommentForValidation;
+import com.google.gerrit.extensions.validators.CommentValidator;
import com.google.gerrit.mail.MailMessage;
import com.google.gerrit.mail.MailProcessingUtil;
import com.google.gerrit.server.mail.receive.MailProcessor;
import com.google.gerrit.testing.FakeEmailSender.Message;
+import com.google.gerrit.testing.TestCommentHelper;
import com.google.inject.Inject;
+import com.google.inject.Module;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.List;
+import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
public class MailProcessorIT extends AbstractMailIT {
@Inject private MailProcessor mailProcessor;
@Inject private AccountOperations accountOperations;
+ @Inject private TestCommentHelper testCommentHelper;
+
+ private static final CommentValidator mockCommentValidator = mock(CommentValidator.class);
+
+ private static final String COMMENT_TEXT = "The comment text";
+
+ @Override
+ public Module createModule() {
+ return new FactoryModule() {
+ @Override
+ public void configure() {
+ bind(CommentValidator.class)
+ .annotatedWith(Exports.named(mockCommentValidator.getClass()))
+ .toInstance(mockCommentValidator);
+ bind(CommentValidator.class).toInstance(mockCommentValidator);
+ }
+ };
+ }
+
+ @BeforeClass
+ public static void setUpMock() {
+ // Let the mock comment validator accept all comments during test setup.
+ when(mockCommentValidator.validateComments(any())).thenReturn(ImmutableList.of());
+ }
+
+ @Before
+ public void setUp() {
+ clearInvocations(mockCommentValidator);
+ }
@Test
public void parseAndPersistChangeMessage() throws Exception {
@@ -223,7 +265,87 @@ public class MailProcessorIT extends AbstractMailIT {
assertThat(message.headers()).containsKey("Subject");
}
+ @Test
+ public void validateChangeMessage_rejected() throws Exception {
+ String changeId = createChangeWithReview();
+ ChangeInfo changeInfo = gApi.changes().id(changeId).get();
+ List<CommentInfo> comments = gApi.changes().id(changeId).current().commentsAsList();
+ String ts =
+ MailProcessingUtil.rfcDateformatter.format(
+ ZonedDateTime.ofInstant(comments.get(0).updated.toInstant(), ZoneId.of("UTC")));
+
+ setupFailValidation(CommentForValidation.CommentType.CHANGE_MESSAGE);
+
+ MailMessage.Builder b = messageBuilderWithDefaultFields();
+ String txt = newPlaintextBody(getChangeUrl(changeInfo) + "/1", COMMENT_TEXT, null, null, null);
+ b.textContent(txt + textFooterForChange(changeInfo._number, ts));
+
+ Collection<CommentInfo> commentsBefore = testCommentHelper.getPublishedComments(changeId);
+ mailProcessor.process(b.build());
+ assertThat(testCommentHelper.getPublishedComments(changeId)).isEqualTo(commentsBefore);
+
+ assertNotifyTo(user);
+ Message message = sender.nextMessage();
+ assertThat(message.body()).contains("rejected one or more comments");
+ }
+
+ @Test
+ public void validateInlineComment_rejected() throws Exception {
+ String changeId = createChangeWithReview();
+ ChangeInfo changeInfo = gApi.changes().id(changeId).get();
+ List<CommentInfo> comments = gApi.changes().id(changeId).current().commentsAsList();
+ String ts =
+ MailProcessingUtil.rfcDateformatter.format(
+ ZonedDateTime.ofInstant(comments.get(0).updated.toInstant(), ZoneId.of("UTC")));
+
+ setupFailValidation(CommentForValidation.CommentType.INLINE_COMMENT);
+
+ MailMessage.Builder b = messageBuilderWithDefaultFields();
+ String txt = newPlaintextBody(getChangeUrl(changeInfo) + "/1", null, COMMENT_TEXT, null, null);
+ b.textContent(txt + textFooterForChange(changeInfo._number, ts));
+
+ Collection<CommentInfo> commentsBefore = testCommentHelper.getPublishedComments(changeId);
+ mailProcessor.process(b.build());
+ assertThat(testCommentHelper.getPublishedComments(changeId)).isEqualTo(commentsBefore);
+
+ assertNotifyTo(user);
+ Message message = sender.nextMessage();
+ assertThat(message.body()).contains("rejected one or more comments");
+ }
+
+ @Test
+ public void validateFileComment_rejected() throws Exception {
+ String changeId = createChangeWithReview();
+ ChangeInfo changeInfo = gApi.changes().id(changeId).get();
+ List<CommentInfo> comments = gApi.changes().id(changeId).current().commentsAsList();
+ String ts =
+ MailProcessingUtil.rfcDateformatter.format(
+ ZonedDateTime.ofInstant(comments.get(0).updated.toInstant(), ZoneId.of("UTC")));
+
+ setupFailValidation(CommentForValidation.CommentType.FILE_COMMENT);
+
+ MailMessage.Builder b = messageBuilderWithDefaultFields();
+ String txt = newPlaintextBody(getChangeUrl(changeInfo) + "/1", null, null, COMMENT_TEXT, null);
+ b.textContent(txt + textFooterForChange(changeInfo._number, ts));
+
+ Collection<CommentInfo> commentsBefore = testCommentHelper.getPublishedComments(changeId);
+ mailProcessor.process(b.build());
+ assertThat(testCommentHelper.getPublishedComments(changeId)).isEqualTo(commentsBefore);
+
+ assertNotifyTo(user);
+ Message message = sender.nextMessage();
+ assertThat(message.body()).contains("rejected one or more comments");
+ }
+
private String getChangeUrl(ChangeInfo changeInfo) {
return canonicalWebUrl.get() + "c/" + changeInfo.project + "/+/" + changeInfo._number;
}
+
+ private void setupFailValidation(CommentForValidation.CommentType type) {
+ CommentForValidation commentForValidation = CommentForValidation.create(type, COMMENT_TEXT);
+
+ when(mockCommentValidator.validateComments(
+ ImmutableList.of(CommentForValidation.create(type, COMMENT_TEXT))))
+ .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!")));
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/BUILD b/javatests/com/google/gerrit/acceptance/server/notedb/BUILD
index bdb3f3bc3c..ee3bcb019f 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/BUILD
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/BUILD
@@ -7,9 +7,6 @@ acceptance_tests(
"notedb",
"server",
],
- # TODO(dborowitz): Fix leaks in local disk tests so we can reduce heap size.
- # http://crbug.com/gerrit/8567
- vm_args = ["-Xmx1024m"],
deps = [
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/server/util/time",
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
index 748c4eaf9b..c502c798ae 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
@@ -15,17 +15,19 @@
package com.google.gerrit.acceptance.server.notedb;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateListener;
@@ -126,12 +128,9 @@ public class NoteDbOnlyIT extends AbstractDaemonTest {
throw new ResourceConflictException(msg);
}
});
- try {
- bu.execute();
- fail("expected ResourceConflictException");
- } catch (ResourceConflictException e) {
- assertThat(e).hasMessageThat().isEqualTo(msg);
- }
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> bu.execute());
+ assertThat(thrown).hasMessageThat().isEqualTo(msg);
}
// If updateChange hadn't failed, backup would have been updated to master2.
@@ -188,18 +187,13 @@ public class NoteDbOnlyIT extends AbstractDaemonTest {
@Test
public void missingChange() throws Exception {
- Change.Id changeId = new Change.Id(1234567);
+ Change.Id changeId = Change.id(1234567);
assertNoSuchChangeException(() -> notesFactory.create(project, changeId));
assertNoSuchChangeException(() -> notesFactory.createChecked(project, changeId));
}
private void assertNoSuchChangeException(Callable<?> callable) throws Exception {
- try {
- callable.call();
- fail("expected NoSuchChangeException");
- } catch (NoSuchChangeException e) {
- // Expected.
- }
+ assertThrows(NoSuchChangeException.class, () -> callable.call());
}
private class ConcurrentWritingListener implements BatchUpdateListener {
@@ -306,8 +300,8 @@ public class NoteDbOnlyIT extends AbstractDaemonTest {
if (repo instanceof InMemoryRepository) {
((InMemoryRepository) repo).setPerformsAtomicTransactions(true);
} else {
- assertThat(repo.getRefDatabase().performsAtomicTransactions())
- .named("performsAtomicTransactions on %s", repo)
+ assertWithMessage("performsAtomicTransactions on %s", repo)
+ .that(repo.getRefDatabase().performsAtomicTransactions())
.isTrue();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
index 2919e5fe97..12cae84b0b 100644
--- a/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
@@ -19,9 +19,9 @@ import static org.junit.Assert.assertNotEquals;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.conditions.BooleanCondition;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -122,7 +122,7 @@ public class PermissionBackendConditionIT extends AbstractDaemonTest {
@Test
public void refPermissions_sameResourceAndUserEquals() throws Exception {
- Branch.NameKey branch = new Branch.NameKey(project, "branch");
+ BranchNameKey branch = BranchNameKey.create(project, "branch");
BooleanCondition cond1 = pb.user(user()).ref(branch).testCond(RefPermission.READ);
BooleanCondition cond2 = pb.user(user()).ref(branch).testCond(RefPermission.READ);
@@ -132,7 +132,7 @@ public class PermissionBackendConditionIT extends AbstractDaemonTest {
@Test
public void refPermissions_sameResourceAndDifferentUserDoesNotEqual() throws Exception {
- Branch.NameKey branch = new Branch.NameKey(project, "branch");
+ BranchNameKey branch = BranchNameKey.create(project, "branch");
BooleanCondition cond1 = pb.user(user()).ref(branch).testCond(RefPermission.READ);
BooleanCondition cond2 = pb.user(admin()).ref(branch).testCond(RefPermission.READ);
@@ -142,8 +142,8 @@ public class PermissionBackendConditionIT extends AbstractDaemonTest {
@Test
public void refPermissions_differentResourceAndSameUserDoesNotEqual() throws Exception {
- Branch.NameKey branch1 = new Branch.NameKey(project, "branch");
- Branch.NameKey branch2 = new Branch.NameKey(project, "branch2");
+ BranchNameKey branch1 = BranchNameKey.create(project, "branch");
+ BranchNameKey branch2 = BranchNameKey.create(project, "branch2");
BooleanCondition cond1 = pb.user(user()).ref(branch1).testCond(RefPermission.READ);
BooleanCondition cond2 = pb.user(user()).ref(branch2).testCond(RefPermission.READ);
@@ -153,8 +153,8 @@ public class PermissionBackendConditionIT extends AbstractDaemonTest {
@Test
public void refPermissions_differentResourceAndSameUserDoesNotEqual2() throws Exception {
- Branch.NameKey branch1 = new Branch.NameKey(project, "branch");
- Branch.NameKey branch2 = new Branch.NameKey(projectOperations.newProject().create(), "branch");
+ BranchNameKey branch1 = BranchNameKey.create(project, "branch");
+ BranchNameKey branch2 = BranchNameKey.create(projectOperations.newProject().create(), "branch");
BooleanCondition cond1 = pb.user(user()).ref(branch1).testCond(RefPermission.READ);
BooleanCondition cond2 = pb.user(user()).ref(branch2).testCond(RefPermission.READ);
diff --git a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
index 6cbe40e56b..3b7d826049 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
@@ -15,6 +15,8 @@
package com.google.gerrit.acceptance.server.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.labelPermissionKey;
import static com.google.gerrit.common.data.LabelFunction.ANY_WITH_BLOCK;
import static com.google.gerrit.common.data.LabelFunction.MAX_NO_BLOCK;
import static com.google.gerrit.common.data.LabelFunction.MAX_WITH_BLOCK;
@@ -24,68 +26,50 @@ import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LAB
import static com.google.gerrit.extensions.client.ListChangesOption.LABELS;
import static com.google.gerrit.extensions.client.ListChangesOption.SUBMITTABLE;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
-import static com.google.gerrit.server.project.testing.Util.category;
-import static com.google.gerrit.server.project.testing.Util.value;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.server.project.testing.TestLabels.label;
+import static com.google.gerrit.server.project.testing.TestLabels.value;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.events.CommentAddedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.testing.Util;
import com.google.inject.Inject;
import java.util.Arrays;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@NoHttpd
public class CustomLabelIT extends AbstractDaemonTest {
- @Inject private DynamicSet<CommentAddedListener> source;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private ExtensionRegistry extensionRegistry;
private final LabelType label =
- category("CustomLabel", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
+ label("CustomLabel", value(1, "Positive"), value(0, "No score"), value(-1, "Negative"));
- private final LabelType P = category("CustomLabel2", value(1, "Positive"), value(0, "No score"));
-
- private RegistrationHandle eventListenerRegistration;
- private CommentAddedListener.Event lastCommentAddedEvent;
+ private final LabelType P = label("CustomLabel2", value(1, "Positive"), value(0, "No score"));
@Before
public void setUp() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- AccountGroup.UUID anonymousUsers = systemGroupBackend.getGroup(ANONYMOUS_USERS).getUUID();
- Util.allow(
- u.getConfig(),
- Permission.forLabel(label.getName()),
- -1,
- 1,
- anonymousUsers,
- "refs/heads/*");
- Util.allow(
- u.getConfig(), Permission.forLabel(P.getName()), 0, 1, anonymousUsers, "refs/heads/*");
- u.save();
- }
-
- eventListenerRegistration = source.add("gerrit", event -> lastCommentAddedEvent = event);
- }
-
- @After
- public void cleanup() {
- eventListenerRegistration.remove();
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(label.getName()).ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, 1))
+ .add(allowLabel(P.getName()).ref("refs/heads/*").group(ANONYMOUS_USERS).range(0, 1))
+ .update();
}
@Test
@@ -172,28 +156,41 @@ public class CustomLabelIT extends AbstractDaemonTest {
assertThat(q.blocking).isTrue();
}
- @Test
- public void customLabelAnyWithBlock_Addreviewer_ZeroVote() throws Exception {
- P.setFunction(ANY_WITH_BLOCK);
- saveLabelConfig();
- PushOneCommit.Result r = createChange();
- AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email();
- gApi.changes().id(r.getChangeId()).addReviewer(in);
+ private static class TestListener implements CommentAddedListener {
+ public CommentAddedListener.Event lastCommentAddedEvent;
- ReviewInput input = new ReviewInput().label(P.getName(), 0);
- input.message = "foo";
+ @Override
+ public void onCommentAdded(Event event) {
+ lastCommentAddedEvent = event;
+ }
+ }
- revision(r).review(input);
- ChangeInfo c = getWithLabels(r);
- LabelInfo q = c.labels.get(P.getName());
- assertThat(q.all).hasSize(2);
- assertThat(q.approved).isNull();
- assertThat(q.recommended).isNull();
- assertThat(q.disliked).isNull();
- assertThat(q.rejected).isNull();
- assertThat(q.blocking).isNull();
- assertThat(lastCommentAddedEvent.getComment()).isEqualTo("Patch Set 1:\n\n" + input.message);
+ @Test
+ public void customLabelAnyWithBlock_Addreviewer_ZeroVote() throws Exception {
+ TestListener testListener = new TestListener();
+ try (Registration registration = extensionRegistry.newRegistration().add(testListener)) {
+ P.setFunction(ANY_WITH_BLOCK);
+ saveLabelConfig();
+ PushOneCommit.Result r = createChange();
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = user.email();
+ gApi.changes().id(r.getChangeId()).addReviewer(in);
+
+ ReviewInput input = new ReviewInput().label(P.getName(), 0);
+ input.message = "foo";
+
+ revision(r).review(input);
+ ChangeInfo c = getWithLabels(r);
+ LabelInfo q = c.labels.get(P.getName());
+ assertThat(q.all).hasSize(1);
+ assertThat(q.approved).isNull();
+ assertThat(q.recommended).isNull();
+ assertThat(q.disliked).isNull();
+ assertThat(q.rejected).isNull();
+ assertThat(q.blocking).isNull();
+ assertThat(testListener.lastCommentAddedEvent.getComment())
+ .isEqualTo("Patch Set 1:\n\n" + input.message);
+ }
}
@Test
@@ -265,15 +262,17 @@ public class CustomLabelIT extends AbstractDaemonTest {
assertPermitted(info, P.getName(), 0, 1);
assertPermitted(info, label.getName());
- ReviewInput in = new ReviewInput();
- in.label(P.getName(), P.getMax().getValue());
- revision(r).review(in);
-
- in = new ReviewInput();
- in.label(label.getName(), label.getMax().getValue());
- exception.expect(ResourceConflictException.class);
- exception.expectMessage("Voting on labels disallowed after submit: " + label.getName());
- revision(r).review(in);
+ ReviewInput postSubmitReview1 = new ReviewInput();
+ postSubmitReview1.label(P.getName(), P.getMax().getValue());
+ revision(r).review(postSubmitReview1);
+
+ ReviewInput postSubmitReview2 = new ReviewInput();
+ postSubmitReview2.label(label.getName(), label.getMax().getValue());
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> revision(r).review(postSubmitReview2));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Voting on labels disallowed after submit: " + label.getName());
}
@Test
@@ -289,11 +288,11 @@ public class CustomLabelIT extends AbstractDaemonTest {
value(-1, "I would prefer this is not merged as is"),
value(-2, "This shall not be merged"));
- AccountGroup.UUID registered = SystemGroupBackend.REGISTERED_USERS;
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(u.getConfig(), Permission.forLabel(testLabel), -2, +2, registered, "refs/heads/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(testLabel).ref("refs/heads/*").group(REGISTERED_USERS).range(-2, +2))
+ .update();
PushOneCommit.Result result = createChange();
String changeId = result.getChangeId();
@@ -311,11 +310,12 @@ public class CustomLabelIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(changeId).get().submittable).isTrue();
// Update admin's permitted range for 'Test-Label' to be -1...+1.
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.remove(u.getConfig(), Permission.forLabel(testLabel), registered, "refs/heads/*");
- Util.allow(u.getConfig(), Permission.forLabel(testLabel), -1, +1, registered, "refs/heads/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .remove(labelPermissionKey(testLabel).ref("refs/heads/*").group(REGISTERED_USERS))
+ .add(allowLabel(testLabel).ref("refs/heads/*").group(REGISTERED_USERS).range(-1, +1))
+ .update();
// Verify admin doesn't have +2 permission any more.
assertPermitted(gApi.changes().id(changeId).get(), testLabel, -1, 0, 1);
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
index 4ed16eead6..29574c4e84 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.server.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.server.StarredChangesUtil.IGNORE_LABEL;
import com.google.common.collect.ImmutableSet;
@@ -25,12 +26,12 @@ import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.StarsInput;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.git.NotifyConfig;
import com.google.gerrit.testing.FakeEmailSender.Message;
@@ -218,7 +219,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// push a change to watched project -> should trigger email notification
requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
- cloneProject(new Project.NameKey(watchedProject), admin);
+ cloneProject(Project.nameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
.create(admin.newIdent(), watchedRepo, "TRIGGER", "a", "a1")
@@ -229,7 +230,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// notification
String notWatchedProject = projectOperations.newProject().create().get();
TestRepository<InMemoryRepository> notWatchedRepo =
- cloneProject(new Project.NameKey(notWatchedProject), admin);
+ cloneProject(Project.nameKey(notWatchedProject), admin);
r =
pushFactory
.create(admin.newIdent(), notWatchedRepo, "DONT_TRIGGER", "a", "a1")
@@ -261,7 +262,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// user
requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
- cloneProject(new Project.NameKey(watchedProject), admin);
+ cloneProject(Project.nameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
.create(admin.newIdent(), watchedRepo, "TRIGGER", "a.txt", "a1")
@@ -305,15 +306,15 @@ public class ProjectWatchIT extends AbstractDaemonTest {
requestScopeOperations.setApiUser(user.id());
// watch keyword in project as user
- watch(watchedProject, "multimaster");
+ watch(watchedProject, "multiprimary");
// push a change with keyword -> should trigger email notification
requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
- cloneProject(new Project.NameKey(watchedProject), admin);
+ cloneProject(Project.nameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(admin.newIdent(), watchedRepo, "Document multimaster setup", "a.txt", "a1")
+ .create(admin.newIdent(), watchedRepo, "Document multiprimary setup", "a.txt", "a1")
.to("refs/for/master");
r.assertOkStatus();
@@ -322,7 +323,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
assertThat(messages).hasSize(1);
Message m = messages.get(0);
assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
- assertThat(m.body()).contains("Change subject: Document multimaster setup\n");
+ assertThat(m.body()).contains("Change subject: Document multiprimary setup\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
@@ -347,8 +348,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// push a change to any project -> should trigger email notification
requestScopeOperations.setApiUser(admin.id());
- TestRepository<InMemoryRepository> anyRepo =
- cloneProject(new Project.NameKey(anyProject), admin);
+ TestRepository<InMemoryRepository> anyRepo = cloneProject(Project.nameKey(anyProject), admin);
PushOneCommit.Result r =
pushFactory.create(admin.newIdent(), anyRepo, "TRIGGER", "a", "a1").to("refs/for/master");
r.assertOkStatus();
@@ -374,8 +374,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// push a change to watched file in any project -> should trigger email
// notification for user
requestScopeOperations.setApiUser(admin.id());
- TestRepository<InMemoryRepository> anyRepo =
- cloneProject(new Project.NameKey(anyProject), admin);
+ TestRepository<InMemoryRepository> anyRepo = cloneProject(Project.nameKey(anyProject), admin);
PushOneCommit.Result r =
pushFactory
.create(admin.newIdent(), anyRepo, "TRIGGER", "a.txt", "a1")
@@ -419,16 +418,15 @@ public class ProjectWatchIT extends AbstractDaemonTest {
requestScopeOperations.setApiUser(user.id());
// watch keyword in project as user
- watch(allProjects.get(), "multimaster");
+ watch(allProjects.get(), "multiprimary");
// push a change with keyword to any project -> should trigger email
// notification
requestScopeOperations.setApiUser(admin.id());
- TestRepository<InMemoryRepository> anyRepo =
- cloneProject(new Project.NameKey(anyProject), admin);
+ TestRepository<InMemoryRepository> anyRepo = cloneProject(Project.nameKey(anyProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(admin.newIdent(), anyRepo, "Document multimaster setup", "a.txt", "a1")
+ .create(admin.newIdent(), anyRepo, "Document multiprimary setup", "a.txt", "a1")
.to("refs/for/master");
r.assertOkStatus();
@@ -437,7 +435,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
assertThat(messages).hasSize(1);
Message m = messages.get(0);
assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
- assertThat(m.body()).contains("Change subject: Document multimaster setup\n");
+ assertThat(m.body()).contains("Change subject: Document multiprimary setup\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
@@ -463,7 +461,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// push a change to watched project
requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
- cloneProject(new Project.NameKey(watchedProject), admin);
+ cloneProject(Project.nameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
.create(admin.newIdent(), watchedRepo, "ignored change", "a", "a1")
@@ -496,7 +494,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// push a private change to watched project -> should not trigger email notification
requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
- cloneProject(new Project.NameKey(watchedProject), admin);
+ cloneProject(Project.nameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
.create(admin.newIdent(), watchedRepo, "private change", "a", "a1")
@@ -514,12 +512,14 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// create group that can view all private changes
GroupInfo groupThatCanViewPrivateChanges =
gApi.groups().create("groupThatCanViewPrivateChanges").get();
- grant(
- new Project.NameKey(watchedProject),
- "refs/*",
- Permission.VIEW_PRIVATE_CHANGES,
- false,
- new AccountGroup.UUID(groupThatCanViewPrivateChanges.id));
+ projectOperations
+ .project(Project.nameKey(watchedProject))
+ .forUpdate()
+ .add(
+ allow(Permission.VIEW_PRIVATE_CHANGES)
+ .ref("refs/*")
+ .group(AccountGroup.uuid(groupThatCanViewPrivateChanges.id)))
+ .update();
// watch project as user that can't view private changes
requestScopeOperations.setApiUser(user.id());
@@ -536,7 +536,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// userThatCanViewPrivateChanges, but not for user
requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
- cloneProject(new Project.NameKey(watchedProject), admin);
+ cloneProject(Project.nameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
.create(admin.newIdent(), watchedRepo, "TRIGGER", "a", "a1")
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java b/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
index da3a257915..11d39b445d 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
@@ -15,21 +15,24 @@
package com.google.gerrit.acceptance.server.project;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.groups.GroupApi;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.api.projects.ReflogEntryInfo;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.project.testing.Util;
import com.google.inject.Inject;
import java.io.File;
import java.util.List;
@@ -39,6 +42,7 @@ import org.junit.Test;
@UseLocalDisk
public class ReflogIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private RequestScopeOperations requestScopeOperations;
@Test
@@ -55,7 +59,7 @@ public class ReflogIT extends AbstractDaemonTest {
gApi.changes().id(id.get()).topic("foo");
ReflogEntry last = repo.getReflogReader(changeMetaRef(id)).getLastEntry();
- assertThat(last).named("last RefLogEntry").isNotNull();
+ assertWithMessage("last RefLogEntry").that(last).isNotNull();
assertThat(last.getComment()).isEqualTo("restapi.change.PutTopic");
}
}
@@ -85,8 +89,8 @@ public class ReflogIT extends AbstractDaemonTest {
@Test
public void regularUserIsNotAllowedToGetReflog() throws Exception {
requestScopeOperations.setApiUser(user.id());
- exception.expect(AuthException.class);
- gApi.projects().name(project.get()).branch("master").reflog();
+ assertThrows(
+ AuthException.class, () -> gApi.projects().name(project.get()).branch("master").reflog());
}
@Test
@@ -94,11 +98,11 @@ public class ReflogIT extends AbstractDaemonTest {
GroupApi groupApi = gApi.groups().create(name("get-reflog"));
groupApi.addMembers("user");
- try (ProjectConfigUpdate u = updateProject(project)) {
- Util.allow(
- u.getConfig(), Permission.OWNER, new AccountGroup.UUID(groupApi.get().id), "refs/*");
- u.save();
- }
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(Permission.OWNER).ref("refs/*").group(AccountGroup.uuid(groupApi.get().id)))
+ .update();
requestScopeOperations.setApiUser(user.id());
gApi.projects().name(project.get()).branch("master").reflog();
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java b/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
index adc7807101..b3647e7363 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
@@ -15,16 +15,17 @@
package com.google.gerrit.acceptance.server.quota;
import static com.google.common.truth.Truth.assertThat;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.resetToStrict;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.quota.QuotaBackend;
import com.google.gerrit.server.quota.QuotaEnforcer;
@@ -34,13 +35,12 @@ import com.google.gerrit.server.quota.QuotaResponse;
import com.google.inject.Inject;
import com.google.inject.Module;
import java.util.Collections;
-import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
public class DefaultQuotaBackendIT extends AbstractDaemonTest {
- private static final QuotaEnforcer quotaEnforcer = EasyMock.createStrictMock(QuotaEnforcer.class);
+ private static final QuotaEnforcer quotaEnforcer = mock(QuotaEnforcer.class);
private IdentifiedUser identifiedAdmin;
@Inject private QuotaBackend quotaBackend;
@@ -60,14 +60,13 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
@Before
public void setUp() {
identifiedAdmin = identifiedUserFactory.create(admin.id());
- resetToStrict(quotaEnforcer);
+ clearInvocations(quotaEnforcer);
}
@Test
public void requestTokenForUser() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
- replay(quotaEnforcer);
+ when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok());
assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup"))
.isEqualTo(singletonAggregation(QuotaResponse.ok()));
}
@@ -76,8 +75,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
public void requestTokenForUserAndAccount() {
QuotaRequestContext ctx =
QuotaRequestContext.builder().user(identifiedAdmin).account(user.id()).build();
- expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
- replay(quotaEnforcer);
+ when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok());
assertThat(quotaBackend.user(identifiedAdmin).account(user.id()).requestToken("testGroup"))
.isEqualTo(singletonAggregation(QuotaResponse.ok()));
}
@@ -86,8 +84,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
public void requestTokenForUserAndProject() {
QuotaRequestContext ctx =
QuotaRequestContext.builder().user(identifiedAdmin).project(project).build();
- expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
- replay(quotaEnforcer);
+ when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok());
assertThat(quotaBackend.user(identifiedAdmin).project(project).requestToken("testGroup"))
.isEqualTo(singletonAggregation(QuotaResponse.ok()));
}
@@ -101,8 +98,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
.change(changeId)
.project(project)
.build();
- expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
- replay(quotaEnforcer);
+ when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok());
assertThat(
quotaBackend.user(identifiedAdmin).change(changeId, project).requestToken("testGroup"))
.isEqualTo(singletonAggregation(QuotaResponse.ok()));
@@ -111,8 +107,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
@Test
public void requestTokens() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcer.requestTokens("testGroup", ctx, 123)).andReturn(QuotaResponse.ok());
- replay(quotaEnforcer);
+ when(quotaEnforcer.requestTokens("testGroup", ctx, 123)).thenReturn(QuotaResponse.ok());
assertThat(quotaBackend.user(identifiedAdmin).requestTokens("testGroup", 123))
.isEqualTo(singletonAggregation(QuotaResponse.ok()));
}
@@ -120,8 +115,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
@Test
public void dryRun() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcer.dryRun("testGroup", ctx, 123)).andReturn(QuotaResponse.ok());
- replay(quotaEnforcer);
+ when(quotaEnforcer.dryRun("testGroup", ctx, 123)).thenReturn(QuotaResponse.ok());
assertThat(quotaBackend.user(identifiedAdmin).dryRun("testGroup", 123))
.isEqualTo(singletonAggregation(QuotaResponse.ok()));
}
@@ -131,8 +125,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
QuotaRequestContext ctx =
QuotaRequestContext.builder().user(identifiedAdmin).account(user.id()).build();
QuotaResponse r = QuotaResponse.ok(10L);
- expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r);
- replay(quotaEnforcer);
+ when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(r);
assertThat(quotaBackend.user(identifiedAdmin).account(user.id()).availableTokens("testGroup"))
.isEqualTo(singletonAggregation(r));
}
@@ -142,8 +135,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
QuotaRequestContext ctx =
QuotaRequestContext.builder().user(identifiedAdmin).project(project).build();
QuotaResponse r = QuotaResponse.ok(10L);
- expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r);
- replay(quotaEnforcer);
+ when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(r);
assertThat(quotaBackend.user(identifiedAdmin).project(project).availableTokens("testGroup"))
.isEqualTo(singletonAggregation(r));
}
@@ -158,8 +150,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
.project(project)
.build();
QuotaResponse r = QuotaResponse.ok(10L);
- expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r);
- replay(quotaEnforcer);
+ when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(r);
assertThat(
quotaBackend
.user(identifiedAdmin)
@@ -172,8 +163,7 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
public void availableTokens() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
QuotaResponse r = QuotaResponse.ok(10L);
- expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r);
- replay(quotaEnforcer);
+ when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(r);
assertThat(quotaBackend.user(identifiedAdmin).availableTokens("testGroup"))
.isEqualTo(singletonAggregation(r));
}
@@ -181,56 +171,51 @@ public class DefaultQuotaBackendIT extends AbstractDaemonTest {
@Test
public void requestTokenError() throws Exception {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcer.requestTokens("testGroup", ctx, 1))
- .andReturn(QuotaResponse.error("failed"));
- replay(quotaEnforcer);
+ when(quotaEnforcer.requestTokens("testGroup", ctx, 1))
+ .thenReturn(QuotaResponse.error("failed"));
QuotaResponse.Aggregated result = quotaBackend.user(identifiedAdmin).requestToken("testGroup");
assertThat(result).isEqualTo(singletonAggregation(QuotaResponse.error("failed")));
- exception.expect(QuotaException.class);
- exception.expectMessage("failed");
- result.throwOnError();
+ QuotaException thrown = assertThrows(QuotaException.class, () -> result.throwOnError());
+ assertThat(thrown).hasMessageThat().contains("failed");
}
@Test
public void availableTokensError() throws Exception {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcer.availableTokens("testGroup", ctx))
- .andReturn(QuotaResponse.error("failed"));
- replay(quotaEnforcer);
+ when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.error("failed"));
QuotaResponse.Aggregated result =
quotaBackend.user(identifiedAdmin).availableTokens("testGroup");
assertThat(result).isEqualTo(singletonAggregation(QuotaResponse.error("failed")));
- exception.expect(QuotaException.class);
- exception.expectMessage("failed");
- result.throwOnError();
+ QuotaException thrown = assertThrows(QuotaException.class, () -> result.throwOnError());
+ assertThat(thrown).hasMessageThat().contains("failed");
}
@Test
public void requestTokenPluginThrowsAndRethrows() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andThrow(new NullPointerException());
- replay(quotaEnforcer);
+ when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenThrow(new NullPointerException());
- exception.expect(NullPointerException.class);
- quotaBackend.user(identifiedAdmin).requestToken("testGroup");
+ assertThrows(
+ NullPointerException.class,
+ () -> quotaBackend.user(identifiedAdmin).requestToken("testGroup"));
}
@Test
public void availableTokensPluginThrowsAndRethrows() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcer.availableTokens("testGroup", ctx)).andThrow(new NullPointerException());
- replay(quotaEnforcer);
+ when(quotaEnforcer.availableTokens("testGroup", ctx)).thenThrow(new NullPointerException());
- exception.expect(NullPointerException.class);
- quotaBackend.user(identifiedAdmin).availableTokens("testGroup");
+ assertThrows(
+ NullPointerException.class,
+ () -> quotaBackend.user(identifiedAdmin).availableTokens("testGroup"));
}
private Change.Id retrieveChangeId() throws Exception {
// use REST API so that repository size quota doesn't have to be stubbed
ChangeInfo changeInfo =
gApi.changes().create(new ChangeInput(project.get(), "master", "test")).get();
- return new Change.Id(changeInfo._number);
+ return Change.id(changeInfo._number);
}
private static QuotaResponse.Aggregated singletonAggregation(QuotaResponse response) {
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java b/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
index f5f11610c6..c6b09ccc5a 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
@@ -15,11 +15,12 @@
package com.google.gerrit.acceptance.server.quota;
import static com.google.common.truth.Truth.assertThat;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.resetToStrict;
-import static org.easymock.EasyMock.verify;
+import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -33,15 +34,12 @@ import com.google.gerrit.server.quota.QuotaResponse;
import com.google.inject.Inject;
import com.google.inject.Module;
import java.util.OptionalLong;
-import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
public class MultipleQuotaPluginsIT extends AbstractDaemonTest {
- private static final QuotaEnforcer quotaEnforcerA =
- EasyMock.createStrictMock(QuotaEnforcer.class);
- private static final QuotaEnforcer quotaEnforcerB =
- EasyMock.createStrictMock(QuotaEnforcer.class);
+ private static final QuotaEnforcer quotaEnforcerA = mock(QuotaEnforcer.class);
+ private static final QuotaEnforcer quotaEnforcerB = mock(QuotaEnforcer.class);
private IdentifiedUser identifiedAdmin;
@Inject private QuotaBackend quotaBackend;
@@ -65,93 +63,86 @@ public class MultipleQuotaPluginsIT extends AbstractDaemonTest {
@Before
public void setUp() {
identifiedAdmin = identifiedUserFactory.create(admin.id());
- resetToStrict(quotaEnforcerA);
- resetToStrict(quotaEnforcerB);
+ clearInvocations(quotaEnforcerA);
+ clearInvocations(quotaEnforcerB);
}
@Test
public void refillsOnError() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
- expect(quotaEnforcerB.requestTokens("testGroup", ctx, 1))
- .andReturn(QuotaResponse.error("fail"));
- quotaEnforcerA.refill("testGroup", ctx, 1);
- expectLastCall();
-
- replay(quotaEnforcerA);
- replay(quotaEnforcerB);
+ when(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok());
+ when(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.error("fail"));
assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup"))
.isEqualTo(
QuotaResponse.Aggregated.create(
ImmutableList.of(QuotaResponse.ok(), QuotaResponse.error("fail"))));
+
+ verify(quotaEnforcerA).requestTokens("testGroup", ctx, 1);
+ verify(quotaEnforcerB).requestTokens("testGroup", ctx, 1);
+ verify(quotaEnforcerA).refill("testGroup", ctx, 1);
}
@Test
public void refillsOnException() {
NullPointerException exception = new NullPointerException();
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).andThrow(exception);
- expect(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
- quotaEnforcerB.refill("testGroup", ctx, 1);
- expectLastCall();
-
- replay(quotaEnforcerA);
- replay(quotaEnforcerB);
-
- try {
- quotaBackend.user(identifiedAdmin).requestToken("testGroup");
- fail("expected a NullPointerException");
- } catch (NullPointerException e) {
- assertThat(exception).isEqualTo(e);
- }
-
- verify(quotaEnforcerA);
+ when(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok());
+ when(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).thenThrow(exception);
+
+ NullPointerException thrown =
+ assertThrows(
+ NullPointerException.class,
+ () -> quotaBackend.user(identifiedAdmin).requestToken("testGroup"));
+ assertThat(thrown).isEqualTo(exception);
+
+ verify(quotaEnforcerA).requestTokens("testGroup", ctx, 1);
+ verify(quotaEnforcerB).requestTokens("testGroup", ctx, 1);
+ verify(quotaEnforcerA).refill("testGroup", ctx, 1);
}
@Test
public void doesNotRefillNoOp() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcerA.requestTokens("testGroup", ctx, 1))
- .andReturn(QuotaResponse.error("fail"));
- expect(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.noOp());
-
- replay(quotaEnforcerA);
- replay(quotaEnforcerB);
+ when(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.error("fail"));
+ when(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.noOp());
assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup"))
.isEqualTo(
QuotaResponse.Aggregated.create(
ImmutableList.of(QuotaResponse.error("fail"), QuotaResponse.noOp())));
+
+ verify(quotaEnforcerA).requestTokens("testGroup", ctx, 1);
+ verify(quotaEnforcerB).requestTokens("testGroup", ctx, 1);
}
@Test
public void minimumAvailableTokens() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcerA.availableTokens("testGroup", ctx)).andReturn(QuotaResponse.ok(20L));
- expect(quotaEnforcerB.availableTokens("testGroup", ctx)).andReturn(QuotaResponse.ok(10L));
-
- replay(quotaEnforcerA);
- replay(quotaEnforcerB);
+ when(quotaEnforcerA.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.ok(20L));
+ when(quotaEnforcerB.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.ok(10L));
OptionalLong tokens =
quotaBackend.user(identifiedAdmin).availableTokens("testGroup").availableTokens();
- assertThat(tokens.isPresent()).isTrue();
+ assertThat(tokens).isPresent();
assertThat(tokens.getAsLong()).isEqualTo(10L);
+
+ verify(quotaEnforcerA).availableTokens("testGroup", ctx);
+ verify(quotaEnforcerB).availableTokens("testGroup", ctx);
}
@Test
public void ignoreNoOpForAvailableTokens() {
QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
- expect(quotaEnforcerA.availableTokens("testGroup", ctx)).andReturn(QuotaResponse.noOp());
- expect(quotaEnforcerB.availableTokens("testGroup", ctx)).andReturn(QuotaResponse.ok(20L));
-
- replay(quotaEnforcerA);
- replay(quotaEnforcerB);
+ when(quotaEnforcerA.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.noOp());
+ when(quotaEnforcerB.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.ok(20L));
OptionalLong tokens =
quotaBackend.user(identifiedAdmin).availableTokens("testGroup").availableTokens();
- assertThat(tokens.isPresent()).isTrue();
+ assertThat(tokens).isPresent();
assertThat(tokens.getAsLong()).isEqualTo(20L);
+
+ verify(quotaEnforcerA).availableTokens("testGroup", ctx);
+ verify(quotaEnforcerB).availableTokens("testGroup", ctx);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java b/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java
index 0814230e30..801288a06c 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java
@@ -15,15 +15,16 @@
package com.google.gerrit.acceptance.server.quota;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.server.quota.QuotaGroupDefinitions.REPOSITORY_SIZE_GROUP;
import static com.google.gerrit.server.quota.QuotaResponse.ok;
-import static org.easymock.EasyMock.anyLong;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.resetToStrict;
-import static org.easymock.EasyMock.verify;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.UseLocalDisk;
@@ -33,7 +34,6 @@ import com.google.gerrit.server.quota.QuotaBackend;
import com.google.gerrit.server.quota.QuotaResponse;
import com.google.inject.Module;
import java.util.Collections;
-import org.easymock.EasyMock;
import org.eclipse.jgit.api.errors.TooLargeObjectInPackException;
import org.eclipse.jgit.api.errors.TransportException;
import org.junit.Before;
@@ -42,9 +42,9 @@ import org.junit.Test;
@UseLocalDisk
public class RepositorySizeQuotaIT extends AbstractDaemonTest {
private static final QuotaBackend.WithResource quotaBackendWithResource =
- EasyMock.createStrictMock(QuotaBackend.WithResource.class);
+ mock(QuotaBackend.WithResource.class);
private static final QuotaBackend.WithUser quotaBackendWithUser =
- EasyMock.createStrictMock(QuotaBackend.WithUser.class);
+ mock(QuotaBackend.WithUser.class);
@Override
public Module createModule() {
@@ -70,64 +70,42 @@ public class RepositorySizeQuotaIT extends AbstractDaemonTest {
@Before
public void setUp() {
- resetToStrict(quotaBackendWithResource);
- resetToStrict(quotaBackendWithUser);
+ clearInvocations(quotaBackendWithResource);
+ clearInvocations(quotaBackendWithUser);
}
@Test
public void pushWithAvailableTokens() throws Exception {
- expect(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP))
- .andReturn(singletonAggregation(ok(276L)))
- .times(2);
- expect(quotaBackendWithResource.requestTokens(eq(REPOSITORY_SIZE_GROUP), anyLong()))
- .andReturn(singletonAggregation(ok()));
- expect(quotaBackendWithUser.project(project)).andReturn(quotaBackendWithResource).anyTimes();
- replay(quotaBackendWithResource);
- replay(quotaBackendWithUser);
+ when(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP))
+ .thenReturn(singletonAggregation(ok(276L)));
+ when(quotaBackendWithResource.requestTokens(eq(REPOSITORY_SIZE_GROUP), anyLong()))
+ .thenReturn(singletonAggregation(ok()));
+ when(quotaBackendWithUser.project(project)).thenReturn(quotaBackendWithResource);
pushCommit();
- verify(quotaBackendWithUser);
- verify(quotaBackendWithResource);
+ verify(quotaBackendWithResource, times(2)).availableTokens(REPOSITORY_SIZE_GROUP);
}
@Test
public void pushWithNotSufficientTokens() throws Exception {
long availableTokens = 1L;
- expect(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP))
- .andReturn(singletonAggregation(ok(availableTokens)))
- .anyTimes();
- expect(quotaBackendWithUser.project(project)).andReturn(quotaBackendWithResource).anyTimes();
- replay(quotaBackendWithResource);
- replay(quotaBackendWithUser);
- try {
- pushCommit();
- assert_().fail("expected TooLargeObjectInPackException");
- } catch (TooLargeObjectInPackException e) {
- String msg = e.getMessage();
- assertThat(msg).contains("Object too large");
- assertThat(msg)
- .contains(String.format("Max object size limit is %d bytes.", availableTokens));
- }
- verify(quotaBackendWithUser);
- verify(quotaBackendWithResource);
+ when(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP))
+ .thenReturn(singletonAggregation(ok(availableTokens)));
+ when(quotaBackendWithUser.project(project)).thenReturn(quotaBackendWithResource);
+ TooLargeObjectInPackException thrown =
+ assertThrows(TooLargeObjectInPackException.class, () -> pushCommit());
+ assertThat(thrown).hasMessageThat().contains("Object too large");
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(String.format("Max object size limit is %d bytes.", availableTokens));
}
@Test
public void errorGettingAvailableTokens() throws Exception {
String msg = "quota error";
- expect(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP))
- .andReturn(singletonAggregation(QuotaResponse.error(msg)))
- .anyTimes();
- expect(quotaBackendWithUser.project(project)).andReturn(quotaBackendWithResource).anyTimes();
- replay(quotaBackendWithResource);
- replay(quotaBackendWithUser);
- try {
- pushCommit();
- assert_().fail("expected TransportException");
- } catch (TransportException e) {
- // TransportException has not much info about the cause
- }
- verify(quotaBackendWithUser);
- verify(quotaBackendWithResource);
+ when(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP))
+ .thenReturn(singletonAggregation(QuotaResponse.error(msg)));
+ when(quotaBackendWithUser.project(project)).thenReturn(quotaBackendWithResource);
+ assertThrows(TransportException.class, () -> pushCommit());
}
private void pushCommit() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java b/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
index dc082feda9..c0aab382ec 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
@@ -14,30 +14,30 @@
package com.google.gerrit.acceptance.server.quota;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reset;
-import static org.easymock.EasyMock.verify;
+import static com.google.gerrit.httpd.restapi.RestApiServlet.SC_TOO_MANY_REQUESTS;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.quota.QuotaBackend;
import com.google.gerrit.server.quota.QuotaResponse;
import com.google.inject.Module;
import java.util.Collections;
-import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
public class RestApiQuotaIT extends AbstractDaemonTest {
private static final QuotaBackend.WithResource quotaBackendWithResource =
- EasyMock.createStrictMock(QuotaBackend.WithResource.class);
+ mock(QuotaBackend.WithResource.class);
private static final QuotaBackend.WithUser quotaBackendWithUser =
- EasyMock.createStrictMock(QuotaBackend.WithUser.class);
+ mock(QuotaBackend.WithUser.class);
@Override
public Module createModule() {
@@ -63,79 +63,72 @@ public class RestApiQuotaIT extends AbstractDaemonTest {
@Before
public void setUp() {
- reset(quotaBackendWithResource);
- reset(quotaBackendWithUser);
+ clearInvocations(quotaBackendWithResource);
+ clearInvocations(quotaBackendWithUser);
}
@Test
public void changeDetail() throws Exception {
Change.Id changeId = retrieveChangeId();
- expect(quotaBackendWithResource.requestToken("/restapi/changes/detail:GET"))
- .andReturn(singletonAggregation(QuotaResponse.ok()));
- replay(quotaBackendWithResource);
- expect(quotaBackendWithUser.change(changeId, project)).andReturn(quotaBackendWithResource);
- replay(quotaBackendWithUser);
+ when(quotaBackendWithResource.requestToken("/restapi/changes/detail:GET"))
+ .thenReturn(singletonAggregation(QuotaResponse.ok()));
+ when(quotaBackendWithUser.change(changeId, project)).thenReturn(quotaBackendWithResource);
adminRestSession.get("/changes/" + changeId + "/detail").assertOK();
- verify(quotaBackendWithUser);
- verify(quotaBackendWithResource);
+ verify(quotaBackendWithResource).requestToken("/restapi/changes/detail:GET");
+ verify(quotaBackendWithUser).change(changeId, project);
}
@Test
public void revisionDetail() throws Exception {
Change.Id changeId = retrieveChangeId();
- expect(quotaBackendWithResource.requestToken("/restapi/changes/revisions/actions:GET"))
- .andReturn(singletonAggregation(QuotaResponse.ok()));
- replay(quotaBackendWithResource);
- expect(quotaBackendWithUser.change(changeId, project)).andReturn(quotaBackendWithResource);
- replay(quotaBackendWithUser);
+ when(quotaBackendWithResource.requestToken("/restapi/changes/revisions/actions:GET"))
+ .thenReturn(singletonAggregation(QuotaResponse.ok()));
+ when(quotaBackendWithUser.change(changeId, project)).thenReturn(quotaBackendWithResource);
adminRestSession.get("/changes/" + changeId + "/revisions/current/actions").assertOK();
- verify(quotaBackendWithUser);
- verify(quotaBackendWithResource);
+ verify(quotaBackendWithResource).requestToken("/restapi/changes/revisions/actions:GET");
+ verify(quotaBackendWithUser).change(changeId, project);
}
@Test
public void createChangePost() throws Exception {
- expect(quotaBackendWithUser.requestToken("/restapi/changes:POST"))
- .andReturn(singletonAggregation(QuotaResponse.ok()));
- replay(quotaBackendWithUser);
+ when(quotaBackendWithUser.requestToken("/restapi/changes:POST"))
+ .thenReturn(singletonAggregation(QuotaResponse.ok()));
ChangeInput changeInput = new ChangeInput(project.get(), "master", "test");
adminRestSession.post("/changes/", changeInput).assertCreated();
- verify(quotaBackendWithUser);
+ verify(quotaBackendWithUser).requestToken("/restapi/changes:POST");
}
@Test
public void accountDetail() throws Exception {
- expect(quotaBackendWithResource.requestToken("/restapi/accounts/detail:GET"))
- .andReturn(singletonAggregation(QuotaResponse.ok()));
- replay(quotaBackendWithResource);
- expect(quotaBackendWithUser.account(admin.id())).andReturn(quotaBackendWithResource);
- replay(quotaBackendWithUser);
+ when(quotaBackendWithResource.requestToken("/restapi/accounts/detail:GET"))
+ .thenReturn(singletonAggregation(QuotaResponse.ok()));
+ when(quotaBackendWithUser.account(admin.id())).thenReturn(quotaBackendWithResource);
adminRestSession.get("/accounts/self/detail").assertOK();
- verify(quotaBackendWithUser);
- verify(quotaBackendWithResource);
+ verify(quotaBackendWithResource).requestToken("/restapi/accounts/detail:GET");
+ verify(quotaBackendWithUser).account(admin.id());
}
@Test
public void config() throws Exception {
- expect(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
- .andReturn(singletonAggregation(QuotaResponse.ok()));
- replay(quotaBackendWithUser);
+ when(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
+ .thenReturn(singletonAggregation(QuotaResponse.ok()));
adminRestSession.get("/config/server/version").assertOK();
+ verify(quotaBackendWithUser).requestToken("/restapi/config/version:GET");
}
@Test
public void outOfQuotaReturnsError() throws Exception {
- expect(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
- .andReturn(singletonAggregation(QuotaResponse.error("no quota")));
- replay(quotaBackendWithUser);
- adminRestSession.get("/config/server/version").assertStatus(429);
+ when(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
+ .thenReturn(singletonAggregation(QuotaResponse.error("no quota")));
+ adminRestSession.get("/config/server/version").assertStatus(SC_TOO_MANY_REQUESTS);
+ verify(quotaBackendWithUser).requestToken("/restapi/config/version:GET");
}
private Change.Id retrieveChangeId() throws Exception {
// use REST API so that repository size quota doesn't have to be stubbed
ChangeInfo changeInfo =
gApi.changes().create(new ChangeInput(project.get(), "master", "test")).get();
- return new Change.Id(changeInfo._number);
+ return Change.id(changeInfo._number);
}
private static QuotaResponse.Aggregated singletonAggregation(QuotaResponse response) {
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
index 83782c912e..1c820af5d6 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.server.rules;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
@@ -22,11 +23,10 @@ import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRequirement;
-import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
import com.google.inject.Inject;
-import java.util.Collection;
import java.util.Map;
+import java.util.Optional;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.junit.Test;
@@ -42,11 +42,10 @@ public class IgnoreSelfApprovalRuleIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
approve(r.getChangeId());
- Collection<SubmitRecord> submitRecords =
- rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+ Optional<SubmitRecord> submitRecord = rule.evaluate(r.getChange());
- assertThat(submitRecords).hasSize(1);
- SubmitRecord result = submitRecords.iterator().next();
+ assertThat(submitRecord).isPresent();
+ SubmitRecord result = submitRecord.get();
assertThat(result.status).isEqualTo(SubmitRecord.Status.NOT_READY);
assertThat(result.labels).isNotEmpty();
assertThat(result.requirements)
@@ -69,9 +68,8 @@ public class IgnoreSelfApprovalRuleIT extends AbstractDaemonTest {
// Approve as admin
approve(r.getChangeId());
- Collection<SubmitRecord> submitRecords =
- rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
- assertThat(submitRecords).isEmpty();
+ Optional<SubmitRecord> submitRecord = rule.evaluate(r.getChange());
+ assertThat(submitRecord).isEmpty();
}
@Test
@@ -81,9 +79,8 @@ public class IgnoreSelfApprovalRuleIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
approve(r.getChangeId());
- Collection<SubmitRecord> submitRecords =
- rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
- assertThat(submitRecords).isEmpty();
+ Optional<SubmitRecord> submitRecord = rule.evaluate(r.getChange());
+ assertThat(submitRecord).isEmpty();
}
private void enableRule(String labelName, boolean newState) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
index fa13be499b..92cc396e4f 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
@@ -20,9 +20,9 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.common.data.SubmitRecord;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.project.SubmitRuleOptions;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.rules.PrologOptions;
import com.google.gerrit.server.rules.PrologRuleEvaluator;
import com.google.gerrit.testing.TestChanges;
import com.google.inject.Inject;
@@ -30,8 +30,8 @@ import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.StructureTerm;
import com.googlecode.prolog_cafe.lang.Term;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
+import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
public class PrologRuleEvaluatorIT extends AbstractDaemonTest {
@@ -45,9 +45,9 @@ public class PrologRuleEvaluatorIT extends AbstractDaemonTest {
StructureTerm labels = new StructureTerm("label", verifiedLabel);
List<Term> terms = ImmutableList.of(makeTerm("ok", labels));
- Collection<SubmitRecord> records = evaluator.resultsToSubmitRecord(null, terms);
+ SubmitRecord record = evaluator.resultsToSubmitRecord(null, terms);
- assertThat(records).hasSize(1);
+ assertThat(record.status).isEqualTo(SubmitRecord.Status.OK);
}
/**
@@ -112,23 +112,16 @@ public class PrologRuleEvaluatorIT extends AbstractDaemonTest {
terms.add(makeTerm("not_ready", makeLabels(label3)));
// When
- List<SubmitRecord> records = evaluator.resultsToSubmitRecord(null, terms);
+ SubmitRecord record = evaluator.resultsToSubmitRecord(null, terms);
// assert that
- SubmitRecord record1Expected = new SubmitRecord();
- record1Expected.status = SubmitRecord.Status.OK;
- record1Expected.labels = new ArrayList<>();
- record1Expected.labels.add(submitRecordLabel2);
+ SubmitRecord expectedRecord = new SubmitRecord();
+ expectedRecord.status = SubmitRecord.Status.OK;
+ expectedRecord.labels = new ArrayList<>();
+ expectedRecord.labels.add(submitRecordLabel2);
+ expectedRecord.labels.add(submitRecordLabel3);
- SubmitRecord record2Expected = new SubmitRecord();
- record2Expected.status = SubmitRecord.Status.OK;
- record2Expected.labels = new ArrayList<>();
- record2Expected.labels.add(submitRecordLabel3);
-
- assertThat(records).hasSize(2);
-
- assertThat(records.get(0)).isEqualTo(record1Expected);
- assertThat(records.get(1)).isEqualTo(record2Expected);
+ assertThat(record).isEqualTo(expectedRecord);
}
private static Term makeTerm(String status, StructureTerm labels) {
@@ -149,12 +142,12 @@ public class PrologRuleEvaluatorIT extends AbstractDaemonTest {
}
private ChangeData makeChangeData() {
- ChangeData cd = ChangeData.createForTest(project, new Change.Id(1), 1);
+ ChangeData cd = ChangeData.createForTest(project, Change.id(1), 1, ObjectId.zeroId());
cd.setChange(TestChanges.newChange(project, admin.id()));
return cd;
}
private PrologRuleEvaluator makeEvaluator() {
- return evaluatorFactory.create(makeChangeData(), SubmitRuleOptions.defaults());
+ return evaluatorFactory.create(makeChangeData(), PrologOptions.defaultOptions());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
index 536cbbbd16..e5432d18f2 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
@@ -19,8 +19,9 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.SubmitRecord;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
@@ -40,6 +41,7 @@ public class RulesIT extends AbstractDaemonTest {
private static final String RULE_TEMPLATE =
"submit_rule(submit(W)) :- \n" + "%s,\n" + "W = label('OK', ok(user(1000000))).";
+ @Inject private ProjectOperations projectOperations;
@Inject private SubmitRuleEvaluator.Factory evaluatorFactory;
@Before
@@ -84,7 +86,7 @@ public class RulesIT extends AbstractDaemonTest {
}
private SubmitRecord.Status statusForRule() throws Exception {
- String oldHead = getRemoteHead().name();
+ String oldHead = projectOperations.project(project).getHead("master").name();
PushOneCommit.Result result1 =
pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
testRepo.reset(oldHead);
diff --git a/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java b/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
index 9fb7c2d8cb..4fe0df48f6 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
@@ -20,102 +20,91 @@ import static java.util.stream.Collectors.toList;
import com.google.common.base.Joiner;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.ChangeIndexedCounter;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.events.ChangeIndexedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import com.google.inject.Injector;
import java.util.List;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
@NoHttpd
@UseSsh
public abstract class AbstractIndexTests extends AbstractDaemonTest {
- @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
-
- private ChangeIndexedCounter changeIndexedCounter;
- private RegistrationHandle changeIndexedCounterHandle;
+ @Inject private ExtensionRegistry extensionRegistry;
/** @param injector injector */
public void configureIndex(Injector injector) {}
- @Before
- public void addChangeIndexedCounter() {
- changeIndexedCounter = new ChangeIndexedCounter();
- changeIndexedCounterHandle = changeIndexedListeners.add("gerrit", changeIndexedCounter);
- }
-
- @After
- public void removeChangeIndexedCounter() {
- if (changeIndexedCounterHandle != null) {
- changeIndexedCounterHandle.remove();
- }
- }
-
@Test
@GerritConfig(name = "index.autoReindexIfStale", value = "false")
public void indexChange() throws Exception {
- configureIndex(server.getTestInjector());
+ ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedCounter)) {
+ configureIndex(server.getTestInjector());
- PushOneCommit.Result change = createChange("first change", "test1.txt", "test1");
- String changeId = change.getChangeId();
- String changeLegacyId = change.getChange().getId().toString();
- ChangeInfo changeInfo = gApi.changes().id(changeId).get();
+ PushOneCommit.Result change = createChange("first change", "test1.txt", "test1");
+ String changeId = change.getChangeId();
+ String changeLegacyId = change.getChange().getId().toString();
+ ChangeInfo changeInfo = gApi.changes().id(changeId).get();
- disableChangeIndexWrites();
- amendChange(changeId, "second test", "test2.txt", "test2");
+ disableChangeIndexWrites();
+ amendChange(changeId, "second test", "test2.txt", "test2");
- assertChangeQuery(change.getChange(), false);
- enableChangeIndexWrites();
+ assertChangeQuery(change.getChange(), false);
+ enableChangeIndexWrites();
- changeIndexedCounter.clear();
- String cmd = Joiner.on(" ").join("gerrit", "index", "changes", changeLegacyId);
- adminSshSession.exec(cmd);
- adminSshSession.assertSuccess();
+ changeIndexedCounter.clear();
+ String cmd = Joiner.on(" ").join("gerrit", "index", "changes", changeLegacyId);
+ adminSshSession.exec(cmd);
+ adminSshSession.assertSuccess();
- changeIndexedCounter.assertReindexOf(changeInfo, 1);
+ changeIndexedCounter.assertReindexOf(changeInfo, 1);
- assertChangeQuery(change.getChange(), true);
+ assertChangeQuery(change.getChange(), true);
+ }
}
@Test
@GerritConfig(name = "index.autoReindexIfStale", value = "false")
public void indexProject() throws Exception {
- configureIndex(server.getTestInjector());
-
- PushOneCommit.Result change = createChange("first change", "test1.txt", "test1");
- String changeId = change.getChangeId();
- ChangeInfo changeInfo = gApi.changes().id(changeId).get();
+ ChangeIndexedCounter changeIndexedCounter = new ChangeIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(changeIndexedCounter)) {
+ configureIndex(server.getTestInjector());
- disableChangeIndexWrites();
- amendChange(changeId, "second test", "test2.txt", "test2");
+ PushOneCommit.Result change = createChange("first change", "test1.txt", "test1");
+ String changeId = change.getChangeId();
+ ChangeInfo changeInfo = gApi.changes().id(changeId).get();
- assertChangeQuery(change.getChange(), false);
- enableChangeIndexWrites();
+ disableChangeIndexWrites();
+ amendChange(changeId, "second test", "test2.txt", "test2");
- changeIndexedCounter.clear();
- String cmd = Joiner.on(" ").join("gerrit", "index", "changes-in-project", project.get());
- adminSshSession.exec(cmd);
- adminSshSession.assertSuccess();
+ assertChangeQuery(change.getChange(), false);
+ enableChangeIndexWrites();
- boolean indexing = true;
- while (indexing) {
- String out = adminSshSession.exec("gerrit show-queue --wide");
+ changeIndexedCounter.clear();
+ String cmd = Joiner.on(" ").join("gerrit", "index", "changes-in-project", project.get());
+ adminSshSession.exec(cmd);
adminSshSession.assertSuccess();
- indexing = out.contains("Index all changes of project " + project.get());
- }
- changeIndexedCounter.assertReindexOf(changeInfo, 1);
+ boolean indexing = true;
+ while (indexing) {
+ String out = adminSshSession.exec("gerrit show-queue --wide");
+ adminSshSession.assertSuccess();
+ indexing = out.contains("Index all changes of project " + project.get());
+ }
+
+ changeIndexedCounter.assertReindexOf(changeInfo, 1);
- assertChangeQuery(change.getChange(), true);
+ assertChangeQuery(change.getChange(), true);
+ }
}
private void assertChangeQuery(ChangeData change, boolean assertTrue) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
index 0ce05b0d01..39dbaa7e07 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
@@ -18,7 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.UseSsh;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.project.ProjectState;
import org.junit.Test;
@@ -33,7 +33,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
adminSshSession.exec(
"gerrit create-project --branch master --owner " + newGroupName + " " + newProjectName);
adminSshSession.assertSuccess();
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNotNull();
}
@@ -46,7 +46,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
adminSshSession.exec(
"gerrit create-project --branch master --owner " + wrongGroupName + " " + newProjectName);
adminSshSession.assertFailure();
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNull();
}
@@ -62,7 +62,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
+ newProjectName
+ ".git");
adminSshSession.assertSuccess();
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNotNull();
assertThat(projectState.getName()).isEqualTo(newProjectName);
}
@@ -79,7 +79,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
+ newProjectName
+ "/");
adminSshSession.assertSuccess();
- ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ ProjectState projectState = projectCache.get(Project.nameKey(newProjectName));
assertThat(projectState).isNotNull();
assertThat(projectState.getName()).isEqualTo(newProjectName);
}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/CustomIndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/CustomIndexIT.java
new file mode 100644
index 0000000000..434071ff9c
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/ssh/CustomIndexIT.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.ssh;
+
+import com.google.gerrit.index.IndexType;
+import com.google.gerrit.testing.ConfigSuite;
+import org.eclipse.jgit.lib.Config;
+
+/**
+ * Tests for a defaulted custom index configuration. This unknown type is the opposite of {@link
+ * IndexType#getKnownTypes()}.
+ */
+public class CustomIndexIT extends AbstractIndexTests {
+
+ @ConfigSuite.Default
+ public static Config customIndexType() {
+ Config config = new Config();
+ config.setString("index", null, "type", "custom");
+ return config;
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
index 339e8aed54..43a5cebf1b 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
@@ -18,10 +18,12 @@ import static com.google.gerrit.elasticsearch.ElasticTestUtils.createAllIndexes;
import static com.google.gerrit.elasticsearch.ElasticTestUtils.getConfig;
import com.google.gerrit.elasticsearch.ElasticVersion;
+import com.google.gerrit.index.IndexType;
import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Injector;
import org.eclipse.jgit.lib.Config;
+/** Tests for every supported {@link IndexType#isElasticsearch()} most recent index version. */
public class ElasticIndexIT extends AbstractIndexTests {
@ConfigSuite.Default
diff --git a/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java b/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
index c23f889dce..9dcfa3f622 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
@@ -23,7 +23,7 @@ import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.GarbageCollectionResult;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.GarbageCollection;
import com.google.gerrit.server.git.GarbageCollectionQueue;
import com.google.inject.Inject;
diff --git a/javatests/com/google/gerrit/acceptance/ssh/IndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/IndexIT.java
index 370fb72e4b..7062b00f33 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/IndexIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/IndexIT.java
@@ -14,4 +14,7 @@
package com.google.gerrit.acceptance.ssh;
+import com.google.gerrit.index.IndexType;
+
+/** Tests for the default {@link IndexType#isLucene()} index configuration. */
public class IndexIT extends AbstractIndexTests {}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/PluginChangeFieldsIT.java b/javatests/com/google/gerrit/acceptance/ssh/PluginChangeFieldsIT.java
index e61e2cc407..4bf7c19d3a 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/PluginChangeFieldsIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/PluginChangeFieldsIT.java
@@ -22,7 +22,7 @@ import com.google.common.io.CharStreams;
import com.google.gerrit.acceptance.AbstractPluginFieldsTest;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.query.change.OutputStreamQuery;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
diff --git a/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java b/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
index 0f47a4ab2c..78960bb329 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.ssh;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -294,8 +295,8 @@ public class QueryIT extends AbstractDaemonTest {
// computation while formatting the output, such as labels, reviewers etc.
merge(r);
for (ListChangesOption option : ListChangesOption.values()) {
- assertThat(gApi.changes().query(r.getChangeId()).withOption(option).get())
- .named("Option: " + option)
+ assertWithMessage("Option: " + option)
+ .that(gApi.changes().query(r.getChangeId()).withOption(option).get())
.hasSize(1);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java b/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
index 6998a0ada9..5a31bfda2a 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
@@ -22,7 +22,7 @@ import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.SshSession;
import com.google.gerrit.acceptance.UseSsh;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.testing.ConfigSuite;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
index 4e9c4a49e9..012e9980d0 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
@@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;
@@ -29,6 +30,9 @@ import com.google.gerrit.acceptance.UseSsh;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Spliterator;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
import org.junit.Test;
@NoHttpd
@@ -162,6 +166,38 @@ public class SshCommandsIT extends AbstractDaemonTest {
assertThat(commands).containsExactlyElementsIn(SLAVE_COMMANDS.get("gerrit")).inOrder();
}
+ @Test
+ @Sandboxed
+ public void showConnections() throws Exception {
+ Spliterator<String> connectionsOutput =
+ getOutputLines(adminSshSession.exec("gerrit show-connections"));
+
+ assertThat(findConnectionsInOutput(connectionsOutput, "user")).hasSize(1);
+ }
+
+ @Test
+ @Sandboxed
+ public void cloeConnections() throws Exception {
+ List<String> connectionsOutput =
+ findConnectionsInOutput(
+ getOutputLines(adminSshSession.exec("gerrit show-connections")), "user");
+ String connectionId =
+ Splitter.on(" ")
+ .trimResults()
+ .omitEmptyStrings()
+ .split(connectionsOutput.get(0))
+ .iterator()
+ .next();
+
+ String closeConnectionOutput = adminSshSession.exec("gerrit close-connection " + connectionId);
+ assertThat(closeConnectionOutput).contains(connectionId);
+
+ assertThat(
+ findConnectionsInOutput(
+ getOutputLines(adminSshSession.exec("gerrit show-connections")), "user"))
+ .isEmpty();
+ }
+
private List<String> parseCommandsFromGerritHelpText(String helpText) {
List<String> commands = new ArrayList<>();
@@ -194,4 +230,16 @@ public class SshCommandsIT extends AbstractDaemonTest {
return commands;
}
+
+ private List<String> findConnectionsInOutput(Spliterator<String> connectionsOutput, String user) {
+ List<String> connections =
+ StreamSupport.stream(connectionsOutput, false)
+ .filter(s -> s.contains("localhost") && s.contains(user))
+ .collect(Collectors.toList());
+ return connections;
+ }
+
+ private Spliterator<String> getOutputLines(String output) throws Exception {
+ return Splitter.on("\n").trimResults().omitEmptyStrings().split(output).spliterator();
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
index 9c1e23d0a8..ae45d90338 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
@@ -16,79 +16,94 @@ package com.google.gerrit.acceptance.ssh;
import static com.google.common.truth.Truth.assertThat;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.ExtensionRegistry;
+import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
import com.google.gerrit.acceptance.UseSsh;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.PerformanceLogger;
import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.project.CreateProjectArgs;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
-import org.junit.After;
-import org.junit.Before;
+import java.util.ArrayList;
+import java.util.List;
import org.junit.Test;
@UseSsh
public class SshTraceIT extends AbstractDaemonTest {
- @Inject private DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
-
- private TraceValidatingProjectCreationValidationListener projectCreationListener;
- private RegistrationHandle projectCreationListenerRegistrationHandle;
-
- @Before
- public void setup() {
- projectCreationListener = new TraceValidatingProjectCreationValidationListener();
- projectCreationListenerRegistrationHandle =
- projectCreationValidationListeners.add("gerrit", projectCreationListener);
- }
-
- @After
- public void cleanup() {
- projectCreationListenerRegistrationHandle.remove();
- }
+ @Inject private ExtensionRegistry extensionRegistry;
@Test
public void sshCallWithoutTrace() throws Exception {
- adminSshSession.exec("gerrit create-project new1");
- adminSshSession.assertSuccess();
- assertThat(projectCreationListener.traceId).isNull();
- assertThat(projectCreationListener.foundTraceId).isFalse();
- assertThat(projectCreationListener.isLoggingForced).isFalse();
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ adminSshSession.exec("gerrit create-project new1");
+ adminSshSession.assertSuccess();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.foundTraceId).isFalse();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+ }
}
@Test
public void sshCallWithTrace() throws Exception {
- adminSshSession.exec("gerrit create-project --trace new2");
-
- // The trace ID is written to stderr.
- adminSshSession.assertFailure(RequestId.Type.TRACE_ID.name());
-
- assertThat(projectCreationListener.traceId).isNotNull();
- assertThat(projectCreationListener.foundTraceId).isTrue();
- assertThat(projectCreationListener.isLoggingForced).isTrue();
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ adminSshSession.exec("gerrit create-project --trace new2");
+
+ // The trace ID is written to stderr.
+ adminSshSession.assertFailure(RequestId.Type.TRACE_ID.name());
+
+ assertThat(projectCreationListener.traceId).isNotNull();
+ assertThat(projectCreationListener.foundTraceId).isTrue();
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ }
}
@Test
public void sshCallWithTraceAndProvidedTraceId() throws Exception {
- adminSshSession.exec("gerrit create-project --trace --trace-id issue/123 new3");
-
- // The trace ID is written to stderr.
- adminSshSession.assertFailure(RequestId.Type.TRACE_ID.name());
-
- assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
- assertThat(projectCreationListener.foundTraceId).isTrue();
- assertThat(projectCreationListener.isLoggingForced).isTrue();
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ adminSshSession.exec("gerrit create-project --trace --trace-id issue/123 new3");
+
+ // The trace ID is written to stderr.
+ adminSshSession.assertFailure(RequestId.Type.TRACE_ID.name());
+
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.foundTraceId).isTrue();
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ }
}
@Test
public void sshCallWithTraceIdAndWithoutTraceFails() throws Exception {
- adminSshSession.exec("gerrit create-project --trace-id issue/123 new3");
+ adminSshSession.exec("gerrit create-project --trace-id issue/123 new4");
adminSshSession.assertFailure("A trace ID can only be set if --trace was specified.");
}
+ @Test
+ public void performanceLoggingForSshCall() throws Exception {
+ TestPerformanceLogger testPerformanceLogger = new TestPerformanceLogger();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(testPerformanceLogger)) {
+ adminSshSession.exec("gerrit create-project new5");
+ adminSshSession.assertSuccess();
+ assertThat(testPerformanceLogger.logEntries()).isNotEmpty();
+ }
+ }
+
private static class TraceValidatingProjectCreationValidationListener
implements ProjectCreationValidationListener {
String traceId;
@@ -103,4 +118,28 @@ public class SshTraceIT extends AbstractDaemonTest {
this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
}
}
+
+ private static class TestPerformanceLogger implements PerformanceLogger {
+ private List<PerformanceLogEntry> logEntries = new ArrayList<>();
+
+ @Override
+ public void log(String operation, long durationMs, Metadata metadata) {
+ logEntries.add(PerformanceLogEntry.create(operation, metadata));
+ }
+
+ ImmutableList<PerformanceLogEntry> logEntries() {
+ return ImmutableList.copyOf(logEntries);
+ }
+ }
+
+ @AutoValue
+ abstract static class PerformanceLogEntry {
+ static PerformanceLogEntry create(String operation, Metadata metadata) {
+ return new AutoValue_SshTraceIT_PerformanceLogEntry(operation, metadata);
+ }
+
+ abstract String operation();
+
+ abstract Metadata metadata();
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
index e0d6593fb3..96864d93db 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
@@ -15,19 +15,20 @@
package com.google.gerrit.acceptance.testsuite.group;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.google.common.truth.Correspondence;
-import com.google.common.truth.Correspondence.BinaryPredicate;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.inject.Inject;
import java.sql.Timestamp;
import java.util.Objects;
@@ -217,7 +218,7 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void notExistingGroupCanBeCheckedForExistence() throws Exception {
- AccountGroup.UUID notExistingGroupUuid = new AccountGroup.UUID("not-existing-group");
+ AccountGroup.UUID notExistingGroupUuid = AccountGroup.uuid("not-existing-group");
boolean exists = groupOperations.group(notExistingGroupUuid).exists();
@@ -226,10 +227,9 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void retrievingNotExistingGroupFails() throws Exception {
- AccountGroup.UUID notExistingGroupUuid = new AccountGroup.UUID("not-existing-group");
-
- exception.expect(IllegalStateException.class);
- groupOperations.group(notExistingGroupUuid).get();
+ AccountGroup.UUID notExistingGroupUuid = AccountGroup.uuid("not-existing-group");
+ assertThrows(
+ IllegalStateException.class, () -> groupOperations.group(notExistingGroupUuid).get());
}
@Test
@@ -270,7 +270,7 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
AccountGroup.NameKey groupName = groupOperations.group(groupUuid).get().nameKey();
- assertThat(groupName).isEqualTo(new AccountGroup.NameKey("ABC-789-this-name-must-be-unique"));
+ assertThat(groupName).isEqualTo(AccountGroup.nameKey("ABC-789-this-name-must-be-unique"));
}
@Test
@@ -297,7 +297,7 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void ownerGroupUuidOfExistingGroupCanBeRetrieved() throws Exception {
- AccountGroup.UUID originalOwnerGroupUuid = new AccountGroup.UUID("owner group");
+ AccountGroup.UUID originalOwnerGroupUuid = AccountGroup.uuid("owner group");
AccountGroup.UUID groupUuid =
groupOperations.newGroup().ownerGroupUuid(originalOwnerGroupUuid).create();
@@ -314,14 +314,16 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
TestGroup visibleGroup = groupOperations.group(visibleGroupUuid).get();
TestGroup invisibleGroup = groupOperations.group(invisibleGroupUuid).get();
- assertThat(visibleGroup.visibleToAll()).named("visibility of visible group").isTrue();
- assertThat(invisibleGroup.visibleToAll()).named("visibility of invisible group").isFalse();
+ assertWithMessage("visibility of visible group").that(visibleGroup.visibleToAll()).isTrue();
+ assertWithMessage("visibility of invisible group")
+ .that(invisibleGroup.visibleToAll())
+ .isFalse();
}
@Test
public void createdOnOfExistingGroupCanBeRetrieved() throws Exception {
GroupInfo group = gApi.groups().create(createArbitraryGroupInput()).detail();
- AccountGroup.UUID groupUuid = new AccountGroup.UUID(group.id);
+ AccountGroup.UUID groupUuid = AccountGroup.uuid(group.id);
Timestamp createdOn = groupOperations.group(groupUuid).get().createdOn();
@@ -330,9 +332,9 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void membersOfExistingGroupCanBeRetrieved() throws Exception {
- Account.Id memberId1 = new Account.Id(1000);
- Account.Id memberId2 = new Account.Id(2000);
- Account.Id memberId3 = new Account.Id(3000);
+ Account.Id memberId1 = Account.id(1000);
+ Account.Id memberId2 = Account.id(2000);
+ Account.Id memberId3 = Account.id(3000);
AccountGroup.UUID groupUuid =
groupOperations.newGroup().members(memberId1, memberId2, memberId3).create();
@@ -352,9 +354,9 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void subgroupsOfExistingGroupCanBeRetrieved() throws Exception {
- AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
- AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
- AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ AccountGroup.UUID subgroupUuid1 = AccountGroup.uuid("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = AccountGroup.uuid("subgroup 2");
+ AccountGroup.UUID subgroupUuid3 = AccountGroup.uuid("subgroup 3");
AccountGroup.UUID groupUuid =
groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2, subgroupUuid3).create();
@@ -428,11 +430,11 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void ownerGroupUuidCanBeUpdated() throws Exception {
- AccountGroup.UUID originalOwnerGroupUuid = new AccountGroup.UUID("original owner");
+ AccountGroup.UUID originalOwnerGroupUuid = AccountGroup.uuid("original owner");
AccountGroup.UUID groupUuid =
groupOperations.newGroup().ownerGroupUuid(originalOwnerGroupUuid).create();
- AccountGroup.UUID updatedOwnerGroupUuid = new AccountGroup.UUID("updated owner");
+ AccountGroup.UUID updatedOwnerGroupUuid = AccountGroup.uuid("updated owner");
groupOperations.group(groupUuid).forUpdate().ownerGroupUuid(updatedOwnerGroupUuid).update();
AccountGroup.UUID currentOwnerGroupUuid =
@@ -454,8 +456,8 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
public void membersCanBeAdded() throws Exception {
AccountGroup.UUID groupUuid = groupOperations.newGroup().clearMembers().create();
- Account.Id memberId1 = new Account.Id(1000);
- Account.Id memberId2 = new Account.Id(2000);
+ Account.Id memberId1 = Account.id(1000);
+ Account.Id memberId2 = Account.id(2000);
groupOperations.group(groupUuid).forUpdate().addMember(memberId1).addMember(memberId2).update();
ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
@@ -464,8 +466,8 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void membersCanBeRemoved() throws Exception {
- Account.Id memberId1 = new Account.Id(1000);
- Account.Id memberId2 = new Account.Id(2000);
+ Account.Id memberId1 = Account.id(1000);
+ Account.Id memberId2 = Account.id(2000);
AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
groupOperations.group(groupUuid).forUpdate().removeMember(memberId2).update();
@@ -476,11 +478,11 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void memberAdditionAndRemovalCanBeMixed() throws Exception {
- Account.Id memberId1 = new Account.Id(1000);
- Account.Id memberId2 = new Account.Id(2000);
+ Account.Id memberId1 = Account.id(1000);
+ Account.Id memberId2 = Account.id(2000);
AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
- Account.Id memberId3 = new Account.Id(3000);
+ Account.Id memberId3 = Account.id(3000);
groupOperations
.group(groupUuid)
.forUpdate()
@@ -494,8 +496,8 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void membersCanBeCleared() throws Exception {
- Account.Id memberId1 = new Account.Id(1000);
- Account.Id memberId2 = new Account.Id(2000);
+ Account.Id memberId1 = Account.id(1000);
+ Account.Id memberId2 = Account.id(2000);
AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
groupOperations.group(groupUuid).forUpdate().clearMembers().update();
@@ -506,11 +508,11 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void furtherMembersCanBeAddedAfterClearingAll() throws Exception {
- Account.Id memberId1 = new Account.Id(1000);
- Account.Id memberId2 = new Account.Id(2000);
+ Account.Id memberId1 = Account.id(1000);
+ Account.Id memberId2 = Account.id(2000);
AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
- Account.Id memberId3 = new Account.Id(3000);
+ Account.Id memberId3 = Account.id(3000);
groupOperations.group(groupUuid).forUpdate().clearMembers().addMember(memberId3).update();
ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
@@ -521,8 +523,8 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
public void subgroupsCanBeAdded() throws Exception {
AccountGroup.UUID groupUuid = groupOperations.newGroup().clearSubgroups().create();
- AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
- AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID subgroupUuid1 = AccountGroup.uuid("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = AccountGroup.uuid("subgroup 2");
groupOperations
.group(groupUuid)
.forUpdate()
@@ -536,8 +538,8 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void subgroupsCanBeRemoved() throws Exception {
- AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
- AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID subgroupUuid1 = AccountGroup.uuid("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = AccountGroup.uuid("subgroup 2");
AccountGroup.UUID groupUuid =
groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
@@ -549,12 +551,12 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void subgroupAdditionAndRemovalCanBeMixed() throws Exception {
- AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
- AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID subgroupUuid1 = AccountGroup.uuid("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = AccountGroup.uuid("subgroup 2");
AccountGroup.UUID groupUuid =
groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
- AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ AccountGroup.UUID subgroupUuid3 = AccountGroup.uuid("subgroup 3");
groupOperations
.group(groupUuid)
.forUpdate()
@@ -568,8 +570,8 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void subgroupsCanBeCleared() throws Exception {
- AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
- AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID subgroupUuid1 = AccountGroup.uuid("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = AccountGroup.uuid("subgroup 2");
AccountGroup.UUID groupUuid =
groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
@@ -581,12 +583,12 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
@Test
public void furtherSubgroupsCanBeAddedAfterClearingAll() throws Exception {
- AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
- AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID subgroupUuid1 = AccountGroup.uuid("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = AccountGroup.uuid("subgroup 2");
AccountGroup.UUID groupUuid =
groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
- AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ AccountGroup.UUID subgroupUuid3 = AccountGroup.uuid("subgroup 3");
groupOperations
.group(groupUuid)
.forUpdate()
@@ -610,37 +612,31 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
private AccountGroup.UUID createGroupInServer(GroupInput input) throws RestApiException {
GroupInfo group = gApi.groups().create(input).detail();
- return new AccountGroup.UUID(group.id);
+ return AccountGroup.uuid(group.id);
}
private static Correspondence<AccountInfo, Account.Id> getAccountToIdCorrespondence() {
return Correspondence.from(
- new BinaryPredicate<AccountInfo, Account.Id>() {
- @Override
- public boolean apply(AccountInfo actualAccount, Account.Id expectedId) {
- Account.Id accountId =
- Optional.ofNullable(actualAccount)
- .map(account -> account._accountId)
- .map(Account.Id::new)
- .orElse(null);
- return Objects.equals(accountId, expectedId);
- }
+ (actualAccount, expectedId) -> {
+ Account.Id accountId =
+ Optional.ofNullable(actualAccount)
+ .map(account -> account._accountId)
+ .map(Account::id)
+ .orElse(null);
+ return Objects.equals(accountId, expectedId);
},
"has ID");
}
private static Correspondence<GroupInfo, AccountGroup.UUID> getGroupToUuidCorrespondence() {
return Correspondence.from(
- new BinaryPredicate<GroupInfo, AccountGroup.UUID>() {
- @Override
- public boolean apply(GroupInfo actualGroup, AccountGroup.UUID expectedUuid) {
- AccountGroup.UUID groupUuid =
- Optional.ofNullable(actualGroup)
- .map(group -> group.id)
- .map(AccountGroup.UUID::new)
- .orElse(null);
- return Objects.equals(groupUuid, expectedUuid);
- }
+ (actualGroup, expectedUuid) -> {
+ AccountGroup.UUID groupUuid =
+ Optional.ofNullable(actualGroup)
+ .map(group -> group.id)
+ .map(AccountGroup::uuid)
+ .orElse(null);
+ return Objects.equals(groupUuid, expectedUuid);
},
"has UUID");
}
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
index 3f537c077c..c7a8eb6eaa 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
@@ -15,14 +15,41 @@
package com.google.gerrit.acceptance.testsuite.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.blockLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.capabilityKey;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.labelPermissionKey;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
+import static com.google.gerrit.common.data.GlobalCapability.ADMINISTRATE_SERVER;
+import static com.google.gerrit.common.data.GlobalCapability.DEFAULT_MAX_QUERY_LIMIT;
+import static com.google.gerrit.common.data.GlobalCapability.QUERY_LIMIT;
+import static com.google.gerrit.entities.RefNames.REFS_CONFIG;
+import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.truth.ConfigSubject.assertThat;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestPermission;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.projects.BranchInfo;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.extensions.api.projects.ConfigInput;
+import com.google.gerrit.server.project.ProjectConfig;
+import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
import java.util.List;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
import org.junit.Test;
public class ProjectOperationsImplTest extends AbstractDaemonTest {
@@ -48,9 +75,517 @@ public class ProjectOperationsImplTest extends AbstractDaemonTest {
@Test
public void emptyCommit() throws Exception {
Project.NameKey key = projectOperations.newProject().create();
+
List<BranchInfo> branches = gApi.projects().name(key.get()).branches().get();
assertThat(branches).isNotEmpty();
assertThat(branches.stream().map(x -> x.ref).collect(toList()))
.isEqualTo(ImmutableList.of("HEAD", "refs/meta/config", "refs/heads/master"));
}
+
+ @Test
+ public void getProjectConfig() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ assertThat(projectOperations.project(key).getProjectConfig().getProject().getDescription())
+ .isEmpty();
+
+ ConfigInput input = new ConfigInput();
+ input.description = "my fancy project";
+ gApi.projects().name(key.get()).config(input);
+
+ assertThat(projectOperations.project(key).getProjectConfig().getProject().getDescription())
+ .isEqualTo("my fancy project");
+ }
+
+ @Test
+ public void mutatingResultOfGetProjectConfigDoesNotMutateGlobalCachedValue() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ ProjectConfig projectConfig = projectOperations.project(key).getProjectConfig();
+ ProjectState cachedProjectState1 = projectCache.checkedGet(key);
+ ProjectConfig cachedProjectConfig1 = cachedProjectState1.getConfig();
+ assertThat(cachedProjectConfig1).isNotSameInstanceAs(projectConfig);
+ assertThat(cachedProjectConfig1.getProject().getDescription()).isEmpty();
+ assertThat(projectConfig.getProject().getDescription()).isEmpty();
+ projectConfig.getProject().setDescription("my fancy project");
+
+ ProjectConfig cachedProjectConfig2 = projectCache.checkedGet(key).getConfig();
+ assertThat(cachedProjectConfig2).isNotSameInstanceAs(projectConfig);
+ assertThat(cachedProjectConfig2.getProject().getDescription()).isEmpty();
+ }
+
+ @Test
+ public void getProjectConfigNoRefsMetaConfig() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ deleteRefsMetaConfig(key);
+
+ ProjectConfig projectConfig = projectOperations.project(key).getProjectConfig();
+ assertThat(projectConfig.getName()).isEqualTo(key);
+ assertThat(projectConfig.getRevision()).isNull();
+ }
+
+ @Test
+ public void getConfig() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).isNotInstanceOf(StoredConfig.class);
+ assertThat(config).text().isEmpty();
+
+ ConfigInput input = new ConfigInput();
+ input.description = "my fancy project";
+ gApi.projects().name(key.get()).config(input);
+
+ config = projectOperations.project(key).getConfig();
+ assertThat(config).isNotInstanceOf(StoredConfig.class);
+ assertThat(config).sections().containsExactly("project");
+ assertThat(config).subsections("project").isEmpty();
+ assertThat(config).sectionValues("project").containsExactly("description", "my fancy project");
+ }
+
+ @Test
+ public void getConfigNoRefsMetaConfig() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ deleteRefsMetaConfig(key);
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).isNotInstanceOf(StoredConfig.class);
+ assertThat(config).isEmpty();
+ }
+
+ @Test
+ public void addAllowPermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allow(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS))
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("abandon", "group global:Registered-Users");
+ }
+
+ @Test
+ public void addDenyPermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(deny(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS))
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("abandon", "deny group global:Registered-Users");
+ }
+
+ @Test
+ public void addBlockPermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(block(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS))
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("abandon", "block group global:Registered-Users");
+ }
+
+ @Test
+ public void addAllowForcePermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allow(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS).force(true))
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("abandon", "+force group global:Registered-Users");
+ }
+
+ @Test
+ public void updateExclusivePermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allow(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS))
+ .setExclusiveGroup(permissionKey(Permission.ABANDON).ref("refs/foo"), true)
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly(
+ "abandon", "group global:Registered-Users",
+ "exclusiveGroupPermissions", "abandon");
+
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .setExclusiveGroup(permissionKey(Permission.ABANDON).ref("refs/foo"), false)
+ .update();
+
+ config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("abandon", "group global:Registered-Users");
+ }
+
+ @Test
+ public void addMultipleExclusivePermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .setExclusiveGroup(permissionKey(Permission.ABANDON).ref("refs/foo"), true)
+ .setExclusiveGroup(permissionKey(Permission.CREATE).ref("refs/foo"), true)
+ .update();
+ assertThat(projectOperations.project(key).getConfig())
+ .subsectionValues("access", "refs/foo")
+ .containsEntry("exclusiveGroupPermissions", "abandon create");
+
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .setExclusiveGroup(permissionKey(Permission.ABANDON).ref("refs/foo"), false)
+ .update();
+ assertThat(projectOperations.project(key).getConfig())
+ .subsectionValues("access", "refs/foo")
+ .containsEntry("exclusiveGroupPermissions", "create");
+ }
+
+ @Test
+ public void addMultiplePermissions() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allow(Permission.ABANDON).ref("refs/foo").group(PROJECT_OWNERS))
+ .add(allow(Permission.CREATE).ref("refs/foo").group(REGISTERED_USERS))
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly(
+ "abandon", "group global:Project-Owners",
+ "create", "group global:Registered-Users");
+ }
+
+ @Test
+ public void addDuplicatePermissions() throws Exception {
+ TestPermission permission =
+ TestProjectUpdate.allow(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS).build();
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations.project(key).forUpdate().add(permission).add(permission).update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly(
+ "abandon", "group global:Registered-Users",
+ "abandon", "group global:Registered-Users");
+
+ projectOperations.project(key).forUpdate().add(permission).update();
+ config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly(
+ "abandon", "group global:Registered-Users",
+ "abandon", "group global:Registered-Users",
+ "abandon", "group global:Registered-Users");
+ }
+
+ @Test
+ public void addAllowLabelPermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/foo").group(REGISTERED_USERS).range(-1, 2))
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("label-Code-Review", "-1..+2 group global:Registered-Users");
+ }
+
+ @Test
+ public void addBlockLabelPermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/foo").group(REGISTERED_USERS).range(-1, 2))
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("label-Code-Review", "block -1..+2 group global:Registered-Users");
+ }
+
+ @Test
+ public void addAllowExclusiveLabelPermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/foo").group(REGISTERED_USERS).range(-1, 2))
+ .setExclusiveGroup(labelPermissionKey("Code-Review").ref("refs/foo"), true)
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly(
+ "label-Code-Review", "-1..+2 group global:Registered-Users",
+ "exclusiveGroupPermissions", "label-Code-Review");
+
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .setExclusiveGroup(labelPermissionKey("Code-Review").ref("refs/foo"), false)
+ .update();
+
+ config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("label-Code-Review", "-1..+2 group global:Registered-Users");
+ }
+
+ @Test
+ public void addAllowLabelAsPermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(
+ allowLabel("Code-Review")
+ .ref("refs/foo")
+ .group(REGISTERED_USERS)
+ .range(-1, 2)
+ .impersonation(true))
+ .update();
+
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).sections().containsExactly("access");
+ assertThat(config).subsections("access").containsExactly("refs/foo");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("labelAs-Code-Review", "-1..+2 group global:Registered-Users");
+ }
+
+ @Test
+ public void addAllowCapability() throws Exception {
+ Config config = projectOperations.project(allProjects).getConfig();
+ assertThat(config)
+ .sectionValues("capability")
+ .doesNotContainEntry("administrateServer", "group Registered Users");
+
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(ADMINISTRATE_SERVER).group(REGISTERED_USERS))
+ .update();
+
+ assertThat(projectOperations.project(allProjects).getConfig())
+ .sectionValues("capability")
+ .containsEntry("administrateServer", "group Registered Users");
+ }
+
+ @Test
+ public void addAllowCapabilityWithRange() throws Exception {
+ Config config = projectOperations.project(allProjects).getConfig();
+ assertThat(config).sectionValues("capability").doesNotContainKey("queryLimit");
+
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(QUERY_LIMIT).group(REGISTERED_USERS).range(0, 5000))
+ .update();
+
+ assertThat(projectOperations.project(allProjects).getConfig())
+ .sectionValues("capability")
+ .containsEntry("queryLimit", "+0..+5000 group Registered Users");
+ }
+
+ @Test
+ public void addAllowCapabilityWithDefaultRange() throws Exception {
+ Config config = projectOperations.project(allProjects).getConfig();
+ assertThat(config).sectionValues("capability").doesNotContainKey("queryLimit");
+
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(QUERY_LIMIT).group(REGISTERED_USERS))
+ .update();
+
+ assertThat(projectOperations.project(allProjects).getConfig())
+ .sectionValues("capability")
+ .containsEntry("queryLimit", "+0..+" + DEFAULT_MAX_QUERY_LIMIT + " group Registered Users");
+ }
+
+ @Test
+ public void removePermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allow(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS))
+ .add(allow(Permission.ABANDON).ref("refs/foo").group(PROJECT_OWNERS))
+ .update();
+ assertThat(projectOperations.project(key).getConfig())
+ .subsectionValues("access", "refs/foo")
+ .containsExactly(
+ "abandon", "group global:Registered-Users",
+ "abandon", "group global:Project-Owners");
+
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .remove(permissionKey(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS))
+ .update();
+ assertThat(projectOperations.project(key).getConfig())
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("abandon", "group global:Project-Owners");
+ }
+
+ @Test
+ public void removeLabelPermission() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/foo").group(REGISTERED_USERS).range(-1, 2))
+ .add(allowLabel("Code-Review").ref("refs/foo").group(PROJECT_OWNERS).range(-2, 1))
+ .update();
+ assertThat(projectOperations.project(key).getConfig())
+ .subsectionValues("access", "refs/foo")
+ .containsExactly(
+ "label-Code-Review", "-1..+2 group global:Registered-Users",
+ "label-Code-Review", "-2..+1 group global:Project-Owners");
+
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .remove(labelPermissionKey("Code-Review").ref("refs/foo").group(REGISTERED_USERS))
+ .update();
+ assertThat(projectOperations.project(key).getConfig())
+ .subsectionValues("access", "refs/foo")
+ .containsExactly("label-Code-Review", "-2..+1 group global:Project-Owners");
+ }
+
+ @Test
+ public void removeCapability() throws Exception {
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowCapability(ADMINISTRATE_SERVER).group(REGISTERED_USERS))
+ .add(allowCapability(ADMINISTRATE_SERVER).group(PROJECT_OWNERS))
+ .update();
+ assertThat(projectOperations.project(allProjects).getConfig())
+ .sectionValues("capability")
+ .containsAtLeastEntriesIn(
+ ImmutableListMultimap.of(
+ "administrateServer", "group Registered Users",
+ "administrateServer", "group Project Owners"));
+
+ projectOperations
+ .allProjectsForUpdate()
+ .remove(capabilityKey(ADMINISTRATE_SERVER).group(REGISTERED_USERS))
+ .update();
+ assertThat(projectOperations.project(allProjects).getConfig())
+ .sectionValues("capability")
+ .doesNotContainEntry("administrateServer", "group Registered Users");
+ }
+
+ @Test
+ public void removeOnePermissionForAllGroupsFromOneAccessSection() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allow(Permission.ABANDON).ref("refs/foo").group(PROJECT_OWNERS))
+ .add(allow(Permission.ABANDON).ref("refs/foo").group(REGISTERED_USERS))
+ .add(allow(Permission.CREATE).ref("refs/foo").group(REGISTERED_USERS))
+ .update();
+ assertThat(projectOperations.project(key).getConfig())
+ .subsectionValues("access", "refs/foo")
+ .containsAtLeastEntriesIn(
+ ImmutableListMultimap.of(
+ "abandon", "group global:Project-Owners",
+ "abandon", "group global:Registered-Users",
+ "create", "group global:Registered-Users"));
+
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .remove(permissionKey(Permission.ABANDON).ref("refs/foo"))
+ .update();
+ Config config = projectOperations.project(key).getConfig();
+ assertThat(config).subsectionValues("access", "refs/foo").doesNotContainKey("abandon");
+ assertThat(config)
+ .subsectionValues("access", "refs/foo")
+ .containsEntry("create", "group global:Registered-Users");
+ }
+
+ @Test
+ public void updatingCapabilitiesNotAllowedForNonAllProjects() throws Exception {
+ Project.NameKey key = projectOperations.newProject().create();
+ assertThrows(
+ RuntimeException.class,
+ () ->
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .add(allowCapability(ADMINISTRATE_SERVER).group(REGISTERED_USERS))
+ .update());
+ assertThrows(
+ RuntimeException.class,
+ () ->
+ projectOperations
+ .project(key)
+ .forUpdate()
+ .remove(capabilityKey(ADMINISTRATE_SERVER))
+ .update());
+ }
+
+ private void deleteRefsMetaConfig(Project.NameKey key) throws Exception {
+ try (Repository repo = repoManager.openRepository(key);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ tr.delete(REFS_CONFIG);
+ }
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdateTest.java b/javatests/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdateTest.java
new file mode 100644
index 0000000000..8fc1677e78
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdateTest.java
@@ -0,0 +1,202 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.testsuite.project;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.capabilityKey;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.labelPermissionKey;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
+import static com.google.gerrit.common.data.GlobalCapability.ADMINISTRATE_SERVER;
+import static com.google.gerrit.common.data.GlobalCapability.BATCH_CHANGES_LIMIT;
+import static com.google.gerrit.common.data.GlobalCapability.DEFAULT_MAX_BATCH_CHANGES_LIMIT;
+import static com.google.gerrit.common.data.GlobalCapability.DEFAULT_MAX_QUERY_LIMIT;
+import static com.google.gerrit.common.data.GlobalCapability.QUERY_LIMIT;
+import static com.google.gerrit.common.data.Permission.ABANDON;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestCapability;
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestLabelPermission;
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.TestPermissionKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.config.AllProjectsName;
+import java.util.function.Function;
+import org.junit.Test;
+
+public class TestProjectUpdateTest {
+ private static final AllProjectsName ALL_PROJECTS_NAME = new AllProjectsName("All-Projects");
+
+ @Test
+ public void testCapabilityDisallowsZeroRangeOnCapabilityThatHasNoRange() throws Exception {
+ assertThrows(
+ RuntimeException.class,
+ () -> allowCapability(ADMINISTRATE_SERVER).group(REGISTERED_USERS).range(0, 0).build());
+ }
+
+ @Test
+ public void testCapabilityAllowsZeroRangeOnCapabilityThatHasRange() throws Exception {
+ TestCapability c = allowCapability(QUERY_LIMIT).group(REGISTERED_USERS).range(0, 0).build();
+ assertThat(c.min()).isEqualTo(0);
+ assertThat(c.max()).isEqualTo(0);
+ }
+
+ @Test
+ public void testCapabilityDisallowsInvertedRange() throws Exception {
+ assertThrows(
+ RuntimeException.class,
+ () -> allowCapability(QUERY_LIMIT).group(REGISTERED_USERS).range(1, 0).build());
+ }
+
+ @Test
+ public void testCapabilityDisallowsRangeIfCapabilityDoesNotSupportRange() throws Exception {
+ assertThrows(
+ RuntimeException.class,
+ () -> allowCapability(ADMINISTRATE_SERVER).group(REGISTERED_USERS).range(-1, 1).build());
+ }
+
+ @Test
+ public void testCapabilityRangeIsZeroIfCapabilityDoesNotSupportRange() throws Exception {
+ TestCapability c = allowCapability(ADMINISTRATE_SERVER).group(REGISTERED_USERS).build();
+ assertThat(c.min()).isEqualTo(0);
+ assertThat(c.max()).isEqualTo(0);
+ }
+
+ @Test
+ public void testCapabilityUsesDefaultRangeIfUnspecified() throws Exception {
+ TestCapability c = allowCapability(QUERY_LIMIT).group(REGISTERED_USERS).build();
+ assertThat(c.min()).isEqualTo(0);
+ assertThat(c.max()).isEqualTo(DEFAULT_MAX_QUERY_LIMIT);
+
+ c = allowCapability(BATCH_CHANGES_LIMIT).group(REGISTERED_USERS).build();
+ assertThat(c.min()).isEqualTo(0);
+ assertThat(c.max()).isEqualTo(DEFAULT_MAX_BATCH_CHANGES_LIMIT);
+ }
+
+ @Test
+ public void testCapabilityUsesExplicitRangeIfSpecified() throws Exception {
+ TestCapability c = allowCapability(QUERY_LIMIT).group(REGISTERED_USERS).range(5, 20).build();
+ assertThat(c.min()).isEqualTo(5);
+ assertThat(c.max()).isEqualTo(20);
+ }
+
+ @Test
+ public void testLabelPermissionRequiresValidLabelName() throws Exception {
+ Function<String, TestLabelPermission.Builder> labelBuilder =
+ name -> allowLabel(name).ref("refs/*").group(REGISTERED_USERS).range(-1, 1);
+ assertThat(labelBuilder.apply("Code-Review").build().name()).isEqualTo("Code-Review");
+ assertThrows(RuntimeException.class, () -> labelBuilder.apply("not a label").build());
+ assertThrows(RuntimeException.class, () -> labelBuilder.apply("label-Code-Review").build());
+ }
+
+ @Test
+ public void testLabelPermissionDisallowsZeroRange() throws Exception {
+ assertThrows(
+ RuntimeException.class,
+ () -> allowLabel("Code-Review").ref("refs/*").group(REGISTERED_USERS).range(0, 0).build());
+ }
+
+ @Test
+ public void testLabelPermissionDisallowsInvertedRange() throws Exception {
+ assertThrows(
+ RuntimeException.class,
+ () -> allowLabel("Code-Review").ref("refs/*").group(REGISTERED_USERS).range(1, 0).build());
+ }
+
+ @Test
+ public void testPermissionKeyRequiresValidRefName() throws Exception {
+ Function<String, TestPermissionKey.Builder> keyBuilder =
+ ref -> permissionKey(ABANDON).ref(ref).group(REGISTERED_USERS);
+ assertThat(keyBuilder.apply("refs/*").build().section()).isEqualTo("refs/*");
+ assertThrows(RuntimeException.class, () -> keyBuilder.apply(null).build());
+ assertThrows(RuntimeException.class, () -> keyBuilder.apply("foo").build());
+ }
+
+ @Test
+ public void testLabelPermissionKeyRequiresValidLabelName() throws Exception {
+ Function<String, TestPermissionKey.Builder> keyBuilder =
+ label -> labelPermissionKey(label).ref("refs/*").group(REGISTERED_USERS);
+ assertThat(keyBuilder.apply("Code-Review").build().name()).isEqualTo("label-Code-Review");
+ assertThrows(RuntimeException.class, () -> keyBuilder.apply(null).build());
+ assertThrows(RuntimeException.class, () -> keyBuilder.apply("not a label").build());
+ assertThrows(RuntimeException.class, () -> keyBuilder.apply("label-Code-Review").build());
+ }
+
+ @Test
+ public void testPermissionKeyDisallowsSettingRefOnGlobalCapability() throws Exception {
+ assertThrows(RuntimeException.class, () -> capabilityKey(ADMINISTRATE_SERVER).ref("refs/*"));
+ }
+
+ @Test
+ public void testProjectUpdateDisallowsGroupOnExclusiveGroupPermissionKey() throws Exception {
+ TestPermissionKey.Builder b = permissionKey(ABANDON).ref("refs/*");
+ Function<TestPermissionKey.Builder, TestProjectUpdate.Builder> updateBuilder =
+ kb -> builder().setExclusiveGroup(kb, true);
+
+ assertThat(updateBuilder.apply(b).build().exclusiveGroupPermissions())
+ .containsExactly(b.build(), true);
+
+ b.group(REGISTERED_USERS);
+ assertThrows(RuntimeException.class, () -> updateBuilder.apply(b).build());
+ }
+
+ @Test
+ public void hasCapabilityUpdates() throws Exception {
+ assertThat(builder().build().hasCapabilityUpdates()).isFalse();
+ assertThat(
+ builder()
+ .add(allow(ABANDON).ref("refs/*").group(REGISTERED_USERS))
+ .add(allowLabel("Code-Review").ref("refs/*").group(REGISTERED_USERS).range(0, 1))
+ .remove(permissionKey(ABANDON).ref("refs/foo"))
+ .remove(labelPermissionKey("Code-Review").ref("refs/foo"))
+ .setExclusiveGroup(permissionKey(ABANDON).ref("refs/bar"), true)
+ .setExclusiveGroup(labelPermissionKey(ABANDON).ref("refs/bar"), true)
+ .build()
+ .hasCapabilityUpdates())
+ .isFalse();
+ assertThat(
+ builder(ALL_PROJECTS_NAME)
+ .add(allowCapability(ADMINISTRATE_SERVER).group(REGISTERED_USERS))
+ .build()
+ .hasCapabilityUpdates())
+ .isTrue();
+ assertThat(
+ builder(ALL_PROJECTS_NAME)
+ .remove(capabilityKey(ADMINISTRATE_SERVER))
+ .build()
+ .hasCapabilityUpdates())
+ .isTrue();
+ }
+
+ @Test
+ public void updatingCapabilitiesNotAllowedForNonAllProjects() throws Exception {
+ assertThrows(
+ RuntimeException.class,
+ () -> builder().add(allowCapability(ADMINISTRATE_SERVER).group(REGISTERED_USERS)).update());
+ assertThrows(
+ RuntimeException.class,
+ () -> builder().remove(capabilityKey(ADMINISTRATE_SERVER)).update());
+ }
+
+ private static TestProjectUpdate.Builder builder() {
+ return builder(Project.nameKey("test-project"));
+ }
+
+ private static TestProjectUpdate.Builder builder(Project.NameKey nameKey) {
+ return TestProjectUpdate.builder(nameKey, ALL_PROJECTS_NAME, u -> {});
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java
index 5cbed1b0e1..f6421a5065 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java
@@ -15,8 +15,9 @@
package com.google.gerrit.acceptance.testsuite.request;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -24,8 +25,8 @@ import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.TestAccount;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.ChangeInput;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.CurrentUser.PropertyKey;
@@ -67,12 +68,9 @@ public class RequestScopeOperationsImplTest extends AbstractDaemonTest {
@Test
public void setApiUserToNonExistingUser() throws Exception {
fastCheckCurrentUser(admin.id());
- try {
- requestScopeOperations.setApiUser(new Account.Id(sequences.nextAccountId()));
- assert_().fail("expected RuntimeException");
- } catch (RuntimeException e) {
- // Expected.
- }
+ assertThrows(
+ RuntimeException.class,
+ () -> requestScopeOperations.setApiUser(Account.id(sequences.nextAccountId())));
checkCurrentUser(admin.id());
}
@@ -98,24 +96,26 @@ public class RequestScopeOperationsImplTest extends AbstractDaemonTest {
private void fastCheckCurrentUser(Account.Id expected) {
// Check current user quickly, since the full check requires creating changes and is quite slow.
- assertThat(userProvider.get().isIdentifiedUser())
- .named("user from provider is an IdentifiedUser")
+ assertWithMessage("user from provider is an IdentifiedUser")
+ .that(userProvider.get().isIdentifiedUser())
.isTrue();
- assertThat(userProvider.get().getAccountId()).named("user from provider").isEqualTo(expected);
+ assertWithMessage("user from provider")
+ .that(userProvider.get().getAccountId())
+ .isEqualTo(expected);
}
private void checkCurrentUser(Account.Id expected) throws Exception {
// Test all supported ways that an acceptance test might query the active user.
fastCheckCurrentUser(expected);
- assertThat(gApi.accounts().self().get()._accountId)
- .named("user from GerritApi")
+ assertWithMessage("user from GerritApi")
+ .that(gApi.accounts().self().get()._accountId)
.isEqualTo(expected.get());
AcceptanceTestRequestScope.Context ctx = atrScope.get();
- assertThat(ctx.getUser().isIdentifiedUser())
- .named("user from AcceptanceTestRequestScope.Context is an IdentifiedUser")
+ assertWithMessage("user from AcceptanceTestRequestScope.Context is an IdentifiedUser")
+ .that(ctx.getUser().isIdentifiedUser())
.isTrue();
- assertThat(ctx.getUser().getAccountId())
- .named("user from AcceptanceTestRequestScope.Context")
+ assertWithMessage("user from AcceptanceTestRequestScope.Context")
+ .that(ctx.getUser().getAccountId())
.isEqualTo(expected);
checkSshUser(expected);
}
@@ -131,8 +131,8 @@ public class RequestScopeOperationsImplTest extends AbstractDaemonTest {
assertThat(gApi.changes().id(changeId).get().owner._accountId).isEqualTo(expected.get());
String queryResults =
atrScope.get().getSession().exec("gerrit query owner:self change:" + changeId);
- assertThat(findDistinct(queryResults, "I[0-9a-f]{40}"))
- .named("Change-Ids in query results:\n%s", queryResults)
+ assertWithMessage("Change-Ids in query results:\n%s", queryResults)
+ .that(findDistinct(queryResults, "I[0-9a-f]{40}"))
.containsExactly(changeId);
}
diff --git a/javatests/com/google/gerrit/common/AutoValueTest.java b/javatests/com/google/gerrit/common/AutoValueTest.java
index 89d7bf4f98..947fe4aa82 100644
--- a/javatests/com/google/gerrit/common/AutoValueTest.java
+++ b/javatests/com/google/gerrit/common/AutoValueTest.java
@@ -17,10 +17,9 @@ package com.google.gerrit.common;
import static com.google.common.truth.Truth.assertThat;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class AutoValueTest extends GerritBaseTests {
+public class AutoValueTest {
@AutoValue
abstract static class Auto {
static Auto create(String val) {
diff --git a/javatests/com/google/gerrit/common/BUILD b/javatests/com/google/gerrit/common/BUILD
index 18ececd41f..c7b21a3fd3 100644
--- a/javatests/com/google/gerrit/common/BUILD
+++ b/javatests/com/google/gerrit/common/BUILD
@@ -8,7 +8,6 @@ junit_tests(
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/common:version",
"//java/com/google/gerrit/launcher",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
diff --git a/javatests/com/google/gerrit/common/data/AccessSectionTest.java b/javatests/com/google/gerrit/common/data/AccessSectionTest.java
index faf9d6c752..e775cbcdb5 100644
--- a/javatests/com/google/gerrit/common/data/AccessSectionTest.java
+++ b/javatests/com/google/gerrit/common/data/AccessSectionTest.java
@@ -15,16 +15,16 @@
package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.junit.Before;
import org.junit.Test;
-public class AccessSectionTest extends GerritBaseTests {
+public class AccessSectionTest {
private static final String REF_PATTERN = "refs/heads/master";
private AccessSection accessSection;
@@ -57,16 +57,17 @@ public class AccessSectionTest extends GerritBaseTests {
Permission submitPermission = new Permission(Permission.SUBMIT);
accessSection.setPermissions(ImmutableList.of(submitPermission));
assertThat(accessSection.getPermissions()).containsExactly(submitPermission);
-
- exception.expect(NullPointerException.class);
- accessSection.setPermissions(null);
+ assertThrows(NullPointerException.class, () -> accessSection.setPermissions(null));
}
@Test
public void cannotSetDuplicatePermissions() {
- exception.expect(IllegalArgumentException.class);
- accessSection.setPermissions(
- ImmutableList.of(new Permission(Permission.ABANDON), new Permission(Permission.ABANDON)));
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ accessSection.setPermissions(
+ ImmutableList.of(
+ new Permission(Permission.ABANDON), new Permission(Permission.ABANDON))));
}
@Test
@@ -76,9 +77,11 @@ public class AccessSectionTest extends GerritBaseTests {
Permission abandonPermissionUpperCase =
new Permission(Permission.ABANDON.toUpperCase(Locale.US));
- exception.expect(IllegalArgumentException.class);
- accessSection.setPermissions(
- ImmutableList.of(abandonPermissionLowerCase, abandonPermissionUpperCase));
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ accessSection.setPermissions(
+ ImmutableList.of(abandonPermissionLowerCase, abandonPermissionUpperCase)));
}
@Test
@@ -92,9 +95,7 @@ public class AccessSectionTest extends GerritBaseTests {
Permission submitPermission = new Permission(Permission.SUBMIT);
accessSection.setPermissions(ImmutableList.of(submitPermission));
assertThat(accessSection.getPermission(Permission.SUBMIT)).isEqualTo(submitPermission);
-
- exception.expect(NullPointerException.class);
- accessSection.getPermission(null);
+ assertThrows(NullPointerException.class, () -> accessSection.getPermission(null));
}
@Test
@@ -112,8 +113,7 @@ public class AccessSectionTest extends GerritBaseTests {
assertThat(accessSection.getPermission(Permission.SUBMIT, true))
.isEqualTo(new Permission(Permission.SUBMIT));
- exception.expect(NullPointerException.class);
- accessSection.getPermission(null, true);
+ assertThrows(NullPointerException.class, () -> accessSection.getPermission(null, true));
}
@Test
@@ -130,9 +130,7 @@ public class AccessSectionTest extends GerritBaseTests {
assertThat(accessSection.getPermissions())
.containsExactly(abandonPermission, rebasePermission, submitPermission)
.inOrder();
-
- exception.expect(NullPointerException.class);
- accessSection.addPermission(null);
+ assertThrows(NullPointerException.class, () -> accessSection.addPermission(null));
}
@Test
@@ -166,9 +164,7 @@ public class AccessSectionTest extends GerritBaseTests {
assertThat(accessSection.getPermissions())
.containsExactly(abandonPermission, rebasePermission)
.inOrder();
-
- exception.expect(NullPointerException.class);
- accessSection.remove(null);
+ assertThrows(NullPointerException.class, () -> accessSection.remove(null));
}
@Test
@@ -187,8 +183,7 @@ public class AccessSectionTest extends GerritBaseTests {
.containsExactly(abandonPermission, rebasePermission)
.inOrder();
- exception.expect(NullPointerException.class);
- accessSection.removePermission(null);
+ assertThrows(NullPointerException.class, () -> accessSection.removePermission(null));
}
@Test
@@ -229,9 +224,7 @@ public class AccessSectionTest extends GerritBaseTests {
assertThat(accessSection1.getPermissions())
.containsExactly(abandonPermission, rebasePermission, submitPermission)
.inOrder();
-
- exception.expect(NullPointerException.class);
- accessSection.mergeFrom(null);
+ assertThrows(NullPointerException.class, () -> accessSection.mergeFrom(null));
}
@Test
diff --git a/javatests/com/google/gerrit/common/data/BUILD b/javatests/com/google/gerrit/common/data/BUILD
index 776a5e0250..f2b7d63b26 100644
--- a/javatests/com/google/gerrit/common/data/BUILD
+++ b/javatests/com/google/gerrit/common/data/BUILD
@@ -5,7 +5,7 @@ junit_tests(
srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
"//lib/truth",
diff --git a/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java b/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java
index 3dd2db3a5a..dcd3c054c2 100644
--- a/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java
+++ b/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class EncodePathSeparatorTest extends GerritBaseTests {
+public class EncodePathSeparatorTest {
@Test
public void defaultBehaviour() {
assertThat(new GitwebType().replacePathSeparator("a/b")).isEqualTo("a/b");
diff --git a/javatests/com/google/gerrit/common/data/FilenameComparatorTest.java b/javatests/com/google/gerrit/common/data/FilenameComparatorTest.java
index 055f57d3cc..ec71e05a16 100644
--- a/javatests/com/google/gerrit/common/data/FilenameComparatorTest.java
+++ b/javatests/com/google/gerrit/common/data/FilenameComparatorTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class FilenameComparatorTest extends GerritBaseTests {
+public class FilenameComparatorTest {
private FilenameComparator comparator = FilenameComparator.INSTANCE;
@Test
diff --git a/javatests/com/google/gerrit/common/data/GroupReferenceTest.java b/javatests/com/google/gerrit/common/data/GroupReferenceTest.java
index 8cf486b118..25b55c79bb 100644
--- a/javatests/com/google/gerrit/common/data/GroupReferenceTest.java
+++ b/javatests/com/google/gerrit/common/data/GroupReferenceTest.java
@@ -15,17 +15,17 @@
package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.AccountGroup.UUID;
import org.junit.Test;
-public class GroupReferenceTest extends GerritBaseTests {
+public class GroupReferenceTest {
@Test
public void forGroupDescription() {
String name = "foo";
- AccountGroup.UUID uuid = new AccountGroup.UUID("uuid-foo");
+ AccountGroup.UUID uuid = AccountGroup.uuid("uuid-foo");
GroupReference groupReference =
GroupReference.forGroup(
new GroupDescription.Basic() {
@@ -56,7 +56,7 @@ public class GroupReferenceTest extends GerritBaseTests {
@Test
public void create() {
- AccountGroup.UUID uuid = new AccountGroup.UUID("uuid");
+ AccountGroup.UUID uuid = AccountGroup.uuid("uuid");
String name = "foo";
GroupReference groupReference = new GroupReference(uuid, name);
assertThat(groupReference.getUUID()).isEqualTo(uuid);
@@ -68,15 +68,15 @@ public class GroupReferenceTest extends GerritBaseTests {
// GroupReferences where the UUID is null are used to represent groups from project.config that
// cannot be resolved.
String name = "foo";
- GroupReference groupReference = new GroupReference(null, name);
+ GroupReference groupReference = new GroupReference(name);
assertThat(groupReference.getUUID()).isNull();
assertThat(groupReference.getName()).isEqualTo(name);
}
@Test
public void cannotCreateWithoutName() {
- exception.expect(NullPointerException.class);
- new GroupReference(new AccountGroup.UUID("uuid"), null);
+ assertThrows(
+ NullPointerException.class, () -> new GroupReference(AccountGroup.uuid("uuid"), null));
}
@Test
@@ -99,12 +99,12 @@ public class GroupReferenceTest extends GerritBaseTests {
@Test
public void getAndSetUuid() {
- AccountGroup.UUID uuid = new AccountGroup.UUID("uuid-foo");
+ AccountGroup.UUID uuid = AccountGroup.uuid("uuid-foo");
String name = "foo";
GroupReference groupReference = new GroupReference(uuid, name);
assertThat(groupReference.getUUID()).isEqualTo(uuid);
- AccountGroup.UUID uuid2 = new AccountGroup.UUID("uuid-bar");
+ AccountGroup.UUID uuid2 = AccountGroup.uuid("uuid-bar");
groupReference.setUUID(uuid2);
assertThat(groupReference.getUUID()).isEqualTo(uuid2);
@@ -116,7 +116,7 @@ public class GroupReferenceTest extends GerritBaseTests {
@Test
public void getAndSetName() {
- AccountGroup.UUID uuid = new AccountGroup.UUID("uuid-foo");
+ AccountGroup.UUID uuid = AccountGroup.uuid("uuid-foo");
String name = "foo";
GroupReference groupReference = new GroupReference(uuid, name);
assertThat(groupReference.getName()).isEqualTo(name);
@@ -125,21 +125,20 @@ public class GroupReferenceTest extends GerritBaseTests {
groupReference.setName(name2);
assertThat(groupReference.getName()).isEqualTo(name2);
- exception.expect(NullPointerException.class);
- groupReference.setName(null);
+ assertThrows(NullPointerException.class, () -> groupReference.setName(null));
}
@Test
public void toConfigValue() {
String name = "foo";
- GroupReference groupReference = new GroupReference(new AccountGroup.UUID("uuid-foo"), name);
+ GroupReference groupReference = new GroupReference(AccountGroup.uuid("uuid-foo"), name);
assertThat(groupReference.toConfigValue()).isEqualTo("group " + name);
}
@Test
public void testEquals() {
- AccountGroup.UUID uuid1 = new AccountGroup.UUID("uuid-foo");
- AccountGroup.UUID uuid2 = new AccountGroup.UUID("uuid-bar");
+ AccountGroup.UUID uuid1 = AccountGroup.uuid("uuid-foo");
+ AccountGroup.UUID uuid2 = AccountGroup.uuid("uuid-bar");
String name1 = "foo";
String name2 = "bar";
@@ -154,12 +153,11 @@ public class GroupReferenceTest extends GerritBaseTests {
@Test
public void testHashcode() {
- AccountGroup.UUID uuid1 = new AccountGroup.UUID("uuid1");
+ AccountGroup.UUID uuid1 = AccountGroup.uuid("uuid1");
assertThat(new GroupReference(uuid1, "foo").hashCode())
.isEqualTo(new GroupReference(uuid1, "bar").hashCode());
// Check that the following calls don't fail with an exception.
- new GroupReference(null, "bar").hashCode();
- new GroupReference(new AccountGroup.UUID(null), "bar").hashCode();
+ new GroupReference("bar").hashCode();
}
}
diff --git a/javatests/com/google/gerrit/common/data/LabelFunctionTest.java b/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
index a534a9e8f8..6f5232b7d7 100644
--- a/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
+++ b/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
@@ -17,23 +17,22 @@ package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.testing.GerritBaseTests;
-import java.sql.Date;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import org.junit.Test;
-public class LabelFunctionTest extends GerritBaseTests {
+public class LabelFunctionTest {
private static final String LABEL_NAME = "Verified";
- private static final LabelId LABEL_ID = new LabelId(LABEL_NAME);
- private static final Change.Id CHANGE_ID = new Change.Id(100);
- private static final PatchSet.Id PS_ID = new PatchSet.Id(CHANGE_ID, 1);
+ private static final LabelId LABEL_ID = LabelId.create(LABEL_NAME);
+ private static final Change.Id CHANGE_ID = Change.id(100);
+ private static final PatchSet.Id PS_ID = PatchSet.id(CHANGE_ID, 1);
private static final LabelType VERIFIED_LABEL = makeLabel();
private static final PatchSetApproval APPROVAL_2 = makeApproval(2);
private static final PatchSetApproval APPROVAL_1 = makeApproval(1);
@@ -82,7 +81,7 @@ public class LabelFunctionTest extends GerritBaseTests {
SubmitRecord.Label myLabel = LabelFunction.MAX_NO_BLOCK.check(VERIFIED_LABEL, approvals);
assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.OK);
- assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_2.getAccountId());
+ assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_2.accountId());
}
private static LabelType makeLabel() {
@@ -97,14 +96,11 @@ public class LabelFunctionTest extends GerritBaseTests {
}
private static PatchSetApproval makeApproval(int value) {
- Account.Id accountId = new Account.Id(10000 + value);
- PatchSetApproval.Key key = makeKey(PS_ID, accountId, LABEL_ID);
- return new PatchSetApproval(key, (short) value, Date.from(Instant.now()));
- }
-
- private static PatchSetApproval.Key makeKey(
- PatchSet.Id psId, Account.Id accountId, LabelId labelId) {
- return new PatchSetApproval.Key(psId, accountId, labelId);
+ return PatchSetApproval.builder()
+ .key(PatchSetApproval.key(PS_ID, Account.id(10000 + value), LABEL_ID))
+ .value(value)
+ .granted(Date.from(Instant.now()))
+ .build();
}
private static void checkBlockWorks(LabelFunction function) {
@@ -113,7 +109,7 @@ public class LabelFunctionTest extends GerritBaseTests {
SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, approvals);
assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.REJECT);
- assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_M2.getAccountId());
+ assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_M2.accountId());
}
private static void checkNothingHappens(LabelFunction function) {
@@ -144,6 +140,6 @@ public class LabelFunctionTest extends GerritBaseTests {
SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, approvals);
assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.OK);
- assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_2.getAccountId());
+ assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_2.accountId());
}
}
diff --git a/javatests/com/google/gerrit/common/data/LabelTypeTest.java b/javatests/com/google/gerrit/common/data/LabelTypeTest.java
index db0df2e695..6c3befb1ac 100644
--- a/javatests/com/google/gerrit/common/data/LabelTypeTest.java
+++ b/javatests/com/google/gerrit/common/data/LabelTypeTest.java
@@ -17,10 +17,9 @@ package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class LabelTypeTest extends GerritBaseTests {
+public class LabelTypeTest {
@Test
public void sortLabelValues() {
LabelValue v0 = new LabelValue((short) 0, "Zero");
diff --git a/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java b/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
index b22a51151f..b646d2bce0 100644
--- a/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
+++ b/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
@@ -17,12 +17,11 @@ package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
-public class ParameterizedStringTest extends GerritBaseTests {
+public class ParameterizedStringTest {
@Test
public void emptyString() {
ParameterizedString p = new ParameterizedString("");
diff --git a/javatests/com/google/gerrit/common/data/PermissionRuleTest.java b/javatests/com/google/gerrit/common/data/PermissionRuleTest.java
index f442f3930f..d815dbc2e5 100644
--- a/javatests/com/google/gerrit/common/data/PermissionRuleTest.java
+++ b/javatests/com/google/gerrit/common/data/PermissionRuleTest.java
@@ -15,20 +15,20 @@
package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.common.data.PermissionRule.Action;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.AccountGroup;
import org.junit.Before;
import org.junit.Test;
-public class PermissionRuleTest extends GerritBaseTests {
+public class PermissionRuleTest {
private GroupReference groupReference;
private PermissionRule permissionRule;
@Before
public void setup() {
- this.groupReference = new GroupReference(new AccountGroup.UUID("uuid"), "group");
+ this.groupReference = new GroupReference(AccountGroup.uuid("uuid"), "group");
this.permissionRule = new PermissionRule(groupReference);
}
@@ -42,8 +42,7 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void cannotSetActionToNull() {
- exception.expect(NullPointerException.class);
- permissionRule.setAction(null);
+ assertThrows(NullPointerException.class, () -> permissionRule.setAction(null));
}
@Test
@@ -131,7 +130,7 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void setGroup() {
- GroupReference groupReference2 = new GroupReference(new AccountGroup.UUID("uuid2"), "group2");
+ GroupReference groupReference2 = new GroupReference(AccountGroup.uuid("uuid2"), "group2");
assertThat(groupReference2).isNotEqualTo(groupReference);
assertThat(permissionRule.getGroup()).isEqualTo(groupReference);
@@ -142,10 +141,10 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void mergeFromAnyBlock() {
- GroupReference groupReference1 = new GroupReference(new AccountGroup.UUID("uuid1"), "group1");
+ GroupReference groupReference1 = new GroupReference(AccountGroup.uuid("uuid1"), "group1");
PermissionRule permissionRule1 = new PermissionRule(groupReference1);
- GroupReference groupReference2 = new GroupReference(new AccountGroup.UUID("uuid2"), "group2");
+ GroupReference groupReference2 = new GroupReference(AccountGroup.uuid("uuid2"), "group2");
PermissionRule permissionRule2 = new PermissionRule(groupReference2);
permissionRule1.mergeFrom(permissionRule2);
@@ -170,10 +169,10 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void mergeFromAnyDeny() {
- GroupReference groupReference1 = new GroupReference(new AccountGroup.UUID("uuid1"), "group1");
+ GroupReference groupReference1 = new GroupReference(AccountGroup.uuid("uuid1"), "group1");
PermissionRule permissionRule1 = new PermissionRule(groupReference1);
- GroupReference groupReference2 = new GroupReference(new AccountGroup.UUID("uuid2"), "group2");
+ GroupReference groupReference2 = new GroupReference(AccountGroup.uuid("uuid2"), "group2");
PermissionRule permissionRule2 = new PermissionRule(groupReference2);
permissionRule1.mergeFrom(permissionRule2);
@@ -193,10 +192,10 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void mergeFromAnyBatch() {
- GroupReference groupReference1 = new GroupReference(new AccountGroup.UUID("uuid1"), "group1");
+ GroupReference groupReference1 = new GroupReference(AccountGroup.uuid("uuid1"), "group1");
PermissionRule permissionRule1 = new PermissionRule(groupReference1);
- GroupReference groupReference2 = new GroupReference(new AccountGroup.UUID("uuid2"), "group2");
+ GroupReference groupReference2 = new GroupReference(AccountGroup.uuid("uuid2"), "group2");
PermissionRule permissionRule2 = new PermissionRule(groupReference2);
permissionRule1.mergeFrom(permissionRule2);
@@ -216,10 +215,10 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void mergeFromAnyForce() {
- GroupReference groupReference1 = new GroupReference(new AccountGroup.UUID("uuid1"), "group1");
+ GroupReference groupReference1 = new GroupReference(AccountGroup.uuid("uuid1"), "group1");
PermissionRule permissionRule1 = new PermissionRule(groupReference1);
- GroupReference groupReference2 = new GroupReference(new AccountGroup.UUID("uuid2"), "group2");
+ GroupReference groupReference2 = new GroupReference(AccountGroup.uuid("uuid2"), "group2");
PermissionRule permissionRule2 = new PermissionRule(groupReference2);
permissionRule1.mergeFrom(permissionRule2);
@@ -239,11 +238,11 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void mergeFromMergeRange() {
- GroupReference groupReference1 = new GroupReference(new AccountGroup.UUID("uuid1"), "group1");
+ GroupReference groupReference1 = new GroupReference(AccountGroup.uuid("uuid1"), "group1");
PermissionRule permissionRule1 = new PermissionRule(groupReference1);
permissionRule1.setRange(-1, 2);
- GroupReference groupReference2 = new GroupReference(new AccountGroup.UUID("uuid2"), "group2");
+ GroupReference groupReference2 = new GroupReference(AccountGroup.uuid("uuid2"), "group2");
PermissionRule permissionRule2 = new PermissionRule(groupReference2);
permissionRule2.setRange(-2, 1);
@@ -256,10 +255,10 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void mergeFromGroupNotChanged() {
- GroupReference groupReference1 = new GroupReference(new AccountGroup.UUID("uuid1"), "group1");
+ GroupReference groupReference1 = new GroupReference(AccountGroup.uuid("uuid1"), "group1");
PermissionRule permissionRule1 = new PermissionRule(groupReference1);
- GroupReference groupReference2 = new GroupReference(new AccountGroup.UUID("uuid2"), "group2");
+ GroupReference groupReference2 = new GroupReference(AccountGroup.uuid("uuid2"), "group2");
PermissionRule permissionRule2 = new PermissionRule(groupReference2);
permissionRule1.mergeFrom(permissionRule2);
@@ -348,7 +347,7 @@ public class PermissionRuleTest extends GerritBaseTests {
@Test
public void testEquals() {
- GroupReference groupReference2 = new GroupReference(new AccountGroup.UUID("uuid2"), "group2");
+ GroupReference groupReference2 = new GroupReference(AccountGroup.uuid("uuid2"), "group2");
PermissionRule permissionRuleOther = new PermissionRule(groupReference2);
assertThat(permissionRule.equals(permissionRuleOther)).isFalse();
diff --git a/javatests/com/google/gerrit/common/data/PermissionTest.java b/javatests/com/google/gerrit/common/data/PermissionTest.java
index 23380e7f79..1012eff795 100644
--- a/javatests/com/google/gerrit/common/data/PermissionTest.java
+++ b/javatests/com/google/gerrit/common/data/PermissionTest.java
@@ -17,14 +17,13 @@ package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.AccountGroup;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
-public class PermissionTest extends GerritBaseTests {
+public class PermissionTest {
private static final String PERMISSION_NAME = "foo";
private Permission permission;
@@ -155,14 +154,14 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void setAndGetRules() {
PermissionRule permissionRule1 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-1"), "group1"));
PermissionRule permissionRule2 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-2"), "group2"));
permission.setRules(ImmutableList.of(permissionRule1, permissionRule2));
assertThat(permission.getRules()).containsExactly(permissionRule1, permissionRule2).inOrder();
PermissionRule permissionRule3 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-3"), "group3"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-3"), "group3"));
permission.setRules(ImmutableList.of(permissionRule3));
assertThat(permission.getRules()).containsExactly(permissionRule3);
}
@@ -170,10 +169,10 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void cannotAddPermissionByModifyingListThatWasProvidedToAccessSection() {
PermissionRule permissionRule1 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-1"), "group1"));
PermissionRule permissionRule2 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
- GroupReference groupReference3 = new GroupReference(new AccountGroup.UUID("uuid-3"), "group3");
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-2"), "group2"));
+ GroupReference groupReference3 = new GroupReference(AccountGroup.uuid("uuid-3"), "group3");
List<PermissionRule> rules = new ArrayList<>();
rules.add(permissionRule1);
@@ -188,14 +187,14 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void getNonExistingRule() {
- GroupReference groupReference = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
+ GroupReference groupReference = new GroupReference(AccountGroup.uuid("uuid-1"), "group1");
assertThat(permission.getRule(groupReference)).isNull();
assertThat(permission.getRule(groupReference, false)).isNull();
}
@Test
public void getRule() {
- GroupReference groupReference = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
+ GroupReference groupReference = new GroupReference(AccountGroup.uuid("uuid-1"), "group1");
PermissionRule permissionRule = new PermissionRule(groupReference);
permission.setRules(ImmutableList.of(permissionRule));
assertThat(permission.getRule(groupReference)).isEqualTo(permissionRule);
@@ -203,7 +202,7 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void createMissingRuleOnGet() {
- GroupReference groupReference = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
+ GroupReference groupReference = new GroupReference(AccountGroup.uuid("uuid-1"), "group1");
assertThat(permission.getRule(groupReference)).isNull();
assertThat(permission.getRule(groupReference, true))
@@ -213,11 +212,11 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void addRule() {
PermissionRule permissionRule1 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-1"), "group1"));
PermissionRule permissionRule2 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-2"), "group2"));
permission.setRules(ImmutableList.of(permissionRule1, permissionRule2));
- GroupReference groupReference3 = new GroupReference(new AccountGroup.UUID("uuid-3"), "group3");
+ GroupReference groupReference3 = new GroupReference(AccountGroup.uuid("uuid-3"), "group3");
assertThat(permission.getRule(groupReference3)).isNull();
PermissionRule permissionRule3 = new PermissionRule(groupReference3);
@@ -231,10 +230,10 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void removeRule() {
PermissionRule permissionRule1 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-1"), "group1"));
PermissionRule permissionRule2 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
- GroupReference groupReference3 = new GroupReference(new AccountGroup.UUID("uuid-3"), "group3");
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-2"), "group2"));
+ GroupReference groupReference3 = new GroupReference(AccountGroup.uuid("uuid-3"), "group3");
PermissionRule permissionRule3 = new PermissionRule(groupReference3);
permission.setRules(ImmutableList.of(permissionRule1, permissionRule2, permissionRule3));
@@ -248,10 +247,10 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void removeRuleByGroupReference() {
PermissionRule permissionRule1 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-1"), "group1"));
PermissionRule permissionRule2 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
- GroupReference groupReference3 = new GroupReference(new AccountGroup.UUID("uuid-3"), "group3");
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-2"), "group2"));
+ GroupReference groupReference3 = new GroupReference(AccountGroup.uuid("uuid-3"), "group3");
PermissionRule permissionRule3 = new PermissionRule(groupReference3);
permission.setRules(ImmutableList.of(permissionRule1, permissionRule2, permissionRule3));
@@ -265,9 +264,9 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void clearRules() {
PermissionRule permissionRule1 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-1"), "group1"));
PermissionRule permissionRule2 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-2"), "group2"));
permission.setRules(ImmutableList.of(permissionRule1, permissionRule2));
assertThat(permission.getRules()).isNotEmpty();
@@ -279,11 +278,11 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void mergePermissions() {
PermissionRule permissionRule1 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-1"), "group1"));
PermissionRule permissionRule2 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-2"), "group2"));
PermissionRule permissionRule3 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-3"), "group3"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-3"), "group3"));
Permission permission1 = new Permission("foo");
permission1.setRules(ImmutableList.of(permissionRule1, permissionRule2));
@@ -300,9 +299,9 @@ public class PermissionTest extends GerritBaseTests {
@Test
public void testEquals() {
PermissionRule permissionRule1 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-1"), "group1"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-1"), "group1"));
PermissionRule permissionRule2 =
- new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2"));
+ new PermissionRule(new GroupReference(AccountGroup.uuid("uuid-2"), "group2"));
permission.setRules(ImmutableList.of(permissionRule1, permissionRule2));
diff --git a/javatests/com/google/gerrit/common/data/SubmitRecordTest.java b/javatests/com/google/gerrit/common/data/SubmitRecordTest.java
index 5b9fde7cc0..5386b873e6 100644
--- a/javatests/com/google/gerrit/common/data/SubmitRecordTest.java
+++ b/javatests/com/google/gerrit/common/data/SubmitRecordTest.java
@@ -16,12 +16,11 @@ package com.google.gerrit.common.data;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.ArrayList;
import java.util.Collection;
import org.junit.Test;
-public class SubmitRecordTest extends GerritBaseTests {
+public class SubmitRecordTest {
private static final SubmitRecord OK_RECORD;
private static final SubmitRecord FORCED_RECORD;
private static final SubmitRecord NOT_READY_RECORD;
diff --git a/javatests/com/google/gerrit/elasticsearch/BUILD b/javatests/com/google/gerrit/elasticsearch/BUILD
index c7ae0d0a5f..ab2bb1283d 100644
--- a/javatests/com/google/gerrit/elasticsearch/BUILD
+++ b/javatests/com/google/gerrit/elasticsearch/BUILD
@@ -12,12 +12,11 @@ java_library(
deps = [
"//java/com/google/gerrit/elasticsearch",
"//java/com/google/gerrit/index",
- "//java/com/google/gerrit/server",
"//lib:guava",
+ "//lib:jgit",
"//lib:junit",
"//lib/guice",
"//lib/httpcomponents:httpcore",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/log:api",
"//lib/testcontainers",
"//lib/testcontainers:testcontainers-elasticsearch",
@@ -29,7 +28,7 @@ ELASTICSEARCH_DEPS = [
"//java/com/google/gerrit/elasticsearch",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
]
HTTP_TEST_DEPS = [
@@ -86,9 +85,9 @@ junit_tests(
"//java/com/google/gerrit/elasticsearch",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
"//lib/guice",
"//lib/httpcomponents:httpcore",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
index 9ce1456e09..7e044c330f 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
@@ -21,17 +21,17 @@ import static com.google.gerrit.elasticsearch.ElasticConfiguration.KEY_PREFIX;
import static com.google.gerrit.elasticsearch.ElasticConfiguration.KEY_SERVER;
import static com.google.gerrit.elasticsearch.ElasticConfiguration.KEY_USERNAME;
import static com.google.gerrit.elasticsearch.ElasticConfiguration.SECTION_ELASTICSEARCH;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.ProvisionException;
import java.util.Arrays;
import org.apache.http.HttpHost;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
-public class ElasticConfigurationTest extends GerritBaseTests {
+public class ElasticConfigurationTest {
@Test
public void singleServerNoOtherConfig() throws Exception {
Config cfg = newConfig();
@@ -121,9 +121,9 @@ public class ElasticConfigurationTest extends GerritBaseTests {
.containsExactly(hostURIs);
}
- private void assertProvisionException(Config cfg) throws Exception {
- exception.expect(ProvisionException.class);
- exception.expectMessage("No valid Elasticsearch servers configured");
- new ElasticConfiguration(cfg);
+ private void assertProvisionException(Config cfg) {
+ ProvisionException thrown =
+ assertThrows(ProvisionException.class, () -> new ElasticConfiguration(cfg));
+ assertThat(thrown).hasMessageThat().contains("No valid Elasticsearch servers configured");
}
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java b/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
index 0203a22df9..dcc688032a 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
@@ -15,7 +15,6 @@
package com.google.gerrit.elasticsearch;
import com.google.gerrit.index.IndexDefinition;
-import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
@@ -27,7 +26,7 @@ public final class ElasticTestUtils {
public static void configure(Config config, ElasticContainer container, String prefix) {
String hostname = container.getHttpHost().getHostName();
int port = container.getHttpHost().getPort();
- config.setEnum("index", null, "type", IndexType.ELASTICSEARCH);
+ config.setString("index", null, "type", "elasticsearch");
config.setString("elasticsearch", null, "server", "http://" + hostname + ":" + port);
config.setString("elasticsearch", null, "prefix", prefix);
config.setInt("index", null, "maxLimit", 10000);
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
index d4d321d395..15d8dd63fe 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
@@ -57,7 +57,7 @@ public class ElasticV6QueryAccountsTest extends AbstractQueryAccountsTest {
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- String indicesPrefix = getSanitizedMethodName();
+ String indicesPrefix = testName.getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
index 68c4b710f9..d734f1eb6f 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
@@ -14,8 +14,11 @@
package com.google.gerrit.elasticsearch;
+import static java.util.concurrent.TimeUnit.MINUTES;
+
import com.google.gerrit.server.query.change.AbstractQueryChangesTest;
import com.google.gerrit.testing.ConfigSuite;
+import com.google.gerrit.testing.GerritTestName;
import com.google.gerrit.testing.InMemoryModule;
import com.google.gerrit.testing.IndexConfig;
import com.google.inject.Guice;
@@ -28,6 +31,7 @@ import org.eclipse.jgit.lib.Config;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Rule;
public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest {
@ConfigSuite.Default
@@ -55,19 +59,23 @@ public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest {
}
}
+ @Rule public final GerritTestName testName = new GerritTestName();
+
@After
- public void closeIndex() {
+ public void closeIndex() throws Exception {
// Close the index after each test to prevent exceeding Elasticsearch's
// shard limit (see Issue 10120).
- client.execute(
- new HttpPost(
- String.format(
- "http://%s:%d/%s*/_close",
- container.getHttpHost().getHostName(),
- container.getHttpHost().getPort(),
- getSanitizedMethodName())),
- HttpClientContext.create(),
- null);
+ client
+ .execute(
+ new HttpPost(
+ String.format(
+ "http://%s:%d/%s*/_close",
+ container.getHttpHost().getHostName(),
+ container.getHttpHost().getPort(),
+ testName.getSanitizedMethodName())),
+ HttpClientContext.create(),
+ null)
+ .get(5, MINUTES);
}
@Override
@@ -80,7 +88,7 @@ public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest {
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- String indicesPrefix = getSanitizedMethodName();
+ String indicesPrefix = testName.getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
index 99c07f4486..28d798e7c4 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
@@ -57,7 +57,7 @@ public class ElasticV6QueryGroupsTest extends AbstractQueryGroupsTest {
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- String indicesPrefix = getSanitizedMethodName();
+ String indicesPrefix = testName.getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
index 89c77748b7..6658d7244e 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
@@ -57,7 +57,7 @@ public class ElasticV6QueryProjectsTest extends AbstractQueryProjectsTest {
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- String indicesPrefix = getSanitizedMethodName();
+ String indicesPrefix = testName.getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
index 91d366e711..48264906d4 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
@@ -57,7 +57,7 @@ public class ElasticV7QueryAccountsTest extends AbstractQueryAccountsTest {
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- String indicesPrefix = getSanitizedMethodName();
+ String indicesPrefix = testName.getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
index f11dc5b97d..d9a4d2ebc2 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
@@ -14,8 +14,11 @@
package com.google.gerrit.elasticsearch;
+import static java.util.concurrent.TimeUnit.MINUTES;
+
import com.google.gerrit.server.query.change.AbstractQueryChangesTest;
import com.google.gerrit.testing.ConfigSuite;
+import com.google.gerrit.testing.GerritTestName;
import com.google.gerrit.testing.InMemoryModule;
import com.google.gerrit.testing.IndexConfig;
import com.google.inject.Guice;
@@ -28,6 +31,7 @@ import org.eclipse.jgit.lib.Config;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Rule;
public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
@ConfigSuite.Default
@@ -55,19 +59,23 @@ public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
}
}
+ @Rule public final GerritTestName testName = new GerritTestName();
+
@After
- public void closeIndex() {
+ public void closeIndex() throws Exception {
// Close the index after each test to prevent exceeding Elasticsearch's
// shard limit (see Issue 10120).
- client.execute(
- new HttpPost(
- String.format(
- "http://%s:%d/%s*/_close",
- container.getHttpHost().getHostName(),
- container.getHttpHost().getPort(),
- getSanitizedMethodName())),
- HttpClientContext.create(),
- null);
+ client
+ .execute(
+ new HttpPost(
+ String.format(
+ "http://%s:%d/%s*/_close",
+ container.getHttpHost().getHostName(),
+ container.getHttpHost().getPort(),
+ testName.getSanitizedMethodName())),
+ HttpClientContext.create(),
+ null)
+ .get(5, MINUTES);
}
@Override
@@ -80,7 +88,7 @@ public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- String indicesPrefix = getSanitizedMethodName();
+ String indicesPrefix = testName.getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
index 282763a7dd..0fc96f8388 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
@@ -57,7 +57,7 @@ public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest {
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- String indicesPrefix = getSanitizedMethodName();
+ String indicesPrefix = testName.getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
index 4d83cf572c..1e56af9556 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
@@ -57,7 +57,7 @@ public class ElasticV7QueryProjectsTest extends AbstractQueryProjectsTest {
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- String indicesPrefix = getSanitizedMethodName();
+ String indicesPrefix = testName.getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig));
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
index 0a7a4222a6..9325a1b7bc 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
@@ -15,11 +15,11 @@
package com.google.gerrit.elasticsearch;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class ElasticVersionTest extends GerritBaseTests {
+public class ElasticVersionTest {
@Test
public void supportedVersion() throws Exception {
assertThat(ElasticVersion.forVersion("6.8.0")).isEqualTo(ElasticVersion.V6_8);
@@ -55,10 +55,14 @@ public class ElasticVersionTest extends GerritBaseTests {
@Test
public void unsupportedVersion() throws Exception {
- exception.expect(ElasticVersion.UnsupportedVersion.class);
- exception.expectMessage(
- "Unsupported version: [4.0.0]. Supported versions: " + ElasticVersion.supportedVersions());
- ElasticVersion.forVersion("4.0.0");
+ ElasticVersion.UnsupportedVersion thrown =
+ assertThrows(
+ ElasticVersion.UnsupportedVersion.class, () -> ElasticVersion.forVersion("4.0.0"));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "Unsupported version: [4.0.0]. Supported versions: "
+ + ElasticVersion.supportedVersions());
}
@Test
diff --git a/javatests/com/google/gerrit/reviewdb/client/AccountGroupTest.java b/javatests/com/google/gerrit/entities/AccountGroupTest.java
index 18a55bf77a..a9d5188cbb 100644
--- a/javatests/com/google/gerrit/reviewdb/client/AccountGroupTest.java
+++ b/javatests/com/google/gerrit/entities/AccountGroupTest.java
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.AccountGroup.UUID.fromRef;
-import static com.google.gerrit.reviewdb.client.AccountGroup.UUID.fromRefPart;
+import static com.google.gerrit.entities.AccountGroup.UUID.fromRef;
+import static com.google.gerrit.entities.AccountGroup.UUID.fromRefPart;
+import static com.google.gerrit.entities.AccountGroup.uuid;
import java.sql.Timestamp;
import java.time.Instant;
@@ -70,7 +71,29 @@ public class AccountGroupTest {
assertThat(fromRefPart("ab/" + TEST_UUID)).isNull();
}
- private AccountGroup.UUID uuid(String uuid) {
- return new AccountGroup.UUID(uuid);
+ @Test
+ public void uuidToString() {
+ assertThat(uuid("foo").toString()).isEqualTo("foo");
+ assertThat(uuid("foo bar").toString()).isEqualTo("foo+bar");
+ assertThat(uuid("foo:bar").toString()).isEqualTo("foo%3Abar");
+ }
+
+ @Test
+ public void parseUuid() {
+ assertThat(AccountGroup.UUID.parse("foo")).isEqualTo(uuid("foo"));
+ assertThat(AccountGroup.UUID.parse("foo+bar")).isEqualTo(uuid("foo bar"));
+ assertThat(AccountGroup.UUID.parse("foo%3Abar")).isEqualTo(uuid("foo:bar"));
+ }
+
+ @Test
+ public void idToString() {
+ assertThat(AccountGroup.id(123).toString()).isEqualTo("123");
+ }
+
+ @Test
+ public void nameKeyToString() {
+ assertThat(AccountGroup.nameKey("foo").toString()).isEqualTo("foo");
+ assertThat(AccountGroup.nameKey("foo bar").toString()).isEqualTo("foo+bar");
+ assertThat(AccountGroup.nameKey("foo:bar").toString()).isEqualTo("foo%3Abar");
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/client/AccountTest.java b/javatests/com/google/gerrit/entities/AccountTest.java
index 11a562f8ae..e89094321a 100644
--- a/javatests/com/google/gerrit/reviewdb/client/AccountTest.java
+++ b/javatests/com/google/gerrit/entities/AccountTest.java
@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.Account.Id.fromRef;
-import static com.google.gerrit.reviewdb.client.Account.Id.fromRefPart;
-import static com.google.gerrit.reviewdb.client.Account.Id.fromRefSuffix;
+import static com.google.gerrit.entities.Account.Id.fromRef;
+import static com.google.gerrit.entities.Account.Id.fromRefPart;
+import static com.google.gerrit.entities.Account.Id.fromRefSuffix;
+import static com.google.gerrit.entities.Account.id;
import org.junit.Test;
@@ -90,8 +91,4 @@ public class AccountTest {
assertThat(fromRefSuffix("12/34")).isEqualTo(id(34));
assertThat(fromRefSuffix("ab/cd")).isNull();
}
-
- private Account.Id id(int n) {
- return new Account.Id(n);
- }
}
diff --git a/javatests/com/google/gerrit/reviewdb/client/BUILD b/javatests/com/google/gerrit/entities/BUILD
index bc993d53e1..b24781a8dc 100644
--- a/javatests/com/google/gerrit/reviewdb/client/BUILD
+++ b/javatests/com/google/gerrit/entities/BUILD
@@ -1,13 +1,13 @@
load("//tools/bzl:junit.bzl", "junit_tests")
junit_tests(
- name = "client_tests",
+ name = "entities_tests",
srcs = glob(["*.java"]),
deps = [
- "//java/com/google/gerrit/reviewdb:server",
- "//java/com/google/gerrit/server/project/testing:project-test-util",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/entities/BranchTest.java b/javatests/com/google/gerrit/entities/BranchTest.java
new file mode 100644
index 0000000000..0483ebcc22
--- /dev/null
+++ b/javatests/com/google/gerrit/entities/BranchTest.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class BranchTest {
+ @Test
+ public void canonicalizeNameDuringConstruction() {
+ assertThat(BranchNameKey.create(new Project.NameKey("foo"), "bar").branch())
+ .isEqualTo("refs/heads/bar");
+ assertThat(BranchNameKey.create(new Project.NameKey("foo"), "refs/heads/bar").branch())
+ .isEqualTo("refs/heads/bar");
+ }
+
+ @Test
+ public void idToString() {
+ assertThat(BranchNameKey.create(new Project.NameKey("foo"), "bar").toString())
+ .isEqualTo("foo,refs/heads/bar");
+ assertThat(BranchNameKey.create(new Project.NameKey("foo bar"), "bar baz").toString())
+ .isEqualTo("foo+bar,refs/heads/bar+baz");
+ assertThat(BranchNameKey.create(new Project.NameKey("foo^bar"), "bar^baz").toString())
+ .isEqualTo("foo%5Ebar,refs/heads/bar%5Ebaz");
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/client/ChangeTest.java b/javatests/com/google/gerrit/entities/ChangeTest.java
index 6d1d0a6817..c75ad5a628 100644
--- a/javatests/com/google/gerrit/reviewdb/client/ChangeTest.java
+++ b/javatests/com/google/gerrit/entities/ChangeTest.java
@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
public class ChangeTest {
@@ -122,8 +123,8 @@ public class ChangeTest {
@Test
public void toRefPrefix() {
- assertThat(new Change.Id(1).toRefPrefix()).isEqualTo("refs/changes/01/1/");
- assertThat(new Change.Id(1234).toRefPrefix()).isEqualTo("refs/changes/34/1234/");
+ assertThat(Change.id(1).toRefPrefix()).isEqualTo("refs/changes/01/1/");
+ assertThat(Change.id(1234).toRefPrefix()).isEqualTo("refs/changes/34/1234/");
}
@Test
@@ -147,8 +148,20 @@ public class ChangeTest {
assertNotRefPart("1/1");
}
+ @Test
+ public void idToString() {
+ assertThat(Change.id(3).toString()).isEqualTo("3");
+ }
+
+ @Test
+ public void keyToString() {
+ String key = "Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ assertThat(ObjectId.isId(key.substring(1))).isTrue();
+ assertThat(Change.key(key).toString()).isEqualTo(key);
+ }
+
private static void assertRef(int changeId, String refName) {
- assertThat(Change.Id.fromRef(refName)).isEqualTo(new Change.Id(changeId));
+ assertThat(Change.Id.fromRef(refName)).isEqualTo(Change.id(changeId));
}
private static void assertNotRef(String refName) {
@@ -156,7 +169,7 @@ public class ChangeTest {
}
private static void assertAllUsersRef(int changeId, String refName) {
- assertThat(Change.Id.fromAllUsersRef(refName)).isEqualTo(new Change.Id(changeId));
+ assertThat(Change.Id.fromAllUsersRef(refName)).isEqualTo(Change.id(changeId));
}
private static void assertNotAllUsersRef(String refName) {
@@ -164,7 +177,7 @@ public class ChangeTest {
}
private static void assertRefPart(int changeId, String refName) {
- assertEquals(new Change.Id(changeId), Change.Id.fromRefPart(refName));
+ assertEquals(Change.id(changeId), Change.Id.fromRefPart(refName));
}
private static void assertNotRefPart(String refName) {
diff --git a/javatests/com/google/gerrit/reviewdb/client/PatchSetApprovalTest.java b/javatests/com/google/gerrit/entities/PatchSetApprovalTest.java
index 5e42ce05d1..81aa3b84bb 100644
--- a/javatests/com/google/gerrit/reviewdb/client/PatchSetApprovalTest.java
+++ b/javatests/com/google/gerrit/entities/PatchSetApprovalTest.java
@@ -12,27 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
-public class PatchSetApprovalTest extends GerritBaseTests {
+public class PatchSetApprovalTest {
@Test
public void keyEquality() {
PatchSetApproval.Key k1 =
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(1), 2), new Account.Id(3), new LabelId("My-Label"));
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(1), 2), Account.id(3), LabelId.create("My-Label"));
PatchSetApproval.Key k2 =
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(1), 2), new Account.Id(3), new LabelId("My-Label"));
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(1), 2), Account.id(3), LabelId.create("My-Label"));
PatchSetApproval.Key k3 =
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(1), 2), new Account.Id(3), new LabelId("Other-Label"));
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(1), 2), Account.id(3), LabelId.create("Other-Label"));
assertThat(k2).isEqualTo(k1);
assertThat(k3).isNotEqualTo(k1);
diff --git a/javatests/com/google/gerrit/reviewdb/client/PatchSetTest.java b/javatests/com/google/gerrit/entities/PatchSetTest.java
index 51a405f6b6..61e1b4c438 100644
--- a/javatests/com/google/gerrit/reviewdb/client/PatchSetTest.java
+++ b/javatests/com/google/gerrit/entities/PatchSetTest.java
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.PatchSet.joinGroups;
-import static com.google.gerrit.reviewdb.client.PatchSet.splitGroups;
+import static com.google.gerrit.entities.PatchSet.joinGroups;
+import static com.google.gerrit.entities.PatchSet.splitGroups;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
@@ -64,37 +65,67 @@ public class PatchSetTest {
@Test
public void testSplitGroups() {
+ assertRuntimeException(() -> splitGroups(null));
assertThat(splitGroups("")).containsExactly("");
assertThat(splitGroups("abcd")).containsExactly("abcd");
assertThat(splitGroups("ab,cd")).containsExactly("ab", "cd").inOrder();
+ assertThat(splitGroups("ab , cd")).containsExactly("ab ", " cd").inOrder();
assertThat(splitGroups("ab,")).containsExactly("ab", "").inOrder();
assertThat(splitGroups(",cd")).containsExactly("", "cd").inOrder();
}
@Test
public void testJoinGroups() {
+ assertRuntimeException(() -> joinGroups(null));
+ assertRuntimeException(() -> joinGroups(ImmutableList.of("a,", "b")));
assertThat(joinGroups(ImmutableList.of(""))).isEqualTo("");
assertThat(joinGroups(ImmutableList.of("abcd"))).isEqualTo("abcd");
assertThat(joinGroups(ImmutableList.of("ab", "cd"))).isEqualTo("ab,cd");
+ assertThat(joinGroups(ImmutableList.of("ab ", " cd"))).isEqualTo("ab , cd");
assertThat(joinGroups(ImmutableList.of("ab", ""))).isEqualTo("ab,");
assertThat(joinGroups(ImmutableList.of("", "cd"))).isEqualTo(",cd");
}
@Test
public void toRefName() {
- assertThat(new PatchSet.Id(new Change.Id(1), 23).toRefName()).isEqualTo("refs/changes/01/1/23");
- assertThat(new PatchSet.Id(new Change.Id(1234), 5).toRefName())
- .isEqualTo("refs/changes/34/1234/5");
+ assertThat(PatchSet.id(Change.id(1), 23).toRefName()).isEqualTo("refs/changes/01/1/23");
+ assertThat(PatchSet.id(Change.id(1234), 5).toRefName()).isEqualTo("refs/changes/34/1234/5");
+ }
+
+ @Test
+ public void parseId() {
+ assertThat(PatchSet.Id.parse("1,2")).isEqualTo(PatchSet.id(Change.id(1), 2));
+ assertThat(PatchSet.Id.parse("01,02")).isEqualTo(PatchSet.id(Change.id(1), 2));
+ assertInvalidId(null);
+ assertInvalidId("");
+ assertInvalidId("1");
+ assertInvalidId("1,foo.txt");
+ assertInvalidId("foo.txt,1");
+
+ String hexComma = "%" + String.format("%02x", (int) ',');
+ assertInvalidId("1" + hexComma + "2");
+ }
+
+ @Test
+ public void idToString() {
+ assertThat(PatchSet.id(Change.id(2), 3).toString()).isEqualTo("2,3");
}
private static void assertRef(int changeId, int psId, String refName) {
assertThat(PatchSet.isChangeRef(refName)).isTrue();
- assertThat(PatchSet.Id.fromRef(refName))
- .isEqualTo(new PatchSet.Id(new Change.Id(changeId), psId));
+ assertThat(PatchSet.Id.fromRef(refName)).isEqualTo(PatchSet.id(Change.id(changeId), psId));
}
private static void assertNotRef(String refName) {
assertThat(PatchSet.isChangeRef(refName)).isFalse();
assertThat(PatchSet.Id.fromRef(refName)).isNull();
}
+
+ private static void assertInvalidId(String str) {
+ assertRuntimeException(() -> PatchSet.Id.parse(str));
+ }
+
+ private static void assertRuntimeException(Runnable runnable) {
+ assertThrows(RuntimeException.class, () -> runnable.run());
+ }
}
diff --git a/javatests/com/google/gerrit/entities/PatchTest.java b/javatests/com/google/gerrit/entities/PatchTest.java
new file mode 100644
index 0000000000..9f906a92ef
--- /dev/null
+++ b/javatests/com/google/gerrit/entities/PatchTest.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import org.junit.Test;
+
+public class PatchTest {
+ @Test
+ public void isMagic() {
+ assertThat(Patch.isMagic("/COMMIT_MSG")).isTrue();
+ assertThat(Patch.isMagic("/MERGE_LIST")).isTrue();
+
+ assertThat(Patch.isMagic("/COMMIT_MSG/")).isFalse();
+ assertThat(Patch.isMagic("COMMIT_MSG")).isFalse();
+ assertThat(Patch.isMagic("/commit_msg")).isFalse();
+ }
+
+ @Test
+ public void parseKey() {
+ assertThat(Patch.Key.parse("1,2,foo.txt"))
+ .isEqualTo(Patch.key(PatchSet.id(Change.id(1), 2), "foo.txt"));
+ assertThat(Patch.Key.parse("01,02,foo.txt"))
+ .isEqualTo(Patch.key(PatchSet.id(Change.id(1), 2), "foo.txt"));
+ assertInvalidKey(null);
+ assertInvalidKey("");
+ assertInvalidKey("1,2");
+ assertInvalidKey("1, 2, foo.txt");
+ assertInvalidKey("1,foo.txt");
+ assertInvalidKey("1,foo.txt,2");
+ assertInvalidKey("foo.txt,1,2");
+
+ String hexComma = "%" + String.format("%02x", (int) ',');
+ assertInvalidKey("1" + hexComma + "2" + hexComma + "foo.txt");
+ }
+
+ private static void assertInvalidKey(String str) {
+ assertThrows(RuntimeException.class, () -> Patch.Key.parse(str));
+ }
+}
diff --git a/javatests/com/google/gerrit/entities/ProjectTest.java b/javatests/com/google/gerrit/entities/ProjectTest.java
new file mode 100644
index 0000000000..341b54fc06
--- /dev/null
+++ b/javatests/com/google/gerrit/entities/ProjectTest.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class ProjectTest {
+ @Test
+ public void parseId() {
+ assertThat(Project.NameKey.parse("foo")).isEqualTo(new Project.NameKey("foo"));
+ assertThat(Project.NameKey.parse("foo%20bar")).isEqualTo(new Project.NameKey("foo bar"));
+ assertThat(Project.NameKey.parse("foo+bar")).isEqualTo(new Project.NameKey("foo bar"));
+ assertThat(Project.NameKey.parse("foo%2fbar")).isEqualTo(new Project.NameKey("foo/bar"));
+ assertThat(Project.NameKey.parse("foo%2Fbar")).isEqualTo(new Project.NameKey("foo/bar"));
+ }
+
+ @Test
+ public void idToString() {
+ assertThat(Project.nameKey("foo").toString()).isEqualTo("foo");
+ assertThat(Project.nameKey("foo bar").toString()).isEqualTo("foo+bar");
+ assertThat(Project.nameKey("foo/bar").toString()).isEqualTo("foo/bar");
+ assertThat(Project.nameKey("foo^bar").toString()).isEqualTo("foo%5Ebar");
+ assertThat(Project.nameKey("foo%bar").toString()).isEqualTo("foo%25bar");
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/client/RefNamesTest.java b/javatests/com/google/gerrit/entities/RefNamesTest.java
index fa6a722db2..1990292fe3 100644
--- a/javatests/com/google/gerrit/reviewdb/client/RefNamesTest.java
+++ b/javatests/com/google/gerrit/entities/RefNamesTest.java
@@ -12,29 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.client;
+package com.google.gerrit.entities;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.parseAfterShardedRefPart;
-import static com.google.gerrit.reviewdb.client.RefNames.parseRefSuffix;
-import static com.google.gerrit.reviewdb.client.RefNames.parseShardedRefPart;
-import static com.google.gerrit.reviewdb.client.RefNames.parseShardedUuidFromRefPart;
-import static com.google.gerrit.reviewdb.client.RefNames.skipShardedRefPart;
+import static com.google.gerrit.entities.RefNames.parseAfterShardedRefPart;
+import static com.google.gerrit.entities.RefNames.parseRefSuffix;
+import static com.google.gerrit.entities.RefNames.parseShardedRefPart;
+import static com.google.gerrit.entities.RefNames.parseShardedUuidFromRefPart;
+import static com.google.gerrit.entities.RefNames.skipShardedRefPart;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
public class RefNamesTest {
private static final String TEST_GROUP_UUID = "ccab3195282a8ce4f5014efa391e82d10f884c64";
private static final String TEST_SHARDED_GROUP_UUID =
TEST_GROUP_UUID.substring(0, 2) + "/" + TEST_GROUP_UUID;
-
- @Rule public ExpectedException expectedException = ExpectedException.none();
-
- private final Account.Id accountId = new Account.Id(1011123);
- private final Change.Id changeId = new Change.Id(67473);
- private final PatchSet.Id psId = new PatchSet.Id(changeId, 42);
+ private final Account.Id accountId = Account.id(1011123);
+ private final Change.Id changeId = Change.id(67473);
+ private final PatchSet.Id psId = PatchSet.id(changeId, 42);
@Test
public void fullName() throws Exception {
@@ -54,34 +50,35 @@ public class RefNamesTest {
String robotCommentsRef = RefNames.robotCommentsRef(changeId);
assertThat(robotCommentsRef).isEqualTo("refs/changes/73/67473/robot-comments");
assertThat(RefNames.isNoteDbMetaRef(robotCommentsRef)).isTrue();
+
+ String changeRefPrefix = RefNames.changeRefPrefix(changeId);
+ assertThat(changeRefPrefix).isEqualTo("refs/changes/73/67473/");
}
@Test
public void refForGroupIsSharded() throws Exception {
- AccountGroup.UUID groupUuid = new AccountGroup.UUID("ABCDEFG");
+ AccountGroup.UUID groupUuid = AccountGroup.uuid("ABCDEFG");
String groupRef = RefNames.refsGroups(groupUuid);
assertThat(groupRef).isEqualTo("refs/groups/AB/ABCDEFG");
}
@Test
public void refForGroupWithUuidLessThanTwoCharsIsRejected() throws Exception {
- AccountGroup.UUID groupUuid = new AccountGroup.UUID("A");
- expectedException.expect(IllegalArgumentException.class);
- RefNames.refsGroups(groupUuid);
+ AccountGroup.UUID groupUuid = AccountGroup.uuid("A");
+ assertThrows(IllegalArgumentException.class, () -> RefNames.refsGroups(groupUuid));
}
@Test
public void refForDeletedGroupIsSharded() throws Exception {
- AccountGroup.UUID groupUuid = new AccountGroup.UUID("ABCDEFG");
+ AccountGroup.UUID groupUuid = AccountGroup.uuid("ABCDEFG");
String groupRef = RefNames.refsDeletedGroups(groupUuid);
assertThat(groupRef).isEqualTo("refs/deleted-groups/AB/ABCDEFG");
}
@Test
public void refForDeletedGroupWithUuidLessThanTwoCharsIsRejected() throws Exception {
- AccountGroup.UUID groupUuid = new AccountGroup.UUID("A");
- expectedException.expect(IllegalArgumentException.class);
- RefNames.refsDeletedGroups(groupUuid);
+ AccountGroup.UUID groupUuid = AccountGroup.uuid("A");
+ assertThrows(IllegalArgumentException.class, () -> RefNames.refsDeletedGroups(groupUuid));
}
@Test
diff --git a/javatests/com/google/gerrit/reviewdb/converter/AccountIdProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/AccountIdProtoConverterTest.java
index 123a973ede..0e4fbc8512 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/AccountIdProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/AccountIdProtoConverterTest.java
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.protobuf.Parser;
import org.junit.Test;
@@ -30,7 +30,7 @@ public class AccountIdProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- Account.Id accountId = new Account.Id(24);
+ Account.Id accountId = Account.id(24);
Entities.Account_Id proto = accountIdProtoConverter.toProto(accountId);
@@ -40,7 +40,7 @@ public class AccountIdProtoConverterTest {
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- Account.Id accountId = new Account.Id(34832);
+ Account.Id accountId = Account.id(34832);
Account.Id convertedAccountId =
accountIdProtoConverter.fromProto(accountIdProtoConverter.toProto(accountId));
@@ -61,7 +61,8 @@ public class AccountIdProtoConverterTest {
/** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
- public void fieldsExistAsExpected() {
- assertThatSerializedClass(Account.Id.class).hasFields(ImmutableMap.of("id", int.class));
+ public void methodsExistAsExpected() {
+ assertThatSerializedClass(Account.Id.class)
+ .hasAutoValueMethods(ImmutableMap.of("id", int.class));
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/BUILD b/javatests/com/google/gerrit/entities/converter/BUILD
index 9cc941c50b..6ca9871b9c 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/BUILD
+++ b/javatests/com/google/gerrit/entities/converter/BUILD
@@ -4,10 +4,12 @@ junit_tests(
name = "proto_converter_tests",
srcs = glob(["*.java"]),
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/proto/testing",
- "//java/com/google/gerrit/reviewdb:server",
"//lib:guava",
+ "//lib:jgit",
"//lib:protobuf",
+ "//lib/guice",
"//lib/truth",
"//lib/truth:truth-proto-extension",
"//proto:entities_java_proto",
diff --git a/javatests/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/BranchNameKeyProtoConverterTest.java
index 412641f6aa..0a73db8975 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/BranchNameKeyProtoConverterTest.java
@@ -12,17 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.protobuf.Parser;
import java.lang.reflect.Type;
import org.junit.Test;
@@ -33,23 +33,23 @@ public class BranchNameKeyProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- Branch.NameKey nameKey = new Branch.NameKey(new Project.NameKey("project-13"), "branch-72");
+ BranchNameKey nameKey = BranchNameKey.create(Project.nameKey("project-13"), "branch-72");
Entities.Branch_NameKey proto = branchNameKeyProtoConverter.toProto(nameKey);
Entities.Branch_NameKey expectedProto =
Entities.Branch_NameKey.newBuilder()
- .setProjectName(Entities.Project_NameKey.newBuilder().setName("project-13"))
- .setBranchName("refs/heads/branch-72")
+ .setProject(Entities.Project_NameKey.newBuilder().setName("project-13"))
+ .setBranch("refs/heads/branch-72")
.build();
assertThat(proto).isEqualTo(expectedProto);
}
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- Branch.NameKey nameKey = new Branch.NameKey(new Project.NameKey("project-52"), "branch 14");
+ BranchNameKey nameKey = BranchNameKey.create(Project.nameKey("project-52"), "branch 14");
- Branch.NameKey convertedNameKey =
+ BranchNameKey convertedNameKey =
branchNameKeyProtoConverter.fromProto(branchNameKeyProtoConverter.toProto(nameKey));
assertThat(convertedNameKey).isEqualTo(nameKey);
@@ -59,8 +59,8 @@ public class BranchNameKeyProtoConverterTest {
public void protoCanBeParsedFromBytes() throws Exception {
Entities.Branch_NameKey proto =
Entities.Branch_NameKey.newBuilder()
- .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 1"))
- .setBranchName("branch 36")
+ .setProject(Entities.Project_NameKey.newBuilder().setName("project 1"))
+ .setBranch("branch 36")
.build();
byte[] bytes = proto.toByteArray();
@@ -72,12 +72,12 @@ public class BranchNameKeyProtoConverterTest {
/** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
- public void fieldsExistAsExpected() {
- assertThatSerializedClass(Branch.NameKey.class)
- .hasFields(
+ public void methodsExistAsExpected() {
+ assertThatSerializedClass(BranchNameKey.class)
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
- .put("projectName", Project.NameKey.class)
- .put("branchName", String.class)
+ .put("project", Project.NameKey.class)
+ .put("branch", String.class)
.build());
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/ChangeIdProtoConverterTest.java
index d5ebb515b6..12f3f33253 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/ChangeIdProtoConverterTest.java
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.protobuf.Parser;
import org.junit.Test;
@@ -30,7 +30,7 @@ public class ChangeIdProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- Change.Id changeId = new Change.Id(94);
+ Change.Id changeId = Change.id(94);
Entities.Change_Id proto = changeIdProtoConverter.toProto(changeId);
@@ -40,7 +40,7 @@ public class ChangeIdProtoConverterTest {
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- Change.Id changeId = new Change.Id(2903482);
+ Change.Id changeId = Change.id(2903482);
Change.Id convertedChangeId =
changeIdProtoConverter.fromProto(changeIdProtoConverter.toProto(changeId));
@@ -61,7 +61,8 @@ public class ChangeIdProtoConverterTest {
/** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
- public void fieldsExistAsExpected() {
- assertThatSerializedClass(Change.Id.class).hasFields(ImmutableMap.of("id", int.class));
+ public void methodsExistAsExpected() {
+ assertThatSerializedClass(Change.Id.class)
+ .hasAutoValueMethods(ImmutableMap.of("id", int.class));
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/ChangeKeyProtoConverterTest.java
index d94870682b..e9080b3265 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/ChangeKeyProtoConverterTest.java
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.protobuf.Parser;
import org.junit.Test;
@@ -30,7 +30,7 @@ public class ChangeKeyProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- Change.Key changeKey = new Change.Key("change-1");
+ Change.Key changeKey = Change.key("change-1");
Entities.Change_Key proto = changeKeyProtoConverter.toProto(changeKey);
@@ -40,7 +40,7 @@ public class ChangeKeyProtoConverterTest {
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- Change.Key changeKey = new Change.Key("change-52");
+ Change.Key changeKey = Change.key("change-52");
Change.Key convertedChangeKey =
changeKeyProtoConverter.fromProto(changeKeyProtoConverter.toProto(changeKey));
@@ -61,7 +61,8 @@ public class ChangeKeyProtoConverterTest {
/** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
- public void fieldsExistAsExpected() {
- assertThatSerializedClass(Change.Key.class).hasFields(ImmutableMap.of("id", String.class));
+ public void methodsExistAsExpected() {
+ assertThatSerializedClass(Change.Key.class)
+ .hasAutoValueMethods(ImmutableMap.of("key", String.class));
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/ChangeMessageKeyProtoConverterTest.java
index c8bb2ed799..72ce89608b 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/ChangeMessageKeyProtoConverterTest.java
@@ -12,17 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.protobuf.Parser;
import java.lang.reflect.Type;
import org.junit.Test;
@@ -33,7 +33,7 @@ public class ChangeMessageKeyProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- ChangeMessage.Key messageKey = new ChangeMessage.Key(new Change.Id(704), "aabbcc");
+ ChangeMessage.Key messageKey = ChangeMessage.key(Change.id(704), "aabbcc");
Entities.ChangeMessage_Key proto = messageKeyProtoConverter.toProto(messageKey);
@@ -47,7 +47,7 @@ public class ChangeMessageKeyProtoConverterTest {
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- ChangeMessage.Key messageKey = new ChangeMessage.Key(new Change.Id(704), "aabbcc");
+ ChangeMessage.Key messageKey = ChangeMessage.key(Change.id(704), "aabbcc");
ChangeMessage.Key convertedMessageKey =
messageKeyProtoConverter.fromProto(messageKeyProtoConverter.toProto(messageKey));
@@ -72,9 +72,9 @@ public class ChangeMessageKeyProtoConverterTest {
/** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
- public void fieldsExistAsExpected() {
+ public void methodsExistAsExpected() {
assertThatSerializedClass(ChangeMessage.Key.class)
- .hasFields(
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("changeId", Change.Id.class)
.put("uuid", String.class)
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/ChangeMessageProtoConverterTest.java
index 65bdfbbc3d..933ffb40e1 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/ChangeMessageProtoConverterTest.java
@@ -12,19 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.protobuf.Parser;
import java.lang.reflect.Type;
import java.sql.Timestamp;
@@ -38,13 +38,13 @@ public class ChangeMessageProtoConverterTest {
public void allValuesConvertedToProto() {
ChangeMessage changeMessage =
new ChangeMessage(
- new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
- new Account.Id(63),
+ ChangeMessage.key(Change.id(543), "change-message-21"),
+ Account.id(63),
new Timestamp(9876543),
- new PatchSet.Id(new Change.Id(34), 13));
+ PatchSet.id(Change.id(34), 13));
changeMessage.setMessage("This is a change message.");
changeMessage.setTag("An arbitrary tag.");
- changeMessage.setRealAuthor(new Account.Id(10003));
+ changeMessage.setRealAuthor(Account.id(10003));
Entities.ChangeMessage proto = changeMessageProtoConverter.toProto(changeMessage);
@@ -60,7 +60,7 @@ public class ChangeMessageProtoConverterTest {
.setPatchset(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(34))
- .setPatchSetId(13))
+ .setId(13))
.setTag("An arbitrary tag.")
.setRealAuthor(Entities.Account_Id.newBuilder().setId(10003))
.build();
@@ -71,10 +71,10 @@ public class ChangeMessageProtoConverterTest {
public void mainValuesConvertedToProto() {
ChangeMessage changeMessage =
new ChangeMessage(
- new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
- new Account.Id(63),
+ ChangeMessage.key(Change.id(543), "change-message-21"),
+ Account.id(63),
new Timestamp(9876543),
- new PatchSet.Id(new Change.Id(34), 13));
+ PatchSet.id(Change.id(34), 13));
Entities.ChangeMessage proto = changeMessageProtoConverter.toProto(changeMessage);
@@ -89,7 +89,7 @@ public class ChangeMessageProtoConverterTest {
.setPatchset(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(34))
- .setPatchSetId(13))
+ .setId(13))
.build();
assertThat(proto).isEqualTo(expectedProto);
}
@@ -99,10 +99,7 @@ public class ChangeMessageProtoConverterTest {
public void realAuthorIsNotAutomaticallySetToAuthorWhenConvertedToProto() {
ChangeMessage changeMessage =
new ChangeMessage(
- new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
- new Account.Id(63),
- null,
- null);
+ ChangeMessage.key(Change.id(543), "change-message-21"), Account.id(63), null, null);
Entities.ChangeMessage proto = changeMessageProtoConverter.toProto(changeMessage);
@@ -122,8 +119,7 @@ public class ChangeMessageProtoConverterTest {
// writtenOn may not be null according to the column definition but it's optional for the
// protobuf definition. -> assume as optional and hence test null
ChangeMessage changeMessage =
- new ChangeMessage(
- new ChangeMessage.Key(new Change.Id(543), "change-message-21"), null, null, null);
+ new ChangeMessage(ChangeMessage.key(Change.id(543), "change-message-21"), null, null, null);
Entities.ChangeMessage proto = changeMessageProtoConverter.toProto(changeMessage);
@@ -141,13 +137,13 @@ public class ChangeMessageProtoConverterTest {
public void allValuesConvertedToProtoAndBackAgain() {
ChangeMessage changeMessage =
new ChangeMessage(
- new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
- new Account.Id(63),
+ ChangeMessage.key(Change.id(543), "change-message-21"),
+ Account.id(63),
new Timestamp(9876543),
- new PatchSet.Id(new Change.Id(34), 13));
+ PatchSet.id(Change.id(34), 13));
changeMessage.setMessage("This is a change message.");
changeMessage.setTag("An arbitrary tag.");
- changeMessage.setRealAuthor(new Account.Id(10003));
+ changeMessage.setRealAuthor(Account.id(10003));
ChangeMessage convertedChangeMessage =
changeMessageProtoConverter.fromProto(changeMessageProtoConverter.toProto(changeMessage));
@@ -158,10 +154,10 @@ public class ChangeMessageProtoConverterTest {
public void mainValuesConvertedToProtoAndBackAgain() {
ChangeMessage changeMessage =
new ChangeMessage(
- new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
- new Account.Id(63),
+ ChangeMessage.key(Change.id(543), "change-message-21"),
+ Account.id(63),
new Timestamp(9876543),
- new PatchSet.Id(new Change.Id(34), 13));
+ PatchSet.id(Change.id(34), 13));
ChangeMessage convertedChangeMessage =
changeMessageProtoConverter.fromProto(changeMessageProtoConverter.toProto(changeMessage));
@@ -171,8 +167,7 @@ public class ChangeMessageProtoConverterTest {
@Test
public void mandatoryValuesConvertedToProtoAndBackAgain() {
ChangeMessage changeMessage =
- new ChangeMessage(
- new ChangeMessage.Key(new Change.Id(543), "change-message-21"), null, null, null);
+ new ChangeMessage(ChangeMessage.key(Change.id(543), "change-message-21"), null, null, null);
ChangeMessage convertedChangeMessage =
changeMessageProtoConverter.fromProto(changeMessageProtoConverter.toProto(changeMessage));
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/ChangeProtoConverterTest.java
index 61bf105d8b..72e4a7a8ed 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/ChangeProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/ChangeProtoConverterTest.java
@@ -12,20 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.protobuf.Parser;
import java.lang.reflect.Type;
import java.sql.Timestamp;
@@ -38,22 +38,22 @@ public class ChangeProtoConverterTest {
public void allValuesConvertedToProto() {
Change change =
new Change(
- new Change.Key("change 1"),
- new Change.Id(14),
- new Account.Id(35),
- new Branch.NameKey(new Project.NameKey("project 67"), "branch 74"),
+ Change.key("change 1"),
+ Change.id(14),
+ Account.id(35),
+ BranchNameKey.create(Project.nameKey("project 67"), "branch 74"),
new Timestamp(987654L));
change.setLastUpdatedOn(new Timestamp(1234567L));
change.setStatus(Change.Status.MERGED);
change.setCurrentPatchSet(
- new PatchSet.Id(new Change.Id(14), 23), "subject XYZ", "original subject ABC");
+ PatchSet.id(Change.id(14), 23), "subject XYZ", "original subject ABC");
change.setTopic("my topic");
change.setSubmissionId("submission ID 234");
- change.setAssignee(new Account.Id(100001));
+ change.setAssignee(Account.id(100001));
change.setPrivate(true);
change.setWorkInProgress(true);
change.setReviewStarted(true);
- change.setRevertOf(new Change.Id(180));
+ change.setRevertOf(Change.id(180));
Entities.Change proto = changeProtoConverter.toProto(change);
@@ -67,8 +67,8 @@ public class ChangeProtoConverterTest {
.setOwnerAccountId(Entities.Account_Id.newBuilder().setId(35))
.setDest(
Entities.Branch_NameKey.newBuilder()
- .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 67"))
- .setBranchName("refs/heads/branch 74"))
+ .setProject(Entities.Project_NameKey.newBuilder().setName("project 67"))
+ .setBranch("refs/heads/branch 74"))
.setStatus(Change.STATUS_MERGED)
.setCurrentPatchSetId(23)
.setSubject("subject XYZ")
@@ -88,10 +88,10 @@ public class ChangeProtoConverterTest {
public void mandatoryValuesConvertedToProto() {
Change change =
new Change(
- new Change.Key("change 1"),
- new Change.Id(14),
- new Account.Id(35),
- new Branch.NameKey(new Project.NameKey("project 67"), "branch-74"),
+ Change.key("change 1"),
+ Change.id(14),
+ Account.id(35),
+ BranchNameKey.create(Project.nameKey("project 67"), "branch-74"),
new Timestamp(987654L));
Entities.Change proto = changeProtoConverter.toProto(change);
@@ -106,8 +106,8 @@ public class ChangeProtoConverterTest {
.setOwnerAccountId(Entities.Account_Id.newBuilder().setId(35))
.setDest(
Entities.Branch_NameKey.newBuilder()
- .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 67"))
- .setBranchName("refs/heads/branch-74"))
+ .setProject(Entities.Project_NameKey.newBuilder().setName("project 67"))
+ .setBranch("refs/heads/branch-74"))
// Default values which can't be unset.
.setCurrentPatchSetId(0)
.setRowVersion(0)
@@ -124,13 +124,13 @@ public class ChangeProtoConverterTest {
public void currentPatchSetIsAlwaysSetWhenConvertedToProto() {
Change change =
new Change(
- new Change.Key("change 1"),
- new Change.Id(14),
- new Account.Id(35),
- new Branch.NameKey(new Project.NameKey("project 67"), "branch-74"),
+ Change.key("change 1"),
+ Change.id(14),
+ Account.id(35),
+ BranchNameKey.create(Project.nameKey("project 67"), "branch-74"),
new Timestamp(987654L));
// O as ID actually means that no current patch set is present.
- change.setCurrentPatchSet(new PatchSet.Id(new Change.Id(14), 0), null, null);
+ change.setCurrentPatchSet(PatchSet.id(Change.id(14), 0), null, null);
Entities.Change proto = changeProtoConverter.toProto(change);
@@ -144,8 +144,8 @@ public class ChangeProtoConverterTest {
.setOwnerAccountId(Entities.Account_Id.newBuilder().setId(35))
.setDest(
Entities.Branch_NameKey.newBuilder()
- .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 67"))
- .setBranchName("refs/heads/branch-74"))
+ .setProject(Entities.Project_NameKey.newBuilder().setName("project 67"))
+ .setBranch("refs/heads/branch-74"))
.setCurrentPatchSetId(0)
// Default values which can't be unset.
.setRowVersion(0)
@@ -162,12 +162,12 @@ public class ChangeProtoConverterTest {
public void originalSubjectIsNotAutomaticallySetToSubjectWhenConvertedToProto() {
Change change =
new Change(
- new Change.Key("change 1"),
- new Change.Id(14),
- new Account.Id(35),
- new Branch.NameKey(new Project.NameKey("project 67"), "branch-74"),
+ Change.key("change 1"),
+ Change.id(14),
+ Account.id(35),
+ BranchNameKey.create(Project.nameKey("project 67"), "branch-74"),
new Timestamp(987654L));
- change.setCurrentPatchSet(new PatchSet.Id(new Change.Id(14), 23), "subject ABC", null);
+ change.setCurrentPatchSet(PatchSet.id(Change.id(14), 23), "subject ABC", null);
Entities.Change proto = changeProtoConverter.toProto(change);
@@ -181,8 +181,8 @@ public class ChangeProtoConverterTest {
.setOwnerAccountId(Entities.Account_Id.newBuilder().setId(35))
.setDest(
Entities.Branch_NameKey.newBuilder()
- .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 67"))
- .setBranchName("refs/heads/branch-74"))
+ .setProject(Entities.Project_NameKey.newBuilder().setName("project 67"))
+ .setBranch("refs/heads/branch-74"))
.setCurrentPatchSetId(23)
.setSubject("subject ABC")
// Default values which can't be unset.
@@ -199,22 +199,22 @@ public class ChangeProtoConverterTest {
public void allValuesConvertedToProtoAndBackAgain() {
Change change =
new Change(
- new Change.Key("change 1"),
- new Change.Id(14),
- new Account.Id(35),
- new Branch.NameKey(new Project.NameKey("project 67"), "branch-74"),
+ Change.key("change 1"),
+ Change.id(14),
+ Account.id(35),
+ BranchNameKey.create(Project.nameKey("project 67"), "branch-74"),
new Timestamp(987654L));
change.setLastUpdatedOn(new Timestamp(1234567L));
change.setStatus(Change.Status.MERGED);
change.setCurrentPatchSet(
- new PatchSet.Id(new Change.Id(14), 23), "subject XYZ", "original subject ABC");
+ PatchSet.id(Change.id(14), 23), "subject XYZ", "original subject ABC");
change.setTopic("my topic");
change.setSubmissionId("submission ID 234");
- change.setAssignee(new Account.Id(100001));
+ change.setAssignee(Account.id(100001));
change.setPrivate(true);
change.setWorkInProgress(true);
change.setReviewStarted(true);
- change.setRevertOf(new Change.Id(180));
+ change.setRevertOf(Change.id(180));
Change convertedChange = changeProtoConverter.fromProto(changeProtoConverter.toProto(change));
assertEqualChange(convertedChange, change);
@@ -224,10 +224,10 @@ public class ChangeProtoConverterTest {
public void mandatoryValuesConvertedToProtoAndBackAgain() {
Change change =
new Change(
- new Change.Key("change 1"),
- new Change.Id(14),
- new Account.Id(35),
- new Branch.NameKey(new Project.NameKey("project 67"), "branch-74"),
+ Change.key("change 1"),
+ Change.id(14),
+ Account.id(35),
+ BranchNameKey.create(Project.nameKey("project 67"), "branch-74"),
new Timestamp(987654L));
Change convertedChange = changeProtoConverter.fromProto(changeProtoConverter.toProto(change));
@@ -269,8 +269,8 @@ public class ChangeProtoConverterTest {
.setOwnerAccountId(Entities.Account_Id.newBuilder().setId(35))
.setDest(
Entities.Branch_NameKey.newBuilder()
- .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 67"))
- .setBranchName("branch 74"))
+ .setProject(Entities.Project_NameKey.newBuilder().setName("project 67"))
+ .setBranch("branch 74"))
.build();
Change change = changeProtoConverter.fromProto(proto);
@@ -289,8 +289,8 @@ public class ChangeProtoConverterTest {
.setOwnerAccountId(Entities.Account_Id.newBuilder().setId(35))
.setDest(
Entities.Branch_NameKey.newBuilder()
- .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 67"))
- .setBranchName("branch 74"))
+ .setProject(Entities.Project_NameKey.newBuilder().setName("project 67"))
+ .setBranch("branch 74"))
.setStatus(Change.STATUS_MERGED)
.setCurrentPatchSetId(23)
.setSubject("subject XYZ")
@@ -323,7 +323,7 @@ public class ChangeProtoConverterTest {
.put("createdOn", Timestamp.class)
.put("lastUpdatedOn", Timestamp.class)
.put("owner", Account.Id.class)
- .put("dest", Branch.NameKey.class)
+ .put("dest", BranchNameKey.class)
.put("status", char.class)
.put("currentPatchSetId", int.class)
.put("subject", String.class)
diff --git a/javatests/com/google/gerrit/reviewdb/converter/LabelIdProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/LabelIdProtoConverterTest.java
index 41e0f3fdb9..88b9fb651b 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/LabelIdProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/LabelIdProtoConverterTest.java
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.LabelId;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.LabelId;
import com.google.protobuf.Parser;
import org.junit.Test;
@@ -30,7 +30,7 @@ public class LabelIdProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- LabelId labelId = new LabelId("Label ID 42");
+ LabelId labelId = LabelId.create("Label ID 42");
Entities.LabelId proto = labelIdProtoConverter.toProto(labelId);
@@ -40,7 +40,7 @@ public class LabelIdProtoConverterTest {
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- LabelId labelId = new LabelId("label-5");
+ LabelId labelId = LabelId.create("label-5");
LabelId convertedLabelId =
labelIdProtoConverter.fromProto(labelIdProtoConverter.toProto(labelId));
@@ -61,7 +61,8 @@ public class LabelIdProtoConverterTest {
/** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
- public void fieldsExistAsExpected() {
- assertThatSerializedClass(LabelId.class).hasFields(ImmutableMap.of("id", String.class));
+ public void methodsExistAsExpected() {
+ assertThatSerializedClass(LabelId.class)
+ .hasAutoValueMethods(ImmutableMap.of("id", String.class));
}
}
diff --git a/javatests/com/google/gerrit/entities/converter/ObjectIdProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/ObjectIdProtoConverterTest.java
new file mode 100644
index 0000000000..8408b69709
--- /dev/null
+++ b/javatests/com/google/gerrit/entities/converter/ObjectIdProtoConverterTest.java
@@ -0,0 +1,75 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.entities.converter;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.proto.Entities;
+import com.google.gerrit.proto.testing.SerializedClassSubject;
+import com.google.protobuf.Parser;
+import org.eclipse.jgit.lib.ObjectId;
+import org.junit.Test;
+
+public class ObjectIdProtoConverterTest {
+ private final ObjectIdProtoConverter objectIdProtoConverter = ObjectIdProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ ObjectId objectId = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+
+ Entities.ObjectId proto = objectIdProtoConverter.toProto(objectId);
+
+ Entities.ObjectId expectedProto =
+ Entities.ObjectId.newBuilder().setName("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef").build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ ObjectId objectId = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+
+ ObjectId convertedObjectId =
+ objectIdProtoConverter.fromProto(objectIdProtoConverter.toProto(objectId));
+
+ assertThat(convertedObjectId).isEqualTo(objectId);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.ObjectId proto =
+ Entities.ObjectId.newBuilder().setName("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef").build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.ObjectId> parser = objectIdProtoConverter.getParser();
+ Entities.ObjectId parsedProto = parser.parseFrom(bytes);
+
+ assertThat(parsedProto).isEqualTo(proto);
+ }
+
+ /** See {@link SerializedClassSubject} for background and what to do if this test fails. */
+ @Test
+ public void fieldsExistAsExpected() {
+ assertThatSerializedClass(ObjectId.class)
+ .hasFields(
+ ImmutableMap.of(
+ "w1", int.class,
+ "w2", int.class,
+ "w3", int.class,
+ "w4", int.class,
+ "w5", int.class));
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/PatchSetApprovalKeyProtoConverterTest.java
index d1ed419849..11aac0d08a 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/PatchSetApprovalKeyProtoConverterTest.java
@@ -12,20 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.protobuf.Parser;
import java.lang.reflect.Type;
import org.junit.Test;
@@ -37,8 +37,8 @@ public class PatchSetApprovalKeyProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
PatchSetApproval.Key key =
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(42), 14), new Account.Id(100013), new LabelId("label-8"));
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8"));
Entities.PatchSetApproval_Key proto = protoConverter.toProto(key);
@@ -47,9 +47,9 @@ public class PatchSetApprovalKeyProtoConverterTest {
.setPatchSetId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(42))
- .setPatchSetId(14))
+ .setId(14))
.setAccountId(Entities.Account_Id.newBuilder().setId(100013))
- .setCategoryId(Entities.LabelId.newBuilder().setId("label-8"))
+ .setLabelId(Entities.LabelId.newBuilder().setId("label-8"))
.build();
assertThat(proto).isEqualTo(expectedProto);
}
@@ -57,8 +57,8 @@ public class PatchSetApprovalKeyProtoConverterTest {
@Test
public void allValuesConvertedToProtoAndBackAgain() {
PatchSetApproval.Key key =
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(42), 14), new Account.Id(100013), new LabelId("label-8"));
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8"));
PatchSetApproval.Key convertedKey = protoConverter.fromProto(protoConverter.toProto(key));
@@ -72,9 +72,9 @@ public class PatchSetApprovalKeyProtoConverterTest {
.setPatchSetId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(42))
- .setPatchSetId(14))
+ .setId(14))
.setAccountId(Entities.Account_Id.newBuilder().setId(100013))
- .setCategoryId(Entities.LabelId.newBuilder().setId("label-8"))
+ .setLabelId(Entities.LabelId.newBuilder().setId("label-8"))
.build();
byte[] bytes = proto.toByteArray();
@@ -86,13 +86,13 @@ public class PatchSetApprovalKeyProtoConverterTest {
/** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
- public void fieldsExistAsExpected() {
+ public void methodsExistAsExpected() {
assertThatSerializedClass(PatchSetApproval.Key.class)
- .hasFields(
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("patchSetId", PatchSet.Id.class)
.put("accountId", Account.Id.class)
- .put("categoryId", LabelId.class)
+ .put("labelId", LabelId.class)
.build());
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverterTest.java
index 80b2cc2f4d..bca5eea9f4 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverterTest.java
@@ -12,24 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.inject.TypeLiteral;
import com.google.protobuf.Parser;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.util.Date;
+import java.util.Optional;
import org.junit.Test;
public class PatchSetApprovalProtoConverterTest {
@@ -39,16 +41,16 @@ public class PatchSetApprovalProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
PatchSetApproval patchSetApproval =
- new PatchSetApproval(
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(42), 14),
- new Account.Id(100013),
- new LabelId("label-8")),
- (short) 456,
- new Date(987654L));
- patchSetApproval.setTag("tag-21");
- patchSetApproval.setRealAccountId(new Account.Id(612));
- patchSetApproval.setPostSubmit(true);
+ PatchSetApproval.builder()
+ .key(
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8")))
+ .value(456)
+ .granted(new Date(987654L))
+ .tag("tag-21")
+ .realAccountId(Account.id(612))
+ .postSubmit(true)
+ .build();
Entities.PatchSetApproval proto = protoConverter.toProto(patchSetApproval);
@@ -59,9 +61,9 @@ public class PatchSetApprovalProtoConverterTest {
.setPatchSetId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(42))
- .setPatchSetId(14))
+ .setId(14))
.setAccountId(Entities.Account_Id.newBuilder().setId(100013))
- .setCategoryId(Entities.LabelId.newBuilder().setId("label-8")))
+ .setLabelId(Entities.LabelId.newBuilder().setId("label-8")))
.setValue(456)
.setGranted(987654L)
.setTag("tag-21")
@@ -74,13 +76,13 @@ public class PatchSetApprovalProtoConverterTest {
@Test
public void mandatoryValuesConvertedToProto() {
PatchSetApproval patchSetApproval =
- new PatchSetApproval(
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(42), 14),
- new Account.Id(100013),
- new LabelId("label-8")),
- (short) 456,
- new Date(987654L));
+ PatchSetApproval.builder()
+ .key(
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8")))
+ .value(456)
+ .granted(new Date(987654L))
+ .build();
Entities.PatchSetApproval proto = protoConverter.toProto(patchSetApproval);
@@ -91,9 +93,9 @@ public class PatchSetApprovalProtoConverterTest {
.setPatchSetId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(42))
- .setPatchSetId(14))
+ .setId(14))
.setAccountId(Entities.Account_Id.newBuilder().setId(100013))
- .setCategoryId(Entities.LabelId.newBuilder().setId("label-8")))
+ .setLabelId(Entities.LabelId.newBuilder().setId("label-8")))
.setValue(456)
.setGranted(987654L)
// This value can't be unset when our entity class is given.
@@ -105,16 +107,16 @@ public class PatchSetApprovalProtoConverterTest {
@Test
public void allValuesConvertedToProtoAndBackAgain() {
PatchSetApproval patchSetApproval =
- new PatchSetApproval(
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(42), 14),
- new Account.Id(100013),
- new LabelId("label-8")),
- (short) 456,
- new Date(987654L));
- patchSetApproval.setTag("tag-21");
- patchSetApproval.setRealAccountId(new Account.Id(612));
- patchSetApproval.setPostSubmit(true);
+ PatchSetApproval.builder()
+ .key(
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8")))
+ .value(456)
+ .granted(new Date(987654L))
+ .tag("tag-21")
+ .realAccountId(Account.id(612))
+ .postSubmit(true)
+ .build();
PatchSetApproval convertedPatchSetApproval =
protoConverter.fromProto(protoConverter.toProto(patchSetApproval));
@@ -124,13 +126,13 @@ public class PatchSetApprovalProtoConverterTest {
@Test
public void mandatoryValuesConvertedToProtoAndBackAgain() {
PatchSetApproval patchSetApproval =
- new PatchSetApproval(
- new PatchSetApproval.Key(
- new PatchSet.Id(new Change.Id(42), 14),
- new Account.Id(100013),
- new LabelId("label-8")),
- (short) 456,
- new Date(987654L));
+ PatchSetApproval.builder()
+ .key(
+ PatchSetApproval.key(
+ PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8")))
+ .value(456)
+ .granted(new Date(987654L))
+ .build();
PatchSetApproval convertedPatchSetApproval =
protoConverter.fromProto(protoConverter.toProto(patchSetApproval));
@@ -148,19 +150,19 @@ public class PatchSetApprovalProtoConverterTest {
.setPatchSetId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(42))
- .setPatchSetId(14))
+ .setId(14))
.setAccountId(Entities.Account_Id.newBuilder().setId(100013))
- .setCategoryId(Entities.LabelId.newBuilder().setId("label-8")))
+ .setLabelId(Entities.LabelId.newBuilder().setId("label-8")))
.build();
PatchSetApproval patchSetApproval = protoConverter.fromProto(proto);
- assertThat(patchSetApproval.getPatchSetId()).isEqualTo(new PatchSet.Id(new Change.Id(42), 14));
- assertThat(patchSetApproval.getAccountId()).isEqualTo(new Account.Id(100013));
- assertThat(patchSetApproval.getLabelId()).isEqualTo(new LabelId("label-8"));
+ assertThat(patchSetApproval.patchSetId()).isEqualTo(PatchSet.id(Change.id(42), 14));
+ assertThat(patchSetApproval.accountId()).isEqualTo(Account.id(100013));
+ assertThat(patchSetApproval.labelId()).isEqualTo(LabelId.create("label-8"));
// Default values for unset protobuf fields which can't be unset in the entity object.
- assertThat(patchSetApproval.getValue()).isEqualTo(0);
- assertThat(patchSetApproval.getGranted()).isEqualTo(new Timestamp(0));
- assertThat(patchSetApproval.isPostSubmit()).isEqualTo(false);
+ assertThat(patchSetApproval.value()).isEqualTo(0);
+ assertThat(patchSetApproval.granted()).isEqualTo(new Timestamp(0));
+ assertThat(patchSetApproval.postSubmit()).isEqualTo(false);
}
@Test
@@ -172,9 +174,9 @@ public class PatchSetApprovalProtoConverterTest {
.setPatchSetId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(42))
- .setPatchSetId(14))
+ .setId(14))
.setAccountId(Entities.Account_Id.newBuilder().setId(100013))
- .setCategoryId(Entities.LabelId.newBuilder().setId("label-8")))
+ .setLabelId(Entities.LabelId.newBuilder().setId("label-8")))
.setValue(456)
.setGranted(987654L)
.build();
@@ -190,14 +192,15 @@ public class PatchSetApprovalProtoConverterTest {
@Test
public void fieldsExistAsExpected() {
assertThatSerializedClass(PatchSetApproval.class)
- .hasFields(
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("key", PatchSetApproval.Key.class)
.put("value", short.class)
.put("granted", Timestamp.class)
- .put("tag", String.class)
+ .put("tag", new TypeLiteral<Optional<String>>() {}.getType())
.put("realAccountId", Account.Id.class)
.put("postSubmit", boolean.class)
+ .put("toBuilder", PatchSetApproval.Builder.class)
.build());
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/PatchSetIdProtoConverterTest.java
index 1598ef2ce5..530b43150a 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/PatchSetIdProtoConverterTest.java
@@ -12,17 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.protobuf.Parser;
import java.lang.reflect.Type;
import org.junit.Test;
@@ -33,21 +33,21 @@ public class PatchSetIdProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- PatchSet.Id patchSetId = new PatchSet.Id(new Change.Id(103), 73);
+ PatchSet.Id patchSetId = PatchSet.id(Change.id(103), 73);
Entities.PatchSet_Id proto = patchSetIdProtoConverter.toProto(patchSetId);
Entities.PatchSet_Id expectedProto =
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(103))
- .setPatchSetId(73)
+ .setId(73)
.build();
assertThat(proto).isEqualTo(expectedProto);
}
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- PatchSet.Id patchSetId = new PatchSet.Id(new Change.Id(20), 13);
+ PatchSet.Id patchSetId = PatchSet.id(Change.id(20), 13);
PatchSet.Id convertedPatchSetId =
patchSetIdProtoConverter.fromProto(patchSetIdProtoConverter.toProto(patchSetId));
@@ -60,7 +60,7 @@ public class PatchSetIdProtoConverterTest {
Entities.PatchSet_Id proto =
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(103))
- .setPatchSetId(73)
+ .setId(73)
.build();
byte[] bytes = proto.toByteArray();
@@ -72,12 +72,12 @@ public class PatchSetIdProtoConverterTest {
/** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
- public void fieldsExistAsExpected() {
+ public void methodsExistAsExpected() {
assertThatSerializedClass(PatchSet.Id.class)
- .hasFields(
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("changeId", Change.Id.class)
- .put("patchSetId", int.class)
+ .put("id", int.class)
.build());
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/PatchSetProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/PatchSetProtoConverterTest.java
index b8d2b1eb5a..2519e750cb 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/PatchSetProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/PatchSetProtoConverterTest.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
@@ -20,15 +20,17 @@ import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatS
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.truth.Truth;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.inject.TypeLiteral;
import com.google.protobuf.Parser;
import java.lang.reflect.Type;
import java.sql.Timestamp;
+import java.util.Optional;
+import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
public class PatchSetProtoConverterTest {
@@ -36,13 +38,16 @@ public class PatchSetProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- PatchSet patchSet = new PatchSet(new PatchSet.Id(new Change.Id(103), 73));
- patchSet.setRevision(new RevId("aabbccddeeff"));
- patchSet.setUploader(new Account.Id(452));
- patchSet.setCreatedOn(new Timestamp(930349320L));
- patchSet.setGroups(ImmutableList.of("group1, group2"));
- patchSet.setPushCertificate("my push certificate");
- patchSet.setDescription("This is a patch set description.");
+ PatchSet patchSet =
+ PatchSet.builder()
+ .id(PatchSet.id(Change.id(103), 73))
+ .commitId(ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))
+ .uploader(Account.id(452))
+ .createdOn(new Timestamp(930349320L))
+ .groups(ImmutableList.of("group1", " group2"))
+ .pushCertificate("my push certificate")
+ .description("This is a patch set description.")
+ .build();
Entities.PatchSet proto = patchSetProtoConverter.toProto(patchSet);
@@ -51,8 +56,9 @@ public class PatchSetProtoConverterTest {
.setId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(103))
- .setPatchSetId(73))
- .setRevision(Entities.RevId.newBuilder().setId("aabbccddeeff"))
+ .setId(73))
+ .setCommitId(
+ Entities.ObjectId.newBuilder().setName("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))
.setUploaderAccountId(Entities.Account_Id.newBuilder().setId(452))
.setCreatedOn(930349320L)
.setGroups("group1, group2")
@@ -64,7 +70,13 @@ public class PatchSetProtoConverterTest {
@Test
public void mandatoryValuesConvertedToProto() {
- PatchSet patchSet = new PatchSet(new PatchSet.Id(new Change.Id(103), 73));
+ PatchSet patchSet =
+ PatchSet.builder()
+ .id(PatchSet.id(Change.id(103), 73))
+ .commitId(ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))
+ .uploader(Account.id(452))
+ .createdOn(new Timestamp(930349320L))
+ .build();
Entities.PatchSet proto = patchSetProtoConverter.toProto(patchSet);
@@ -73,20 +85,27 @@ public class PatchSetProtoConverterTest {
.setId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(103))
- .setPatchSetId(73))
+ .setId(73))
+ .setCommitId(
+ Entities.ObjectId.newBuilder().setName("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))
+ .setUploaderAccountId(Entities.Account_Id.newBuilder().setId(452))
+ .setCreatedOn(930349320L)
.build();
assertThat(proto).isEqualTo(expectedProto);
}
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- PatchSet patchSet = new PatchSet(new PatchSet.Id(new Change.Id(103), 73));
- patchSet.setRevision(new RevId("aabbccddeeff"));
- patchSet.setUploader(new Account.Id(452));
- patchSet.setCreatedOn(new Timestamp(930349320L));
- patchSet.setGroups(ImmutableList.of("group1, group2"));
- patchSet.setPushCertificate("my push certificate");
- patchSet.setDescription("This is a patch set description.");
+ PatchSet patchSet =
+ PatchSet.builder()
+ .id(PatchSet.id(Change.id(103), 73))
+ .commitId(ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))
+ .uploader(Account.id(452))
+ .createdOn(new Timestamp(930349320L))
+ .groups(ImmutableList.of("group1", " group2"))
+ .pushCertificate("my push certificate")
+ .description("This is a patch set description.")
+ .build();
PatchSet convertedPatchSet =
patchSetProtoConverter.fromProto(patchSetProtoConverter.toProto(patchSet));
@@ -95,7 +114,13 @@ public class PatchSetProtoConverterTest {
@Test
public void mandatoryValuesConvertedToProtoAndBackAgain() {
- PatchSet patchSet = new PatchSet(new PatchSet.Id(new Change.Id(103), 73));
+ PatchSet patchSet =
+ PatchSet.builder()
+ .id(PatchSet.id(Change.id(103), 73))
+ .commitId(ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))
+ .uploader(Account.id(452))
+ .createdOn(new Timestamp(930349320L))
+ .build();
PatchSet convertedPatchSet =
patchSetProtoConverter.fromProto(patchSetProtoConverter.toProto(patchSet));
@@ -103,13 +128,34 @@ public class PatchSetProtoConverterTest {
}
@Test
+ public void previouslyOptionalValuesMayBeMissingFromProto() {
+ Entities.PatchSet proto =
+ Entities.PatchSet.newBuilder()
+ .setId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(103))
+ .setId(73))
+ .build();
+
+ PatchSet convertedPatchSet = patchSetProtoConverter.fromProto(proto);
+ Truth.assertThat(convertedPatchSet)
+ .isEqualTo(
+ PatchSet.builder()
+ .id(PatchSet.id(Change.id(103), 73))
+ .commitId(ObjectId.fromString("0000000000000000000000000000000000000000"))
+ .uploader(Account.id(0))
+ .createdOn(new Timestamp(0))
+ .build());
+ }
+
+ @Test
public void protoCanBeParsedFromBytes() throws Exception {
Entities.PatchSet proto =
Entities.PatchSet.newBuilder()
.setId(
Entities.PatchSet_Id.newBuilder()
.setChangeId(Entities.Change_Id.newBuilder().setId(103))
- .setPatchSetId(73))
+ .setId(73))
.build();
byte[] bytes = proto.toByteArray();
@@ -123,15 +169,15 @@ public class PatchSetProtoConverterTest {
@Test
public void fieldsExistAsExpected() {
assertThatSerializedClass(PatchSet.class)
- .hasFields(
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("id", PatchSet.Id.class)
- .put("revision", RevId.class)
+ .put("commitId", ObjectId.class)
.put("uploader", Account.Id.class)
.put("createdOn", Timestamp.class)
- .put("groups", String.class)
- .put("pushCertificate", String.class)
- .put("description", String.class)
+ .put("groups", new TypeLiteral<ImmutableList<String>>() {}.getType())
+ .put("pushCertificate", new TypeLiteral<Optional<String>>() {}.getType())
+ .put("description", new TypeLiteral<Optional<String>>() {}.getType())
.build());
}
}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/ProjectNameKeyProtoConverterTest.java
index 2ad610712e..2f693e6ba4 100644
--- a/javatests/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/ProjectNameKeyProtoConverterTest.java
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.reviewdb.converter;
+package com.google.gerrit.entities.converter;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.protobuf.Parser;
import org.junit.Test;
@@ -31,7 +31,7 @@ public class ProjectNameKeyProtoConverterTest {
@Test
public void allValuesConvertedToProto() {
- Project.NameKey nameKey = new Project.NameKey("project-72");
+ Project.NameKey nameKey = Project.nameKey("project-72");
Entities.Project_NameKey proto = projectNameKeyProtoConverter.toProto(nameKey);
@@ -42,7 +42,7 @@ public class ProjectNameKeyProtoConverterTest {
@Test
public void allValuesConvertedToProtoAndBackAgain() {
- Project.NameKey nameKey = new Project.NameKey("project-52");
+ Project.NameKey nameKey = Project.nameKey("project-52");
Project.NameKey convertedNameKey =
projectNameKeyProtoConverter.fromProto(projectNameKeyProtoConverter.toProto(nameKey));
diff --git a/javatests/com/google/gerrit/extensions/BUILD b/javatests/com/google/gerrit/extensions/BUILD
index 94e433c346..2202a1168a 100644
--- a/javatests/com/google/gerrit/extensions/BUILD
+++ b/javatests/com/google/gerrit/extensions/BUILD
@@ -7,7 +7,6 @@ junit_tests(
deps = [
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/extensions/common/testing:common-test-util",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
"//lib/guice",
"//lib/truth",
diff --git a/javatests/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java b/javatests/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java
index 86dce049d3..0be10eecdc 100644
--- a/javatests/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java
+++ b/javatests/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java
@@ -16,12 +16,11 @@ package com.google.gerrit.extensions.api.lfs;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Test;
-public class LfsDefinitionsTest extends GerritBaseTests {
+public class LfsDefinitionsTest {
private static final String[] URL_PREFIXES = new String[] {"/", "/a/", "/p/", "/a/p/"};
@Test
diff --git a/javatests/com/google/gerrit/extensions/client/ListOptionTest.java b/javatests/com/google/gerrit/extensions/client/ListOptionTest.java
index 4bb91079cb..5e8c7b6f4e 100644
--- a/javatests/com/google/gerrit/extensions/client/ListOptionTest.java
+++ b/javatests/com/google/gerrit/extensions/client/ListOptionTest.java
@@ -21,11 +21,10 @@ import static com.google.gerrit.extensions.client.ListOptionTest.MyOption.BAZ;
import static com.google.gerrit.extensions.client.ListOptionTest.MyOption.FOO;
import com.google.common.math.IntMath;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.EnumSet;
import org.junit.Test;
-public class ListOptionTest extends GerritBaseTests {
+public class ListOptionTest {
enum MyOption implements ListOption {
FOO(0),
BAR(1),
diff --git a/javatests/com/google/gerrit/extensions/client/RangeTest.java b/javatests/com/google/gerrit/extensions/client/RangeTest.java
index 2c713b5dae..b8938aa50c 100644
--- a/javatests/com/google/gerrit/extensions/client/RangeTest.java
+++ b/javatests/com/google/gerrit/extensions/client/RangeTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.extensions.client;
import static com.google.gerrit.extensions.common.testing.RangeSubject.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class RangeTest extends GerritBaseTests {
+public class RangeTest {
@Test
public void rangeOverMultipleLinesWithSmallerEndCharacterIsValid() {
diff --git a/javatests/com/google/gerrit/extensions/conditions/BUILD b/javatests/com/google/gerrit/extensions/conditions/BUILD
index 7ad2ad3239..e2d595113f 100644
--- a/javatests/com/google/gerrit/extensions/conditions/BUILD
+++ b/javatests/com/google/gerrit/extensions/conditions/BUILD
@@ -5,7 +5,6 @@ junit_tests(
srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/extensions:lib",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/extensions/conditions/BooleanConditionTest.java b/javatests/com/google/gerrit/extensions/conditions/BooleanConditionTest.java
index 81cb719e81..f9f1fa85fc 100644
--- a/javatests/com/google/gerrit/extensions/conditions/BooleanConditionTest.java
+++ b/javatests/com/google/gerrit/extensions/conditions/BooleanConditionTest.java
@@ -20,10 +20,9 @@ import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
import static com.google.gerrit.extensions.conditions.BooleanCondition.valueOf;
import static org.junit.Assert.assertEquals;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class BooleanConditionTest extends GerritBaseTests {
+public class BooleanConditionTest {
private static final BooleanCondition NO_TRIVIAL_EVALUATION =
new BooleanCondition() {
diff --git a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
index d9502247ac..0542c35d1c 100644
--- a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
+++ b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
@@ -17,14 +17,13 @@ package com.google.gerrit.extensions.registration;
import static com.google.common.truth.Truth.assertThat;
import static java.util.stream.Collectors.toSet;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.util.Providers;
import java.util.Iterator;
import org.junit.Test;
-public class DynamicSetTest extends GerritBaseTests {
+public class DynamicSetTest {
// In tests for {@link DynamicSet#contains(Object)}, be sure to avoid
// {@code assertThat(ds).contains(...) @} and
// {@code assertThat(ds).DoesNotContains(...) @} as (since
diff --git a/javatests/com/google/gerrit/git/BUILD b/javatests/com/google/gerrit/git/BUILD
index d57d73fdcf..c2c9ccef99 100644
--- a/javatests/com/google/gerrit/git/BUILD
+++ b/javatests/com/google/gerrit/git/BUILD
@@ -10,11 +10,10 @@ junit_tests(
tags = ["no_windows"],
deps = [
"//java/com/google/gerrit/git",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib:junit",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/truth",
],
)
@@ -30,7 +29,8 @@ junit_tests(
"//java/com/google/gerrit/git",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/git/ObjectIdsTest.java b/javatests/com/google/gerrit/git/ObjectIdsTest.java
new file mode 100644
index 0000000000..b254d6f78a
--- /dev/null
+++ b/javatests/com/google/gerrit/git/ObjectIdsTest.java
@@ -0,0 +1,156 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.git;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.git.ObjectIds.abbreviateName;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
+
+import java.util.function.Function;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.junit.Test;
+
+public class ObjectIdsTest {
+ private static final ObjectId ID =
+ ObjectId.fromString("0000000000100000000000000000000000000000");
+ private static final ObjectId AMBIGUOUS_BLOB_ID =
+ ObjectId.fromString("0000000000b36b6aa7ea4b75318ed078f55505c3");
+ private static final ObjectId AMBIGUOUS_TREE_ID =
+ ObjectId.fromString("0000000000cdcf04beb2fab69e65622616294984");
+
+ @Test
+ public void abbreviateNameDefaultLength() throws Exception {
+ assertRuntimeException(() -> abbreviateName(null));
+ assertThat(abbreviateName(ID)).isEqualTo("0000000");
+ assertThat(abbreviateName(AMBIGUOUS_BLOB_ID)).isEqualTo(abbreviateName(ID));
+ assertThat(abbreviateName(AMBIGUOUS_TREE_ID)).isEqualTo(abbreviateName(ID));
+ }
+
+ @Test
+ public void abbreviateNameCustomLength() throws Exception {
+ assertRuntimeException(() -> abbreviateName(null, 1));
+ assertRuntimeException(() -> abbreviateName(ID, -1));
+ assertRuntimeException(() -> abbreviateName(ID, 0));
+ assertRuntimeException(() -> abbreviateName(ID, 41));
+ assertThat(abbreviateName(ID, 5)).isEqualTo("00000");
+ assertThat(abbreviateName(ID, 40)).isEqualTo(ID.name());
+ }
+
+ @Test
+ public void abbreviateNameDefaultLengthWithReader() throws Exception {
+ assertRuntimeException(() -> abbreviateName(ID, null));
+
+ ObjectReader reader = newReaderWithAmbiguousIds();
+ assertThat(abbreviateName(ID, reader)).isEqualTo("00000000001");
+ }
+
+ @Test
+ public void abbreviateNameCustomLengthWithReader() throws Exception {
+ ObjectReader reader = newReaderWithAmbiguousIds();
+ assertRuntimeException(() -> abbreviateName(ID, -1, reader));
+ assertRuntimeException(() -> abbreviateName(ID, 0, reader));
+ assertRuntimeException(() -> abbreviateName(ID, 41, reader));
+ assertRuntimeException(() -> abbreviateName(ID, 5, null));
+
+ String shortest = "00000000001";
+ assertThat(abbreviateName(ID, 1, reader)).isEqualTo(shortest);
+ assertThat(abbreviateName(ID, 7, reader)).isEqualTo(shortest);
+ assertThat(abbreviateName(ID, shortest.length(), reader)).isEqualTo(shortest);
+ assertThat(abbreviateName(ID, shortest.length() + 1, reader)).isEqualTo("000000000010");
+ }
+
+ @Test
+ public void copyOrNull() throws Exception {
+ testCopy(ObjectIds::copyOrNull);
+ assertThat(ObjectIds.copyOrNull(null)).isNull();
+ }
+
+ @Test
+ public void copyOrZero() throws Exception {
+ testCopy(ObjectIds::copyOrZero);
+ assertThat(ObjectIds.copyOrZero(null)).isEqualTo(ObjectId.zeroId());
+ }
+
+ private void testCopy(Function<AnyObjectId, ObjectId> copyFunc) {
+ MyObjectId myId = new MyObjectId(ID);
+ assertThat(myId).isEqualTo(ID);
+
+ ObjectId copy = copyFunc.apply(myId);
+ assertThat(copy).isEqualTo(myId);
+ assertThat(copy).isNotSameInstanceAs(myId);
+ assertThat(copy.getClass()).isEqualTo(ObjectId.class);
+ }
+
+ @Test
+ public void matchesAbbreviation() throws Exception {
+ assertThat(ObjectIds.matchesAbbreviation(null, "")).isFalse();
+ assertThat(ObjectIds.matchesAbbreviation(null, "0")).isFalse();
+ assertThat(ObjectIds.matchesAbbreviation(null, "00000")).isFalse();
+ assertThat(ObjectIds.matchesAbbreviation(null, "not a SHA-1")).isFalse();
+ assertThat(ObjectIds.matchesAbbreviation(null, ID.name())).isFalse();
+
+ assertThat(ObjectIds.matchesAbbreviation(ID, "")).isTrue();
+ for (int i = 1; i <= OBJECT_ID_STRING_LENGTH; i++) {
+ String prefix = ID.name().substring(0, i);
+ assertWithMessage("match %s against %s", ID.name(), prefix)
+ .that(ObjectIds.matchesAbbreviation(ID, prefix))
+ .isTrue();
+ }
+
+ assertThat(ObjectIds.matchesAbbreviation(ID, "1")).isFalse();
+ assertThat(ObjectIds.matchesAbbreviation(ID, "x")).isFalse();
+ assertThat(ObjectIds.matchesAbbreviation(ID, "not a SHA-1")).isFalse();
+ assertThat(ObjectIds.matchesAbbreviation(ID, AMBIGUOUS_BLOB_ID.name())).isFalse();
+ }
+
+ @FunctionalInterface
+ private interface Func {
+ void call() throws Exception;
+ }
+
+ private static void assertRuntimeException(Func func) throws Exception {
+ assertThrows(RuntimeException.class, () -> func.call());
+ }
+
+ private static ObjectReader newReaderWithAmbiguousIds() throws Exception {
+ // Recipe for creating ambiguous IDs courtesy of git core:
+ // https://github.com/git/git/blob/df799f5d99ac51d4fc791d546de3f936088582fc/t/t1512-rev-parse-disambiguation.sh
+ try (TestRepository<Repository> tr =
+ new TestRepository<>(new InMemoryRepository(new DfsRepositoryDescription("repo")))) {
+ String blobData = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n\nb1rwzyc3\n";
+ RevBlob blob = tr.blob(blobData);
+ assertThat(blob.name()).isEqualTo(AMBIGUOUS_BLOB_ID.name());
+ assertThat(tr.tree(tr.file("a0blgqsjc", blob)).name()).isEqualTo(AMBIGUOUS_TREE_ID.name());
+ return tr.getRevWalk().getObjectReader();
+ }
+ }
+
+ private static class MyObjectId extends ObjectId {
+ private static final long serialVersionUID = 1L;
+
+ MyObjectId(AnyObjectId src) {
+ super(src);
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java b/javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java
index 59961ff3e7..60b90f3985 100644
--- a/javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java
+++ b/javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java
@@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.io.MoreFiles;
import com.google.common.io.RecursiveDeleteOption;
-import com.google.gerrit.testing.GerritBaseTests;
import java.nio.file.Files;
import java.nio.file.Path;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
@@ -36,7 +35,7 @@ import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class RefUpdateUtilRepoTest extends GerritBaseTests {
+public class RefUpdateUtilRepoTest {
public enum RepoSetup {
LOCAL_DISK {
@Override
diff --git a/javatests/com/google/gerrit/git/RefUpdateUtilTest.java b/javatests/com/google/gerrit/git/RefUpdateUtilTest.java
index 429583a588..1d021f7ab9 100644
--- a/javatests/com/google/gerrit/git/RefUpdateUtilTest.java
+++ b/javatests/com/google/gerrit/git/RefUpdateUtilTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.git;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import java.util.function.Consumer;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
@@ -33,7 +32,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
-public class RefUpdateUtilTest extends GerritBaseTests {
+public class RefUpdateUtilTest {
private static final Consumer<ReceiveCommand> OK = c -> c.setResult(ReceiveCommand.Result.OK);
private static final Consumer<ReceiveCommand> LOCK_FAILURE =
c -> c.setResult(ReceiveCommand.Result.LOCK_FAILURE);
@@ -81,23 +80,18 @@ public class RefUpdateUtilTest extends GerritBaseTests {
@SafeVarargs
private static void assertIoException(Consumer<ReceiveCommand>... resultSetters) {
- try {
- RefUpdateUtil.checkResults(newBatchRefUpdate(resultSetters));
- assert_().fail("expected IOException");
- } catch (IOException e) {
- assertThat(e).isNotInstanceOf(LockFailureException.class);
- }
+ IOException thrown =
+ assertThrows(
+ IOException.class, () -> RefUpdateUtil.checkResults(newBatchRefUpdate(resultSetters)));
+ assertThat(thrown).isNotInstanceOf(LockFailureException.class);
}
@SafeVarargs
private static void assertLockFailureException(Consumer<ReceiveCommand>... resultSetters)
throws Exception {
- try {
- RefUpdateUtil.checkResults(newBatchRefUpdate(resultSetters));
- assert_().fail("expected LockFailureException");
- } catch (LockFailureException e) {
- // Expected.
- }
+ assertThrows(
+ LockFailureException.class,
+ () -> RefUpdateUtil.checkResults(newBatchRefUpdate(resultSetters)));
}
@SafeVarargs
diff --git a/javatests/com/google/gerrit/git/testing/BUILD b/javatests/com/google/gerrit/git/testing/BUILD
index 13091853b2..56e9ec29c0 100644
--- a/javatests/com/google/gerrit/git/testing/BUILD
+++ b/javatests/com/google/gerrit/git/testing/BUILD
@@ -5,7 +5,6 @@ junit_tests(
srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/git/testing",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/git/testing/PushResultSubjectTest.java b/javatests/com/google/gerrit/git/testing/PushResultSubjectTest.java
index 5ab52d4e01..3bf815b211 100644
--- a/javatests/com/google/gerrit/git/testing/PushResultSubjectTest.java
+++ b/javatests/com/google/gerrit/git/testing/PushResultSubjectTest.java
@@ -18,10 +18,9 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.git.testing.PushResultSubject.parseProcessed;
import static com.google.gerrit.git.testing.PushResultSubject.trimMessages;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class PushResultSubjectTest extends GerritBaseTests {
+public class PushResultSubjectTest {
@Test
public void testTrimMessages() {
assertThat(trimMessages(null)).isNull();
diff --git a/javatests/com/google/gerrit/gpg/BUILD b/javatests/com/google/gerrit/gpg/BUILD
index f73208d8b6..f459b1a51c 100644
--- a/javatests/com/google/gerrit/gpg/BUILD
+++ b/javatests/com/google/gerrit/gpg/BUILD
@@ -5,16 +5,19 @@ junit_tests(
srcs = glob(["**/*.java"]),
tags = ["no_windows"],
visibility = ["//visibility:public"],
+ runtime_deps = ["//java/com/google/gerrit/lucene"],
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/gpg",
"//java/com/google/gerrit/gpg/testing:gpg-test-util",
"//java/com/google/gerrit/lifecycle",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib/bouncycastle:bcpg",
"//lib/bouncycastle:bcpg-neverlink",
"//lib/bouncycastle:bcprov",
@@ -22,8 +25,6 @@ junit_tests(
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
index 220361e205..45b34199b7 100644
--- a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
@@ -29,10 +29,10 @@ import static org.eclipse.jgit.lib.RefUpdate.Result.NEW;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.common.GpgKeyInfo.Status;
import com.google.gerrit.gpg.testing.TestKey;
import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountManager;
@@ -41,7 +41,6 @@ import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -64,7 +63,7 @@ import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link GerritPublicKeyChecker}. */
-public class GerritPublicKeyCheckerTest extends GerritBaseTests {
+public class GerritPublicKeyCheckerTest {
@Inject @ServerInitiated private Provider<AccountsUpdate> accountsUpdateProvider;
@Inject private AccountManager accountManager;
@@ -199,7 +198,7 @@ public class GerritPublicKeyCheckerTest extends GerritBaseTests {
.update(
"Delete External IDs",
user.getAccountId(),
- (a, u) -> u.deleteExternalIds(a.getExternalIds()));
+ (a, u) -> u.deleteExternalIds(a.externalIds()));
reloadUser();
TestKey key = validKeyWithSecondUserId();
diff --git a/javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java b/javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java
index 145b9cff3f..7703fb08d5 100644
--- a/javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java
@@ -38,7 +38,6 @@ import static org.bouncycastle.openpgp.PGPSignature.DIRECT_KEY;
import static org.junit.Assert.assertEquals;
import com.google.gerrit.gpg.testing.TestKey;
-import com.google.gerrit.testing.GerritBaseTests;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -60,7 +59,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class PublicKeyCheckerTest extends GerritBaseTests {
+public class PublicKeyCheckerTest {
private InMemoryRepository repo;
private PublicKeyStore store;
diff --git a/javatests/com/google/gerrit/gpg/PublicKeyStoreTest.java b/javatests/com/google/gerrit/gpg/PublicKeyStoreTest.java
index be657528b4..3727d38afd 100644
--- a/javatests/com/google/gerrit/gpg/PublicKeyStoreTest.java
+++ b/javatests/com/google/gerrit/gpg/PublicKeyStoreTest.java
@@ -29,7 +29,6 @@ import static org.junit.Assert.assertTrue;
import com.google.common.collect.Iterators;
import com.google.gerrit.gpg.testing.TestKey;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -51,7 +50,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
import org.junit.Test;
-public class PublicKeyStoreTest extends GerritBaseTests {
+public class PublicKeyStoreTest {
private TestRepository<?> tr;
private PublicKeyStore store;
diff --git a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
index 67bf0505c3..266f8684fa 100644
--- a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
@@ -23,7 +23,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import com.google.gerrit.gpg.testing.TestKey;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
@@ -54,7 +53,7 @@ import org.eclipse.jgit.transport.SignedPushConfig;
import org.junit.Before;
import org.junit.Test;
-public class PushCertificateCheckerTest extends GerritBaseTests {
+public class PushCertificateCheckerTest {
private InMemoryRepository repo;
private PublicKeyStore store;
private SignedPushConfig signedPushConfig;
diff --git a/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java b/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
index e2c58d810f..49322485c4 100644
--- a/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
+++ b/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
@@ -14,14 +14,16 @@
package com.google.gerrit.httpd;
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.capture;
-import static org.easymock.EasyMock.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
import com.google.gerrit.server.plugins.Plugin;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
import com.google.gerrit.util.http.testutil.FakeHttpServletResponse;
import com.google.inject.Key;
@@ -30,13 +32,12 @@ import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.easymock.Capture;
-import org.easymock.EasyMockSupport;
-import org.easymock.IMocksControl;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
-public class AllRequestFilterFilterProxyTest extends GerritBaseTests {
+public class AllRequestFilterFilterProxyTest {
/**
* Set of filters for FilterProxy
*
@@ -82,16 +83,11 @@ public class AllRequestFilterFilterProxyTest extends GerritBaseTests {
@Test
public void noFilters() throws Exception {
- EasyMockSupport ems = new EasyMockSupport();
-
- FilterConfig config = ems.createMock(FilterConfig.class);
+ FilterConfig config = mock(FilterConfig.class);
HttpServletRequest req = new FakeHttpServletRequest();
HttpServletResponse res = new FakeHttpServletResponse();
- FilterChain chain = ems.createMock(FilterChain.class);
- chain.doFilter(req, res);
-
- ems.replayAll();
+ FilterChain chain = mock(FilterChain.class);
AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
@@ -99,25 +95,18 @@ public class AllRequestFilterFilterProxyTest extends GerritBaseTests {
filterProxy.doFilter(req, res, chain);
filterProxy.destroy();
- ems.verifyAll();
+ verify(chain).doFilter(req, res);
}
@Test
public void singleFilterNoBubbling() throws Exception {
- EasyMockSupport ems = new EasyMockSupport();
-
- FilterConfig config = ems.createMock("config", FilterConfig.class);
+ FilterConfig config = mock(FilterConfig.class);
HttpServletRequest req = new FakeHttpServletRequest();
HttpServletResponse res = new FakeHttpServletResponse();
- FilterChain chain = ems.createMock("chain", FilterChain.class);
+ FilterChain chain = mock(FilterChain.class);
- AllRequestFilter filter = ems.createStrictMock("filter", AllRequestFilter.class);
- filter.init(config);
- filter.doFilter(eq(req), eq(res), anyObject(FilterChain.class));
- filter.destroy();
-
- ems.replayAll();
+ AllRequestFilter filter = mock(AllRequestFilter.class);
AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
addFilter(filter);
@@ -126,63 +115,52 @@ public class AllRequestFilterFilterProxyTest extends GerritBaseTests {
filterProxy.doFilter(req, res, chain);
filterProxy.destroy();
- ems.verifyAll();
+ InOrder inorder = inOrder(filter);
+ inorder.verify(filter).init(config);
+ inorder.verify(filter).doFilter(eq(req), eq(res), any(FilterChain.class));
+ inorder.verify(filter).destroy();
}
@Test
public void singleFilterBubbling() throws Exception {
- EasyMockSupport ems = new EasyMockSupport();
-
- FilterConfig config = ems.createMock(FilterConfig.class);
+ FilterConfig config = mock(FilterConfig.class);
HttpServletRequest req = new FakeHttpServletRequest();
HttpServletResponse res = new FakeHttpServletResponse();
- IMocksControl mockControl = ems.createStrictControl();
- FilterChain chain = mockControl.createMock(FilterChain.class);
-
- Capture<FilterChain> capturedChain = new Capture<>();
+ FilterChain chain = mock(FilterChain.class);
- AllRequestFilter filter = mockControl.createMock(AllRequestFilter.class);
- filter.init(config);
- filter.doFilter(eq(req), eq(res), capture(capturedChain));
- chain.doFilter(req, res);
- filter.destroy();
+ ArgumentCaptor<FilterChain> capturedChain = ArgumentCaptor.forClass(FilterChain.class);
- ems.replayAll();
+ AllRequestFilter filter = mock(AllRequestFilter.class);
AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
addFilter(filter);
+ InOrder inorder = inOrder(filter, chain);
+
filterProxy.init(config);
filterProxy.doFilter(req, res, chain);
+
+ inorder.verify(filter).init(config);
+ inorder.verify(filter).doFilter(eq(req), eq(res), capturedChain.capture());
capturedChain.getValue().doFilter(req, res);
- filterProxy.destroy();
+ inorder.verify(chain).doFilter(req, res);
- ems.verifyAll();
+ filterProxy.destroy();
+ inorder.verify(filter).destroy();
}
@Test
public void twoFiltersNoBubbling() throws Exception {
- EasyMockSupport ems = new EasyMockSupport();
-
- FilterConfig config = ems.createMock(FilterConfig.class);
+ FilterConfig config = mock(FilterConfig.class);
HttpServletRequest req = new FakeHttpServletRequest();
HttpServletResponse res = new FakeHttpServletResponse();
- IMocksControl mockControl = ems.createStrictControl();
- FilterChain chain = mockControl.createMock(FilterChain.class);
+ FilterChain chain = mock(FilterChain.class);
- AllRequestFilter filterA = mockControl.createMock(AllRequestFilter.class);
-
- AllRequestFilter filterB = mockControl.createMock(AllRequestFilter.class);
- filterA.init(config);
- filterB.init(config);
- filterA.doFilter(eq(req), eq(res), anyObject(FilterChain.class));
- filterA.destroy();
- filterB.destroy();
-
- ems.replayAll();
+ AllRequestFilter filterA = mock(AllRequestFilter.class);
+ AllRequestFilter filterB = mock(AllRequestFilter.class);
AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
addFilter(filterA);
addFilter(filterB);
@@ -191,35 +169,27 @@ public class AllRequestFilterFilterProxyTest extends GerritBaseTests {
filterProxy.doFilter(req, res, chain);
filterProxy.destroy();
- ems.verifyAll();
+ InOrder inorder = inOrder(filterA, filterB);
+ inorder.verify(filterA).init(config);
+ inorder.verify(filterB).init(config);
+ inorder.verify(filterA).doFilter(eq(req), eq(res), any(FilterChain.class));
+ inorder.verify(filterA).destroy();
+ inorder.verify(filterB).destroy();
}
@Test
public void twoFiltersBubbling() throws Exception {
- EasyMockSupport ems = new EasyMockSupport();
-
- FilterConfig config = ems.createMock(FilterConfig.class);
+ FilterConfig config = mock(FilterConfig.class);
HttpServletRequest req = new FakeHttpServletRequest();
HttpServletResponse res = new FakeHttpServletResponse();
- IMocksControl mockControl = ems.createStrictControl();
- FilterChain chain = mockControl.createMock(FilterChain.class);
-
- Capture<FilterChain> capturedChainA = new Capture<>();
- Capture<FilterChain> capturedChainB = new Capture<>();
-
- AllRequestFilter filterA = mockControl.createMock(AllRequestFilter.class);
- AllRequestFilter filterB = mockControl.createMock(AllRequestFilter.class);
+ FilterChain chain = mock(FilterChain.class);
- filterA.init(config);
- filterB.init(config);
- filterA.doFilter(eq(req), eq(res), capture(capturedChainA));
- filterB.doFilter(eq(req), eq(res), capture(capturedChainB));
- chain.doFilter(req, res);
- filterA.destroy();
- filterB.destroy();
+ ArgumentCaptor<FilterChain> capturedChainA = ArgumentCaptor.forClass(FilterChain.class);
+ ArgumentCaptor<FilterChain> capturedChainB = ArgumentCaptor.forClass(FilterChain.class);
- ems.replayAll();
+ AllRequestFilter filterA = mock(AllRequestFilter.class);
+ AllRequestFilter filterB = mock(AllRequestFilter.class);
AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
addFilter(filterA);
@@ -227,70 +197,69 @@ public class AllRequestFilterFilterProxyTest extends GerritBaseTests {
filterProxy.init(config);
filterProxy.doFilter(req, res, chain);
+
+ InOrder inorder = inOrder(filterA, filterB, chain);
+
+ inorder.verify(filterA).init(config);
+ inorder.verify(filterB).init(config);
+ inorder.verify(filterA).doFilter(eq(req), eq(res), capturedChainA.capture());
capturedChainA.getValue().doFilter(req, res);
+ inorder.verify(filterB).doFilter(eq(req), eq(res), capturedChainB.capture());
capturedChainB.getValue().doFilter(req, res);
- filterProxy.destroy();
+ inorder.verify(chain).doFilter(req, res);
- ems.verifyAll();
+ filterProxy.destroy();
+ inorder.verify(filterA).destroy();
+ inorder.verify(filterB).destroy();
}
@Test
public void postponedLoading() throws Exception {
- EasyMockSupport ems = new EasyMockSupport();
-
- FilterConfig config = ems.createMock(FilterConfig.class);
+ FilterConfig config = mock(FilterConfig.class);
HttpServletRequest req1 = new FakeHttpServletRequest();
HttpServletRequest req2 = new FakeHttpServletRequest();
HttpServletResponse res1 = new FakeHttpServletResponse();
HttpServletResponse res2 = new FakeHttpServletResponse();
- IMocksControl mockControl = ems.createStrictControl();
- FilterChain chain = mockControl.createMock("chain", FilterChain.class);
+ FilterChain chain = mock(FilterChain.class);
- Capture<FilterChain> capturedChainA1 = new Capture<>();
- Capture<FilterChain> capturedChainA2 = new Capture<>();
- Capture<FilterChain> capturedChainB = new Capture<>();
+ ArgumentCaptor<FilterChain> capturedChainA1 = ArgumentCaptor.forClass(FilterChain.class);
+ ArgumentCaptor<FilterChain> capturedChainA2 = ArgumentCaptor.forClass(FilterChain.class);
+ ArgumentCaptor<FilterChain> capturedChainB = ArgumentCaptor.forClass(FilterChain.class);
- AllRequestFilter filterA = mockControl.createMock("filterA", AllRequestFilter.class);
- AllRequestFilter filterB = mockControl.createMock("filterB", AllRequestFilter.class);
+ AllRequestFilter filterA = mock(AllRequestFilter.class);
+ AllRequestFilter filterB = mock(AllRequestFilter.class);
- filterA.init(config);
- filterA.doFilter(eq(req1), eq(res1), capture(capturedChainA1));
- chain.doFilter(req1, res1);
-
- filterA.doFilter(eq(req2), eq(res2), capture(capturedChainA2));
- filterB.init(config); // <-- This is crucial part. filterB got loaded
- // after filterProxy's init finished. Nonetheless filterB gets initialized.
- filterB.doFilter(eq(req2), eq(res2), capture(capturedChainB));
- chain.doFilter(req2, res2);
-
- filterA.destroy();
- filterB.destroy();
-
- ems.replayAll();
+ InOrder inorder = inOrder(filterA, filterB, chain);
AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
addFilter(filterA);
filterProxy.init(config);
filterProxy.doFilter(req1, res1, chain);
+ inorder.verify(filterA).init(config);
+ inorder.verify(filterA).doFilter(eq(req1), eq(res1), capturedChainA1.capture());
capturedChainA1.getValue().doFilter(req1, res1);
+ inorder.verify(chain).doFilter(req1, res1);
addFilter(filterB); // <-- Adds filter after filterProxy's init got called.
filterProxy.doFilter(req2, res2, chain);
+ // after filterProxy's init finished. Nonetheless filterB gets initialized.
+ inorder.verify(filterA).doFilter(eq(req2), eq(res2), capturedChainA2.capture());
capturedChainA2.getValue().doFilter(req2, res2);
+ inorder.verify(filterB).init(config); // <-- This is crucial part. filterB got loaded
+ inorder.verify(filterB).doFilter(eq(req2), eq(res2), capturedChainB.capture());
capturedChainB.getValue().doFilter(req2, res2);
+ inorder.verify(chain).doFilter(req2, res2);
filterProxy.destroy();
-
- ems.verifyAll();
+ inorder.verify(filterA).destroy();
+ inorder.verify(filterB).destroy();
}
@Test
public void dynamicUnloading() throws Exception {
- EasyMockSupport ems = new EasyMockSupport();
-
- FilterConfig config = ems.createMock(FilterConfig.class);
+ FilterConfig config = mock(FilterConfig.class);
HttpServletRequest req1 = new FakeHttpServletRequest();
HttpServletRequest req2 = new FakeHttpServletRequest();
HttpServletRequest req3 = new FakeHttpServletRequest();
@@ -298,64 +267,62 @@ public class AllRequestFilterFilterProxyTest extends GerritBaseTests {
HttpServletResponse res2 = new FakeHttpServletResponse();
HttpServletResponse res3 = new FakeHttpServletResponse();
- Plugin plugin = ems.createMock(Plugin.class);
-
- IMocksControl mockControl = ems.createStrictControl();
- FilterChain chain = mockControl.createMock("chain", FilterChain.class);
-
- Capture<FilterChain> capturedChainA1 = new Capture<>();
- Capture<FilterChain> capturedChainB1 = new Capture<>();
- Capture<FilterChain> capturedChainB2 = new Capture<>();
-
- AllRequestFilter filterA = mockControl.createMock("filterA", AllRequestFilter.class);
- AllRequestFilter filterB = mockControl.createMock("filterB", AllRequestFilter.class);
+ Plugin plugin = mock(Plugin.class);
- filterA.init(config);
- filterB.init(config);
+ FilterChain chain = mock(FilterChain.class);
- filterA.doFilter(eq(req1), eq(res1), capture(capturedChainA1));
- filterB.doFilter(eq(req1), eq(res1), capture(capturedChainB1));
- chain.doFilter(req1, res1);
+ ArgumentCaptor<FilterChain> capturedChainA1 = ArgumentCaptor.forClass(FilterChain.class);
+ ArgumentCaptor<FilterChain> capturedChainB1 = ArgumentCaptor.forClass(FilterChain.class);
+ ArgumentCaptor<FilterChain> capturedChainB2 = ArgumentCaptor.forClass(FilterChain.class);
- filterA.destroy(); // Cleaning up of filterA after it got unloaded
-
- filterB.doFilter(eq(req2), eq(res2), capture(capturedChainB2));
- chain.doFilter(req2, res2);
-
- filterB.destroy(); // Cleaning up of filterA after it got unloaded
-
- chain.doFilter(req3, res3);
-
- ems.replayAll();
+ AllRequestFilter filterA = mock(AllRequestFilter.class);
+ AllRequestFilter filterB = mock(AllRequestFilter.class);
AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
ReloadableRegistrationHandle<AllRequestFilter> handleFilterA = addFilter(filterA);
ReloadableRegistrationHandle<AllRequestFilter> handleFilterB = addFilter(filterB);
+ InOrder inorder = inOrder(filterA, filterB, chain);
+
filterProxy.init(config);
+ inorder.verify(filterA).init(config);
+ inorder.verify(filterB).init(config);
+
// Request #1 with filterA and filterB
filterProxy.doFilter(req1, res1, chain);
+ inorder.verify(filterA).doFilter(eq(req1), eq(res1), capturedChainA1.capture());
capturedChainA1.getValue().doFilter(req1, res1);
+ inorder.verify(filterB).doFilter(eq(req1), eq(res1), capturedChainB1.capture());
capturedChainB1.getValue().doFilter(req1, res1);
+ inorder.verify(chain).doFilter(req1, res1);
// Unloading filterA
handleFilterA.remove();
filterProxy.onStopPlugin(plugin);
- // Request #1 only with filterB
+ inorder.verify(filterA).destroy(); // Cleaning up of filterA after it got unloaded
+
+ // Request #2 only with filterB
filterProxy.doFilter(req2, res2, chain);
- capturedChainA1.getValue().doFilter(req2, res2);
+
+ inorder.verify(filterB).doFilter(eq(req2), eq(res2), capturedChainB2.capture());
+ inorder.verify(filterA, never()).doFilter(eq(req2), eq(res2), any(FilterChain.class));
+ capturedChainB2.getValue().doFilter(req2, res2);
+ inorder.verify(chain).doFilter(req2, res2);
// Unloading filterB
handleFilterB.remove();
filterProxy.onStopPlugin(plugin);
- // Request #1 with no additional filters
+ inorder.verify(filterB).destroy(); // Cleaning up of filterA after it got unloaded
+
+ // Request #3 with no additional filters
filterProxy.doFilter(req3, res3, chain);
+ inorder.verify(chain).doFilter(req3, res3);
+ inorder.verify(filterA, never()).doFilter(eq(req2), eq(res2), any(FilterChain.class));
+ inorder.verify(filterB, never()).doFilter(eq(req2), eq(res2), any(FilterChain.class));
filterProxy.destroy();
-
- ems.verifyAll();
}
}
diff --git a/javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java b/javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java
index 0d6d482032..26798290c0 100644
--- a/javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java
+++ b/javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java
@@ -17,53 +17,41 @@ package com.google.gerrit.httpd;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.httpd.AllowRenderInFrameFilter.X_FRAME_OPTIONS_HEADER_NAME;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import static org.easymock.EasyMock.expectLastCall;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import com.google.gerrit.httpd.AllowRenderInFrameFilter.XFrameOption;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.easymock.EasyMockSupport;
import org.eclipse.jgit.lib.Config;
-import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
-public class AllowRenderInFrameFilterTest extends GerritBaseTests {
+@RunWith(MockitoJUnitRunner.class)
+public class AllowRenderInFrameFilterTest {
- Config cfg;
- ServletRequest request;
- HttpServletResponse response;
- FilterChain filterChain;
-
- EasyMockSupport ems = new EasyMockSupport();
-
- @Before
- public void setup() throws IOException, ServletException {
- cfg = new Config();
- request = ems.createMock(ServletRequest.class);
- response = ems.createMock(HttpServletResponse.class);
- filterChain = ems.createMock(FilterChain.class);
- ems.resetAll();
- // we want to make sure that doFilter is always called
- filterChain.doFilter(request, response);
- }
+ private Config cfg = new Config();
+ @Mock ServletRequest request;
+ @Mock HttpServletResponse response;
+ @Mock FilterChain filterChain;
@Test
public void shouldDenyInFrameRenderingWhenCanRenderInFrameIsFalse()
throws IOException, ServletException {
cfg.setBoolean("gerrit", null, "canLoadInIFrame", false);
- response.addHeader(X_FRAME_OPTIONS_HEADER_NAME, "DENY");
- expectLastCall().times(1);
- ems.replayAll();
-
AllowRenderInFrameFilter objectUnderTest = new AllowRenderInFrameFilter(cfg);
objectUnderTest.doFilter(request, response, filterChain);
- ems.verifyAll();
+ verify(response, times(1)).addHeader(X_FRAME_OPTIONS_HEADER_NAME, "DENY");
}
@Test
@@ -72,14 +60,10 @@ public class AllowRenderInFrameFilterTest extends GerritBaseTests {
cfg.setBoolean("gerrit", null, "canLoadInIFrame", false);
cfg.setEnum("gerrit", null, "xframeOption", XFrameOption.SAMEORIGIN);
- response.addHeader(X_FRAME_OPTIONS_HEADER_NAME, "DENY");
- expectLastCall().times(1);
- ems.replayAll();
-
AllowRenderInFrameFilter objectUnderTest = new AllowRenderInFrameFilter(cfg);
objectUnderTest.doFilter(request, response, filterChain);
- ems.verifyAll();
+ verify(response, times(1)).addHeader(X_FRAME_OPTIONS_HEADER_NAME, "DENY");
}
@Test
@@ -88,14 +72,10 @@ public class AllowRenderInFrameFilterTest extends GerritBaseTests {
cfg.setBoolean("gerrit", null, "canLoadInIFrame", false);
cfg.setEnum("gerrit", null, "xframeOption", XFrameOption.ALLOW);
- response.addHeader(X_FRAME_OPTIONS_HEADER_NAME, "DENY");
- expectLastCall().times(1);
- ems.replayAll();
-
AllowRenderInFrameFilter objectUnderTest = new AllowRenderInFrameFilter(cfg);
objectUnderTest.doFilter(request, response, filterChain);
- ems.verifyAll();
+ verify(response, times(1)).addHeader(X_FRAME_OPTIONS_HEADER_NAME, "DENY");
}
@Test
@@ -103,14 +83,10 @@ public class AllowRenderInFrameFilterTest extends GerritBaseTests {
throws IOException, ServletException {
cfg.setBoolean("gerrit", null, "canLoadInIFrame", true);
- response.addHeader(X_FRAME_OPTIONS_HEADER_NAME, "SAMEORIGIN");
- expectLastCall().times(1);
- ems.replayAll();
-
AllowRenderInFrameFilter objectUnderTest = new AllowRenderInFrameFilter(cfg);
objectUnderTest.doFilter(request, response, filterChain);
- ems.verifyAll();
+ verify(response, times(1)).addHeader(X_FRAME_OPTIONS_HEADER_NAME, "SAMEORIGIN");
}
@Test
@@ -118,12 +94,11 @@ public class AllowRenderInFrameFilterTest extends GerritBaseTests {
throws IOException, ServletException {
cfg.setBoolean("gerrit", null, "canLoadInIFrame", true);
cfg.setEnum("gerrit", null, "xframeOption", XFrameOption.ALLOW);
- ems.replayAll();
AllowRenderInFrameFilter objectUnderTest = new AllowRenderInFrameFilter(cfg);
objectUnderTest.doFilter(request, response, filterChain);
- ems.verifyAll();
+ verify(response, never()).addHeader(eq(X_FRAME_OPTIONS_HEADER_NAME), anyString());
}
@Test
@@ -132,14 +107,10 @@ public class AllowRenderInFrameFilterTest extends GerritBaseTests {
cfg.setBoolean("gerrit", null, "canLoadInIFrame", true);
cfg.setEnum("gerrit", null, "xframeOption", XFrameOption.SAMEORIGIN);
- response.addHeader(X_FRAME_OPTIONS_HEADER_NAME, "SAMEORIGIN");
- expectLastCall().times(1);
- ems.replayAll();
-
AllowRenderInFrameFilter objectUnderTest = new AllowRenderInFrameFilter(cfg);
objectUnderTest.doFilter(request, response, filterChain);
- ems.verifyAll();
+ verify(response, times(1)).addHeader(X_FRAME_OPTIONS_HEADER_NAME, "SAMEORIGIN");
}
@Test
@@ -147,14 +118,10 @@ public class AllowRenderInFrameFilterTest extends GerritBaseTests {
cfg.setBoolean("gerrit", null, "canLoadInIFrame", true);
cfg.setString("gerrit", null, "xframeOption", "sameOrigin");
- response.addHeader(X_FRAME_OPTIONS_HEADER_NAME, "SAMEORIGIN");
- expectLastCall().times(1);
- ems.replayAll();
-
AllowRenderInFrameFilter objectUnderTest = new AllowRenderInFrameFilter(cfg);
objectUnderTest.doFilter(request, response, filterChain);
- ems.verifyAll();
+ verify(response, times(1)).addHeader(X_FRAME_OPTIONS_HEADER_NAME, "SAMEORIGIN");
}
@Test
diff --git a/javatests/com/google/gerrit/httpd/BUILD b/javatests/com/google/gerrit/httpd/BUILD
index f2c8ded82a..d7518907d3 100644
--- a/javatests/com/google/gerrit/httpd/BUILD
+++ b/javatests/com/google/gerrit/httpd/BUILD
@@ -8,19 +8,19 @@ junit_tests(
"//java/com/google/gerrit/httpd",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/testing:gerrit-test-util",
- "//java/com/google/gerrit/util/http",
"//javatests/com/google/gerrit/util/http/testutil",
"//lib:gson",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib:jimfs",
"//lib:junit",
"//lib:servlet-api-without-neverlink",
"//lib:soy",
- "//lib/easymock",
"//lib/guice",
"//lib/guice:guice-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
+ "//lib/mockito",
"//lib/truth",
+ "//lib/truth:truth-java8-extension",
],
)
diff --git a/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java b/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
index e19085d46e..f012ee34ac 100644
--- a/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
+++ b/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
@@ -17,10 +17,9 @@ package com.google.gerrit.httpd;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.httpd.RemoteUserUtil.extractUsername;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class RemoteUserUtilTest extends GerritBaseTests {
+public class RemoteUserUtilTest {
@Test
public void testExtractUsername() {
assertThat(extractUsername(null)).isNull();
diff --git a/javatests/com/google/gerrit/httpd/plugins/ContextMapperTest.java b/javatests/com/google/gerrit/httpd/plugins/ContextMapperTest.java
index 2de3788992..684a2415bd 100644
--- a/javatests/com/google/gerrit/httpd/plugins/ContextMapperTest.java
+++ b/javatests/com/google/gerrit/httpd/plugins/ContextMapperTest.java
@@ -16,12 +16,11 @@ package com.google.gerrit.httpd.plugins;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.junit.Test;
-public class ContextMapperTest extends GerritBaseTests {
+public class ContextMapperTest {
private static final String CONTEXT = "/context";
private static final String PLUGIN_NAME = "my-plugin";
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java b/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
new file mode 100644
index 0000000000..15c66eba68
--- /dev/null
+++ b/javatests/com/google/gerrit/httpd/raw/IndexHtmlUtilTest.java
@@ -0,0 +1,77 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd.raw;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.httpd.raw.IndexHtmlUtil.staticTemplateData;
+
+import com.google.template.soy.data.SanitizedContent;
+import com.google.template.soy.data.UnsafeSanitizedContentOrdainer;
+import java.util.HashMap;
+import org.junit.Test;
+
+public class IndexHtmlUtilTest {
+
+ @Test
+ public void noPathAndNoCDN() throws Exception {
+ assertThat(
+ staticTemplateData(
+ "http://example.com/", null, null, new HashMap<>(), IndexHtmlUtilTest::ordain))
+ .containsExactly("canonicalPath", "", "staticResourcePath", ordain(""));
+ }
+
+ @Test
+ public void pathAndNoCDN() throws Exception {
+ assertThat(
+ staticTemplateData(
+ "http://example.com/gerrit/",
+ null,
+ null,
+ new HashMap<>(),
+ IndexHtmlUtilTest::ordain))
+ .containsExactly("canonicalPath", "/gerrit", "staticResourcePath", ordain("/gerrit"));
+ }
+
+ @Test
+ public void noPathAndCDN() throws Exception {
+ assertThat(
+ staticTemplateData(
+ "http://example.com/",
+ "http://my-cdn.com/foo/bar/",
+ null,
+ new HashMap<>(),
+ IndexHtmlUtilTest::ordain))
+ .containsExactly(
+ "canonicalPath", "", "staticResourcePath", ordain("http://my-cdn.com/foo/bar/"));
+ }
+
+ @Test
+ public void pathAndCDN() throws Exception {
+ assertThat(
+ staticTemplateData(
+ "http://example.com/gerrit",
+ "http://my-cdn.com/foo/bar/",
+ null,
+ new HashMap<>(),
+ IndexHtmlUtilTest::ordain))
+ .containsExactly(
+ "canonicalPath", "/gerrit", "staticResourcePath", ordain("http://my-cdn.com/foo/bar/"));
+ }
+
+ private static SanitizedContent ordain(String s) {
+ return UnsafeSanitizedContentOrdainer.ordainAsSafe(
+ s, SanitizedContent.ContentKind.TRUSTED_RESOURCE_URI);
+ }
+}
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
index 340f690d76..77ab58b1ae 100644
--- a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
@@ -15,66 +15,52 @@
package com.google.gerrit.httpd.raw;
import static com.google.common.truth.Truth.assertThat;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
-import com.google.gerrit.testing.GerritBaseTests;
-import java.net.URISyntaxException;
-import java.util.Map;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.accounts.Accounts;
+import com.google.gerrit.extensions.api.config.Config;
+import com.google.gerrit.extensions.api.config.Server;
+import com.google.gerrit.extensions.common.ServerInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
+import com.google.gerrit.util.http.testutil.FakeHttpServletResponse;
import org.junit.Test;
-public class IndexServletTest extends GerritBaseTests {
- static class TestIndexServlet extends IndexServlet {
- private static final long serialVersionUID = 1L;
-
- TestIndexServlet(String canonicalURL, String cdnPath, String faviconPath)
- throws URISyntaxException {
- super(canonicalURL, cdnPath, faviconPath);
- }
-
- String getIndexSource() {
- return new String(indexSource, UTF_8);
- }
- }
+public class IndexServletTest {
@Test
- public void noPathAndNoCDN() throws URISyntaxException {
- Map<String, Object> data = IndexServlet.getTemplateData("http://example.com/", null, null);
- assertThat(data.get("canonicalPath")).isEqualTo("");
- assertThat(data.get("staticResourcePath").toString()).isEqualTo("");
- }
+ public void renderTemplate() throws Exception {
+ Accounts accountsApi = mock(Accounts.class);
+ when(accountsApi.self()).thenThrow(new AuthException("user needs to be authenticated"));
- @Test
- public void pathAndNoCDN() throws URISyntaxException {
- Map<String, Object> data =
- IndexServlet.getTemplateData("http://example.com/gerrit/", null, null);
- assertThat(data.get("canonicalPath")).isEqualTo("/gerrit");
- assertThat(data.get("staticResourcePath").toString()).isEqualTo("/gerrit");
- }
+ Server serverApi = mock(Server.class);
+ when(serverApi.getVersion()).thenReturn("123");
+ when(serverApi.topMenus()).thenReturn(ImmutableList.of());
+ ServerInfo serverInfo = new ServerInfo();
+ serverInfo.defaultTheme = "my-default-theme";
+ when(serverApi.getInfo()).thenReturn(serverInfo);
- @Test
- public void noPathAndCDN() throws URISyntaxException {
- Map<String, Object> data =
- IndexServlet.getTemplateData("http://example.com/", "http://my-cdn.com/foo/bar/", null);
- assertThat(data.get("canonicalPath")).isEqualTo("");
- assertThat(data.get("staticResourcePath").toString()).isEqualTo("http://my-cdn.com/foo/bar/");
- }
+ Config configApi = mock(Config.class);
+ when(configApi.server()).thenReturn(serverApi);
- @Test
- public void pathAndCDN() throws URISyntaxException {
- Map<String, Object> data =
- IndexServlet.getTemplateData(
- "http://example.com/gerrit", "http://my-cdn.com/foo/bar/", null);
- assertThat(data.get("canonicalPath")).isEqualTo("/gerrit");
- assertThat(data.get("staticResourcePath").toString()).isEqualTo("http://my-cdn.com/foo/bar/");
- }
+ GerritApi gerritApi = mock(GerritApi.class);
+ when(gerritApi.accounts()).thenReturn(accountsApi);
+ when(gerritApi.config()).thenReturn(configApi);
- @Test
- public void renderTemplate() throws URISyntaxException {
String testCanonicalUrl = "foo-url";
String testCdnPath = "bar-cdn";
String testFaviconURL = "zaz-url";
- TestIndexServlet servlet = new TestIndexServlet(testCanonicalUrl, testCdnPath, testFaviconURL);
- String output = servlet.getIndexSource();
+ IndexServlet servlet =
+ new IndexServlet(testCanonicalUrl, testCdnPath, testFaviconURL, gerritApi);
+
+ FakeHttpServletResponse response = new FakeHttpServletResponse();
+
+ servlet.doGet(new FakeHttpServletRequest(), response);
+
+ String output = response.getActualBodyString();
assertThat(output).contains("<!DOCTYPE html>");
assertThat(output).contains("window.CANONICAL_PATH = '" + testCanonicalUrl);
assertThat(output).contains("<link rel=\"preload\" href=\"" + testCdnPath);
@@ -84,5 +70,12 @@ public class IndexServletTest extends GerritBaseTests {
+ testCanonicalUrl
+ "/"
+ testFaviconURL);
+ assertThat(output)
+ .contains(
+ "window.INITIAL_DATA = JSON.parse("
+ + "'\\x7b\\x22\\/config\\/server\\/version\\x22: \\x22123\\x22, "
+ + "\\x22\\/config\\/server\\/info\\x22: \\x7b\\x22default_theme\\x22:"
+ + "\\x22my-default-theme\\x22\\x7d, \\x22\\/config\\/server\\/top-menus\\x22: "
+ + "\\x5b\\x5d\\x7d');</script>");
}
}
diff --git a/javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java b/javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java
index cfcc1d0984..dd594d61d5 100644
--- a/javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java
@@ -15,6 +15,7 @@
package com.google.gerrit.httpd.raw;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_OK;
@@ -28,7 +29,6 @@ import com.google.common.io.ByteStreams;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.google.gerrit.httpd.raw.ResourceServlet.Resource;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
import com.google.gerrit.util.http.testutil.FakeHttpServletResponse;
import java.io.ByteArrayInputStream;
@@ -45,7 +45,7 @@ import java.util.zip.GZIPInputStream;
import org.junit.Before;
import org.junit.Test;
-public class ResourceServletTest extends GerritBaseTests {
+public class ResourceServletTest {
private static Cache<Path, Resource> newCache(int size) {
return CacheBuilder.newBuilder().maximumSize(size).recordStats().build();
}
@@ -336,8 +336,8 @@ public class ResourceServletTest extends GerritBaseTests {
}
private static void assertCacheHits(Cache<?, ?> cache, int hits, int misses) {
- assertThat(cache.stats().hitCount()).named("hits").isEqualTo(hits);
- assertThat(cache.stats().missCount()).named("misses").isEqualTo(misses);
+ assertWithMessage("hits").that(cache.stats().hitCount()).isEqualTo(hits);
+ assertWithMessage("misses").that(cache.stats().missCount()).isEqualTo(misses);
}
private static void assertCacheable(FakeHttpServletResponse res, boolean revalidate) {
diff --git a/javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java b/javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java
index fa3eaea5e2..fb1ebd9551 100644
--- a/javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java
+++ b/javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.httpd.restapi;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class HttpLogRedactTest extends GerritBaseTests {
+public class HttpLogRedactTest {
@Test
public void redactAuth() {
assertThat(LogRedactUtil.redactQueryString("query=status:open")).isEqualTo("query=status:open");
diff --git a/javatests/com/google/gerrit/httpd/restapi/ParameterParserTest.java b/javatests/com/google/gerrit/httpd/restapi/ParameterParserTest.java
index 30d318b654..a550ac7c76 100644
--- a/javatests/com/google/gerrit/httpd/restapi/ParameterParserTest.java
+++ b/javatests/com/google/gerrit/httpd/restapi/ParameterParserTest.java
@@ -15,21 +15,20 @@
package com.google.gerrit.httpd.restapi;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.httpd.restapi.ParameterParser.QueryParams;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.junit.Test;
-public class ParameterParserTest extends GerritBaseTests {
+public class ParameterParserTest {
@Test
public void convertFormToJson() throws BadRequestException {
JsonObject obj =
@@ -110,35 +109,26 @@ public class ParameterParserTest extends GerritBaseTests {
public void rejectDuplicateMethod() {
FakeHttpServletRequest req = new FakeHttpServletRequest();
req.setQueryString("$m=PUT&$m=DELETE");
- try {
- ParameterParser.getQueryParams(req);
- fail("expected BadRequestException");
- } catch (BadRequestException bad) {
- assertThat(bad).hasMessageThat().isEqualTo("duplicate $m");
- }
+ BadRequestException bad =
+ assertThrows(BadRequestException.class, () -> ParameterParser.getQueryParams(req));
+ assertThat(bad).hasMessageThat().isEqualTo("duplicate $m");
}
@Test
public void rejectDuplicateContentType() {
FakeHttpServletRequest req = new FakeHttpServletRequest();
req.setQueryString("$ct=json&$ct=string");
- try {
- ParameterParser.getQueryParams(req);
- fail("expected BadRequestException");
- } catch (BadRequestException bad) {
- assertThat(bad).hasMessageThat().isEqualTo("duplicate $ct");
- }
+ BadRequestException bad =
+ assertThrows(BadRequestException.class, () -> ParameterParser.getQueryParams(req));
+ assertThat(bad).hasMessageThat().isEqualTo("duplicate $ct");
}
@Test
public void rejectInvalidMethod() {
FakeHttpServletRequest req = new FakeHttpServletRequest();
req.setQueryString("$m=CONNECT");
- try {
- ParameterParser.getQueryParams(req);
- fail("expected BadRequestException");
- } catch (BadRequestException bad) {
- assertThat(bad).hasMessageThat().isEqualTo("invalid $m");
- }
+ BadRequestException bad =
+ assertThrows(BadRequestException.class, () -> ParameterParser.getQueryParams(req));
+ assertThat(bad).hasMessageThat().isEqualTo("invalid $m");
}
}
diff --git a/javatests/com/google/gerrit/index/BUILD b/javatests/com/google/gerrit/index/BUILD
index a1f60de66b..861e768710 100644
--- a/javatests/com/google/gerrit/index/BUILD
+++ b/javatests/com/google/gerrit/index/BUILD
@@ -10,11 +10,12 @@ junit_tests(
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
"//java/com/google/gerrit/index/query/testing",
+ "//java/com/google/gerrit/server",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
"//lib:junit",
"//lib/antlr:java-runtime",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/index/SchemaUtilTest.java b/javatests/com/google/gerrit/index/SchemaUtilTest.java
index d6b8421c3a..698e00a54a 100644
--- a/javatests/com/google/gerrit/index/SchemaUtilTest.java
+++ b/javatests/com/google/gerrit/index/SchemaUtilTest.java
@@ -18,13 +18,13 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.index.SchemaUtil.getNameParts;
import static com.google.gerrit.index.SchemaUtil.getPersonParts;
import static com.google.gerrit.index.SchemaUtil.schema;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Map;
import org.eclipse.jgit.lib.PersonIdent;
import org.junit.Test;
-public class SchemaUtilTest extends GerritBaseTests {
+public class SchemaUtilTest {
static class TestSchemas {
static final Schema<String> V1 = schema();
static final Schema<String> V2 = schema();
@@ -43,9 +43,9 @@ public class SchemaUtilTest extends GerritBaseTests {
assertThat(all.get(1)).isEqualTo(TestSchemas.V1);
assertThat(all.get(2)).isEqualTo(TestSchemas.V2);
assertThat(all.get(4)).isEqualTo(TestSchemas.V4);
-
- exception.expect(IllegalArgumentException.class);
- SchemaUtil.schemasFromClass(TestSchemas.class, Object.class);
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> SchemaUtil.schemasFromClass(TestSchemas.class, Object.class));
}
@Test
diff --git a/javatests/com/google/gerrit/index/query/AndPredicateTest.java b/javatests/com/google/gerrit/index/query/AndPredicateTest.java
index 21098b315e..16828dd242 100644
--- a/javatests/com/google/gerrit/index/query/AndPredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/AndPredicateTest.java
@@ -16,12 +16,12 @@ package com.google.gerrit.index.query;
import static com.google.common.collect.ImmutableList.of;
import static com.google.gerrit.index.query.Predicate.and;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import java.util.List;
import org.junit.Test;
@@ -43,28 +43,13 @@ public class AndPredicateTest extends PredicateTest {
final TestPredicate b = f("author", "bob");
final Predicate<String> n = and(a, b);
- try {
- n.getChildren().clear();
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- // Expected
- }
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().clear());
assertChildren("clear", n, of(a, b));
- try {
- n.getChildren().remove(0);
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- // Expected
- }
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().remove(0));
assertChildren("remove(0)", n, of(a, b));
- try {
- n.getChildren().iterator().remove();
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- // Expected
- }
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().iterator().remove());
assertChildren("iterator().remove()", n, of(a, b));
}
diff --git a/javatests/com/google/gerrit/index/query/FieldPredicateTest.java b/javatests/com/google/gerrit/index/query/FieldPredicateTest.java
index 805f31c194..2d2c99e5ee 100644
--- a/javatests/com/google/gerrit/index/query/FieldPredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/FieldPredicateTest.java
@@ -14,6 +14,8 @@
package com.google.gerrit.index.query;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
@@ -61,8 +63,9 @@ public class FieldPredicateTest extends PredicateTest {
assertSame(f, f.copy(Collections.emptyList()));
assertSame(f, f.copy(f.getChildren()));
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage("Expected 0 children");
- f.copy(Collections.singleton(f("owner", "bob")));
+ IllegalArgumentException thrown =
+ assertThrows(
+ IllegalArgumentException.class, () -> f.copy(Collections.singleton(f("owner", "bob"))));
+ assertThat(thrown).hasMessageThat().contains("Expected 0 children");
}
}
diff --git a/javatests/com/google/gerrit/index/query/LazyDataSourceTest.java b/javatests/com/google/gerrit/index/query/LazyDataSourceTest.java
new file mode 100644
index 0000000000..7064f648d4
--- /dev/null
+++ b/javatests/com/google/gerrit/index/query/LazyDataSourceTest.java
@@ -0,0 +1,106 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.index.query;
+
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gerrit.server.query.change.OrSource;
+import java.util.Collection;
+import java.util.Iterator;
+import org.junit.Test;
+
+/**
+ * Tests that boolean data sources are lazy in that they don't call {@link ResultSet#toList()} or
+ * {@link ResultSet#toList()}. This is necessary because it allows Gerrit to send multiple queries
+ * to the index in parallel, have the results come in asynchronously and wait for them only when we
+ * call aforementioned methods on the {@link ResultSet}.
+ */
+public class LazyDataSourceTest {
+
+ /** Helper to avoid a mock which would be hard to create because of the type inference. */
+ static class LazyPredicate extends Predicate<ChangeData> implements ChangeDataSource {
+ @Override
+ public int getCardinality() {
+ return 1;
+ }
+
+ @Override
+ public ResultSet<ChangeData> read() {
+ return new FailingResultSet<>();
+ }
+
+ @Override
+ public ResultSet<FieldBundle> readRaw() {
+ return new FailingResultSet<>();
+ }
+
+ @Override
+ public Predicate<ChangeData> copy(Collection<? extends Predicate<ChangeData>> children) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public int hashCode() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public boolean hasChange() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+ }
+
+ /** Implementation that throws {@link AssertionError} when accessing results. */
+ static class FailingResultSet<T> implements ResultSet<T> {
+ @Override
+ public Iterator<T> iterator() {
+ throw new AssertionError(
+ "called iterator() on the result set, but shouldn't have because the data source must be lazy");
+ }
+
+ @Override
+ public ImmutableList<T> toList() {
+ throw new AssertionError(
+ "called toList() on the result set, but shouldn't have because the data source must be lazy");
+ }
+
+ @Override
+ public void close() {
+ // No-op
+ }
+ }
+
+ @Test
+ public void andSourceIsLazy() {
+ AndSource<ChangeData> and = new AndSource<>(ImmutableList.of(new LazyPredicate()));
+ ResultSet<ChangeData> resultSet = and.read();
+ assertThrows(AssertionError.class, () -> resultSet.toList());
+ }
+
+ @Test
+ public void orSourceIsLazy() {
+ OrSource or = new OrSource(ImmutableList.of(new LazyPredicate()));
+ ResultSet<ChangeData> resultSet = or.read();
+ assertThrows(AssertionError.class, () -> resultSet.toList());
+ }
+}
diff --git a/javatests/com/google/gerrit/index/query/NotPredicateTest.java b/javatests/com/google/gerrit/index/query/NotPredicateTest.java
index d10d2df498..3d1839d2a4 100644
--- a/javatests/com/google/gerrit/index/query/NotPredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/NotPredicateTest.java
@@ -16,12 +16,12 @@ package com.google.gerrit.index.query;
import static com.google.gerrit.index.query.Predicate.and;
import static com.google.gerrit.index.query.Predicate.not;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import java.util.Collections;
import java.util.List;
@@ -50,26 +50,14 @@ public class NotPredicateTest extends PredicateTest {
final TestPredicate p = f("author", "bob");
final Predicate<String> n = not(p);
- try {
- n.getChildren().clear();
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- assertOnlyChild("clear", p, n);
- }
-
- try {
- n.getChildren().remove(0);
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- assertOnlyChild("remove(0)", p, n);
- }
-
- try {
- n.getChildren().iterator().remove();
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- assertOnlyChild("remove()", p, n);
- }
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().clear());
+ assertOnlyChild("clear", p, n);
+
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().remove(0));
+ assertOnlyChild("remove(0)", p, n);
+
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().iterator().remove());
+ assertOnlyChild("remove()", p, n);
}
private static void assertOnlyChild(String o, Predicate<String> c, Predicate<String> p) {
@@ -112,18 +100,11 @@ public class NotPredicateTest extends PredicateTest {
assertNotSame(n, n.copy(sb));
assertEquals(sb, n.copy(sb).getChildren());
- try {
- n.copy(Collections.emptyList());
- fail("Expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- assertEquals("Expected exactly one child", e.getMessage());
- }
-
- try {
- n.copy(and(a, b).getChildren());
- fail("Expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- assertEquals("Expected exactly one child", e.getMessage());
- }
+ IllegalArgumentException e =
+ assertThrows(IllegalArgumentException.class, () -> n.copy(Collections.emptyList()));
+ assertEquals("Expected exactly one child", e.getMessage());
+
+ e = assertThrows(IllegalArgumentException.class, () -> n.copy(and(a, b).getChildren()));
+ assertEquals("Expected exactly one child", e.getMessage());
}
}
diff --git a/javatests/com/google/gerrit/index/query/OrPredicateTest.java b/javatests/com/google/gerrit/index/query/OrPredicateTest.java
index 255a3f8cce..1cbcb7567a 100644
--- a/javatests/com/google/gerrit/index/query/OrPredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/OrPredicateTest.java
@@ -16,12 +16,12 @@ package com.google.gerrit.index.query;
import static com.google.common.collect.ImmutableList.of;
import static com.google.gerrit.index.query.Predicate.or;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import java.util.List;
import org.junit.Test;
@@ -43,28 +43,13 @@ public class OrPredicateTest extends PredicateTest {
final TestPredicate b = f("author", "bob");
final Predicate<String> n = or(a, b);
- try {
- n.getChildren().clear();
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- // Expected
- }
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().clear());
assertChildren("clear", n, of(a, b));
- try {
- n.getChildren().remove(0);
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- // Expected
- }
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().remove(0));
assertChildren("remove(0)", n, of(a, b));
- try {
- n.getChildren().iterator().remove();
- fail("Expected UnsupportedOperationException");
- } catch (UnsupportedOperationException e) {
- // Expected
- }
+ assertThrows(UnsupportedOperationException.class, () -> n.getChildren().iterator().remove());
assertChildren("iterator().remove()", n, of(a, b));
}
diff --git a/javatests/com/google/gerrit/index/query/PredicateTest.java b/javatests/com/google/gerrit/index/query/PredicateTest.java
index 2295a60004..3ec7f1362d 100644
--- a/javatests/com/google/gerrit/index/query/PredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/PredicateTest.java
@@ -14,11 +14,10 @@
package com.google.gerrit.index.query;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Ignore;
@Ignore
-public abstract class PredicateTest extends GerritBaseTests {
+public abstract class PredicateTest {
protected static final class TestPredicate extends OperatorPredicate<String> {
protected TestPredicate(String name, String value) {
super(name, value);
diff --git a/javatests/com/google/gerrit/index/query/QueryBuilderTest.java b/javatests/com/google/gerrit/index/query/QueryBuilderTest.java
index 6a397dca89..f65375995e 100644
--- a/javatests/com/google/gerrit/index/query/QueryBuilderTest.java
+++ b/javatests/com/google/gerrit/index/query/QueryBuilderTest.java
@@ -17,12 +17,11 @@ package com.google.gerrit.index.query;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.truth.ThrowableSubject;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Collection;
import java.util.Objects;
import org.junit.Test;
-public class QueryBuilderTest extends GerritBaseTests {
+public class QueryBuilderTest {
private static class TestPredicate extends Predicate<Object> {
private final String field;
private final String value;
diff --git a/javatests/com/google/gerrit/index/query/QueryParserTest.java b/javatests/com/google/gerrit/index/query/QueryParserTest.java
index b4dd1ee0c1..776a2c4bb0 100644
--- a/javatests/com/google/gerrit/index/query/QueryParserTest.java
+++ b/javatests/com/google/gerrit/index/query/QueryParserTest.java
@@ -14,7 +14,6 @@
package com.google.gerrit.index.query;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.index.query.QueryParser.AND;
import static com.google.gerrit.index.query.QueryParser.COLON;
import static com.google.gerrit.index.query.QueryParser.DEFAULT_FIELD;
@@ -22,12 +21,12 @@ import static com.google.gerrit.index.query.QueryParser.FIELD_NAME;
import static com.google.gerrit.index.query.QueryParser.SINGLE_WORD;
import static com.google.gerrit.index.query.QueryParser.parse;
import static com.google.gerrit.index.query.testing.TreeSubject.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import org.antlr.runtime.tree.Tree;
import org.junit.Test;
-public class QueryParserTest extends GerritBaseTests {
+public class QueryParserTest {
@Test
public void fieldNameAndValue() throws Exception {
Tree r = parse("project:tools/gerrit");
@@ -206,11 +205,6 @@ public class QueryParserTest extends GerritBaseTests {
}
private static void assertParseFails(String query) {
- try {
- parse(query);
- assert_().fail("expected parse to fail: %s", query);
- } catch (QueryParseException e) {
- // Expected.
- }
+ assertThrows(QueryParseException.class, () -> parse(query));
}
}
diff --git a/javatests/com/google/gerrit/integration/git/BUILD b/javatests/com/google/gerrit/integration/git/BUILD
index 4f1be091bf..28755af59e 100644
--- a/javatests/com/google/gerrit/integration/git/BUILD
+++ b/javatests/com/google/gerrit/integration/git/BUILD
@@ -1,6 +1,12 @@
load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
acceptance_tests(
+ srcs = ["GitProtocolV2IT.java"],
+ group = "protocol-v2",
+ labels = ["git-protocol-v2"],
+)
+
+acceptance_tests(
srcs = ["UploadArchiveIT.java"],
group = "upload-archive",
labels = ["git-upload-archive"],
diff --git a/javatests/com/google/gerrit/integration/git/GitProtocolV2IT.java b/javatests/com/google/gerrit/integration/git/GitProtocolV2IT.java
new file mode 100644
index 0000000000..8577c16faa
--- /dev/null
+++ b/javatests/com/google/gerrit/integration/git/GitProtocolV2IT.java
@@ -0,0 +1,382 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.integration.git;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.GerritServer.TestSshServerAddress;
+import com.google.gerrit.acceptance.GitClientVersion;
+import com.google.gerrit.acceptance.StandaloneSiteTest;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.common.ChangeInput;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.inject.Inject;
+import java.io.File;
+import java.net.InetSocketAddress;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+@UseSsh
+public class GitProtocolV2IT extends StandaloneSiteTest {
+ private static final String ADMIN_PASSWORD = "secret";
+ private final String[] SSH_KEYGEN_CMD =
+ new String[] {"ssh-keygen", "-t", "rsa", "-q", "-P", "", "-f"};
+ private final String[] GIT_LS_REMOTE =
+ new String[] {"git", "-c", "protocol.version=2", "ls-remote", "-o", "trace=12345"};
+ private final String[] GIT_CLONE_MIRROR =
+ new String[] {"git", "-c", "protocol.version=2", "clone", "--mirror"};
+ private final String[] GIT_FETCH = new String[] {"git", "-c", "protocol.version=2", "fetch"};
+ private final String[] GIT_INIT = new String[] {"git", "init"};
+ private final String GIT_SSH_COMMAND =
+ "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i";
+
+ @Inject private GerritApi gApi;
+ @Inject private AccountCreator accountCreator;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private @TestSshServerAddress InetSocketAddress sshAddress;
+ @Inject private @GerritServerConfig Config config;
+ @Inject private AllProjectsName allProjectsName;
+
+ @BeforeClass
+ public static void assertGitClientVersion() throws Exception {
+ // Minimum required git-core version that supports wire protocol v2 is 2.18.0
+ GitClientVersion requiredGitVersion = new GitClientVersion(2, 18, 0);
+ GitClientVersion actualGitVersion =
+ new GitClientVersion(execute(ImmutableList.of("git", "version"), new File("/")));
+ // If git client version cannot be updated, consider to skip this tests. Due to
+ // an existing issue in bazel, JUnit assumption violation feature cannot be used.
+ assertThat(actualGitVersion).isAtLeast(requiredGitVersion);
+ }
+
+ @Test
+ public void testGitWireProtocolV2WithSsh() throws Exception {
+ try (ServerContext ctx = startServer()) {
+ ctx.getInjector().injectMembers(this);
+
+ // Create project
+ Project.NameKey project = Project.nameKey("foo");
+ gApi.projects().create(project.get());
+
+ // Set up project permission
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(deny(Permission.READ).ref("refs/*").group(SystemGroupBackend.ANONYMOUS_USERS))
+ .add(
+ allow(Permission.READ)
+ .ref("refs/heads/master")
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
+
+ // Set protocol.version=2 in target repository
+ execute(
+ ImmutableList.of("git", "config", "protocol.version", "2"),
+ sitePaths.site_path.resolve("git").resolve(project.get() + Constants.DOT_GIT).toFile());
+
+ // Retrieve HTTP url
+ String url = config.getString("gerrit", null, "canonicalweburl");
+ String urlDestinationTemplate =
+ url.substring(0, 7)
+ + "%s:secret@"
+ + url.substring(7, url.length())
+ + "/a/"
+ + project.get();
+
+ // Retrieve SSH host and port
+ String sshDestinationTemplate =
+ "ssh://%s@" + sshAddress.getHostName() + ":" + sshAddress.getPort() + "/" + project.get();
+
+ // Admin user was already created by the base class
+ setUpUserAuthentication(admin.username());
+
+ // Create non-admin user
+ TestAccount user = accountCreator.user();
+ setUpUserAuthentication(user.username());
+
+ // Prepare data for new change on master branch
+ ChangeInput in = new ChangeInput(project.get(), "master", "Test public change");
+ in.newBranch = true;
+
+ // Create new change and retrieve SHA1 for the created patch set
+ String commit =
+ gApi.changes()
+ .id(gApi.changes().create(in).info().changeId)
+ .current()
+ .commit(false)
+ .commit;
+
+ // Prepare new change on secret branch
+ in = new ChangeInput(project.get(), ADMIN_PASSWORD, "Test secret change");
+ in.newBranch = true;
+
+ // Create new change and retrieve SHA1 for the created patch set
+ String secretCommit =
+ gApi.changes()
+ .id(gApi.changes().create(in).info().changeId)
+ .current()
+ .commit(false)
+ .commit;
+
+ // Read refs from target repository using git wire protocol v2 over HTTP for admin user
+ String out =
+ execute(
+ ImmutableList.<String>builder()
+ .add(GIT_LS_REMOTE)
+ .add(String.format(urlDestinationTemplate, admin.username()))
+ .build(),
+ ImmutableMap.of("GIT_TRACE_PACKET", "1"));
+
+ assertGitProtocolV2Refs(commit, out);
+ assertThat(out).contains(secretCommit);
+
+ // Read refs from target repository using git wire protocol v2 over SSH for admin user
+ out =
+ execute(
+ ImmutableList.<String>builder()
+ .add(GIT_LS_REMOTE)
+ .add(String.format(sshDestinationTemplate, admin.username()))
+ .build(),
+ ImmutableMap.of(
+ "GIT_SSH_COMMAND",
+ GIT_SSH_COMMAND
+ + sitePaths.data_dir.resolve(String.format("id_rsa_%s", admin.username())),
+ "GIT_TRACE_PACKET",
+ "1"));
+
+ assertGitProtocolV2Refs(commit, out);
+ assertThat(out).contains(secretCommit);
+
+ // Read refs from target repository using git wire protocol v2 over HTTP for non-admin user
+ out =
+ execute(
+ ImmutableList.<String>builder()
+ .add(GIT_LS_REMOTE)
+ .add(String.format(urlDestinationTemplate, user.username()))
+ .build(),
+ ImmutableMap.of("GIT_TRACE_PACKET", "1"));
+
+ assertGitProtocolV2Refs(commit, out);
+ assertThat(out).doesNotContain(secretCommit);
+
+ // Read refs from target repository using git wire protocol v2 over SSH for non-admin user
+ out =
+ execute(
+ ImmutableList.<String>builder()
+ .add(GIT_LS_REMOTE)
+ .add(String.format(sshDestinationTemplate, user.username()))
+ .build(),
+ ImmutableMap.of(
+ "GIT_SSH_COMMAND",
+ GIT_SSH_COMMAND
+ + sitePaths.data_dir.resolve(String.format("id_rsa_%s", user.username())),
+ "GIT_TRACE_PACKET",
+ "1"));
+
+ assertGitProtocolV2Refs(commit, out);
+ assertThat(out).doesNotContain(secretCommit);
+ }
+ }
+
+ @Test
+ public void testGitWireProtocolV2HidesRefMetaConfig() throws Exception {
+ try (ServerContext ctx = startServer()) {
+ ctx.getInjector().injectMembers(this);
+ String url = config.getString("gerrit", null, "canonicalweburl");
+
+ // Create project
+ Project.NameKey allRefsVisibleProject = Project.nameKey("all-refs-visible");
+ gApi.projects().create(allRefsVisibleProject.get());
+
+ // Set protocol.version=2 in target repository
+ execute(
+ ImmutableList.of("git", "config", "protocol.version", "2"),
+ sitePaths
+ .site_path
+ .resolve("git")
+ .resolve(allRefsVisibleProject.get() + Constants.DOT_GIT)
+ .toFile());
+
+ // Set up project permission to allow reading all refs
+ projectOperations
+ .project(allRefsVisibleProject)
+ .forUpdate()
+ .add(allow(Permission.READ).ref("refs/heads/*").group(SystemGroupBackend.ANONYMOUS_USERS))
+ .add(
+ allow(Permission.READ)
+ .ref("refs/changes/*")
+ .group(SystemGroupBackend.ANONYMOUS_USERS))
+ .update();
+
+ // Create new change and retrieve refs for the created patch set
+ ChangeInput visibleChangeIn =
+ new ChangeInput(allRefsVisibleProject.get(), "master", "Test public change");
+ visibleChangeIn.newBranch = true;
+ int visibleChangeNumber = gApi.changes().create(visibleChangeIn).info()._number;
+ Change.Id changeId = Change.id(visibleChangeNumber);
+ String visibleChangeNumberRef = RefNames.patchSetRef(PatchSet.id(changeId, 1));
+ String visibleChangeNumberMetaRef = RefNames.changeMetaRef(changeId);
+
+ // Read refs from target repository using git wire protocol v2 over HTTP anonymously
+ String outAnonymousLsRemote =
+ execute(
+ ImmutableList.<String>builder()
+ .add(GIT_CLONE_MIRROR)
+ .add(url + "/" + allRefsVisibleProject.get())
+ .build(),
+ ImmutableMap.of("GIT_TRACE_PACKET", "1"));
+
+ assertThat(outAnonymousLsRemote).contains("git< version 2");
+ assertThat(outAnonymousLsRemote).doesNotContain(RefNames.REFS_CONFIG);
+ assertThat(outAnonymousLsRemote).contains(visibleChangeNumberRef);
+ assertThat(outAnonymousLsRemote).contains(visibleChangeNumberMetaRef);
+ }
+ }
+
+ @Test
+ public void testGitWireProtocolV2FetchIndividualRef() throws Exception {
+ try (ServerContext ctx = startServer()) {
+ ctx.getInjector().injectMembers(this);
+
+ // Setup admin password
+ gApi.accounts().id(admin.username()).setHttpPassword(ADMIN_PASSWORD);
+
+ // Get authenticated Git/HTTP URL
+ String urlWithCredentials =
+ config
+ .getString("gerrit", null, "canonicalweburl")
+ .replace("http://", "http://" + admin.username() + ":" + ADMIN_PASSWORD + "@");
+
+ // Create project
+ Project.NameKey privateProject = Project.nameKey("private-project");
+ gApi.projects().create(privateProject.get());
+
+ // Set protocol.version=2 in target repository
+ execute(
+ ImmutableList.of("git", "config", "protocol.version", "2"),
+ sitePaths
+ .site_path
+ .resolve("git")
+ .resolve(privateProject.get() + Constants.DOT_GIT)
+ .toFile());
+
+ // Disallow general read permissions for anonymous users
+ projectOperations
+ .project(allProjectsName)
+ .forUpdate()
+ .add(deny(Permission.READ).ref("refs/*").group(SystemGroupBackend.ANONYMOUS_USERS))
+ .add(
+ allow(Permission.READ)
+ .ref("refs/heads/master")
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
+
+ // Set up project permission to allow registered users fetching changes/*
+ projectOperations
+ .project(privateProject)
+ .forUpdate()
+ .add(
+ allow(Permission.READ)
+ .ref("refs/changes/*")
+ .group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
+
+ // Create new change and retrieve refs for the created patch set
+ ChangeInput visibleChangeIn =
+ new ChangeInput(privateProject.get(), "master", "Test private change");
+ visibleChangeIn.newBranch = true;
+ int visibleChangeNumber = gApi.changes().create(visibleChangeIn).info()._number;
+ Change.Id changeId = Change.id(visibleChangeNumber);
+ String visibleChangeNumberRef = RefNames.patchSetRef(PatchSet.id(changeId, 1));
+
+ // Fetch a single ref using git wire protocol v2 over HTTP with authentication
+ execute(GIT_INIT);
+
+ String outFetchRef =
+ execute(
+ ImmutableList.<String>builder()
+ .add(GIT_FETCH)
+ .add(urlWithCredentials + "/" + privateProject.get())
+ .add(visibleChangeNumberRef)
+ .build(),
+ ImmutableMap.of("GIT_TRACE_PACKET", "1"));
+
+ assertThat(outFetchRef).contains("git< version 2");
+ assertThat(outFetchRef).contains(visibleChangeNumberRef);
+ }
+ }
+
+ private void setUpUserAuthentication(String username) throws Exception {
+ // Assign HTTP password to user
+ gApi.accounts().id(username).setHttpPassword(ADMIN_PASSWORD);
+
+ // Generate private/public key for user
+ execute(
+ ImmutableList.<String>builder()
+ .add(SSH_KEYGEN_CMD)
+ .add(String.format("id_rsa_%s", username))
+ .build());
+
+ // Read the content of generated public key and add it for the user in Gerrit
+ gApi.accounts()
+ .id(username)
+ .addSshKey(
+ new String(
+ java.nio.file.Files.readAllBytes(
+ sitePaths.data_dir.resolve(String.format("id_rsa_%s.pub", username))),
+ UTF_8));
+ }
+
+ private static void assertGitProtocolV2Refs(String commit, String out) {
+ assertThat(out).contains("git< version 2");
+ assertThat(out).contains("refs/changes/01/1/1");
+ assertThat(out).contains("refs/changes/01/1/meta");
+ assertThat(out).contains(commit);
+ }
+
+ private String execute(String... cmds) throws Exception {
+ return execute(ImmutableList.<String>builder().add(cmds).build());
+ }
+
+ private String execute(ImmutableList<String> cmd) throws Exception {
+ return execute(cmd, sitePaths.data_dir.toFile(), ImmutableMap.of());
+ }
+
+ private String execute(ImmutableList<String> cmd, ImmutableMap<String, String> env)
+ throws Exception {
+ return execute(cmd, sitePaths.data_dir.toFile(), env);
+ }
+
+ private static String execute(ImmutableList<String> cmd, File dir) throws Exception {
+ return execute(cmd, dir, ImmutableMap.of());
+ }
+}
diff --git a/javatests/com/google/gerrit/integration/git/UploadArchiveIT.java b/javatests/com/google/gerrit/integration/git/UploadArchiveIT.java
index 18a78b0664..2968111f38 100644
--- a/javatests/com/google/gerrit/integration/git/UploadArchiveIT.java
+++ b/javatests/com/google/gerrit/integration/git/UploadArchiveIT.java
@@ -27,11 +27,11 @@ import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.StandaloneSiteTest;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.common.RawInputUtil;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import java.io.BufferedInputStream;
import java.io.IOException;
@@ -133,7 +133,7 @@ public class UploadArchiveIT extends StandaloneSiteTest {
private void setUpTestHarness(ServerContext ctx) throws RestApiException, Exception {
ctx.getInjector().injectMembers(this);
- project = new Project.NameKey("upload-archive-project-test");
+ project = Project.nameKey("upload-archive-project-test");
gApi.projects().create(project.get());
setUpAuthentication();
sshDestination =
diff --git a/javatests/com/google/gerrit/json/BUILD b/javatests/com/google/gerrit/json/BUILD
index 4894cdb466..575f575bd2 100644
--- a/javatests/com/google/gerrit/json/BUILD
+++ b/javatests/com/google/gerrit/json/BUILD
@@ -5,6 +5,9 @@ junit_tests(
srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/json",
+ "//java/com/google/gerrit/server/util/time",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:gson",
"//lib:guava",
"//lib/truth",
],
diff --git a/javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java b/javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java
index a8488a96eb..05a9cfb583 100644
--- a/javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java
+++ b/javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java
@@ -15,8 +15,8 @@
package com.google.gerrit.json;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.json.JavaSqlTimestampHelper.parseTimestamp;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
@@ -73,12 +73,7 @@ public class JavaSqlTimestampHelperTest {
}
private static void assertInvalid(String input) {
- try {
- parseTimestamp(input);
- assert_().fail("Expected IllegalArgumentException for: " + input);
- } catch (IllegalArgumentException e) {
- // Expected;
- }
+ assertThrows(IllegalArgumentException.class, () -> parseTimestamp(input));
}
private String reformat(String input) {
diff --git a/javatests/com/google/gerrit/json/JsonEnumMappingTest.java b/javatests/com/google/gerrit/json/JsonEnumMappingTest.java
new file mode 100644
index 0000000000..dd710f98a6
--- /dev/null
+++ b/javatests/com/google/gerrit/json/JsonEnumMappingTest.java
@@ -0,0 +1,80 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.json;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gson.Gson;
+import org.junit.Test;
+
+public class JsonEnumMappingTest {
+
+ // Use the regular, pre-configured Gson object we use throughout the Gerrit server to ensure that
+ // the EnumTypeAdapterFactory is properly set up.
+ private final Gson gson = OutputFormat.JSON.newGson();
+
+ @Test
+ public void nullCanBeWrittenAndParsedBack() {
+ String resultingJson = gson.toJson(null, TestEnum.class);
+ TestEnum value = gson.fromJson(resultingJson, TestEnum.class);
+ assertThat(value).isNull();
+ }
+
+ @Test
+ public void enumValueCanBeWrittenAndParsedBack() {
+ String resultingJson = gson.toJson(TestEnum.ONE, TestEnum.class);
+ TestEnum value = gson.fromJson(resultingJson, TestEnum.class);
+ assertThat(value).isEqualTo(TestEnum.ONE);
+ }
+
+ @Test
+ public void enumValueCanBeParsed() {
+ TestData data = gson.fromJson("{\"value\":\"ONE\"}", TestData.class);
+ assertThat(data.value).isEqualTo(TestEnum.ONE);
+ }
+
+ @Test
+ public void mixedCaseEnumValueIsTreatedAsUnset() {
+ TestData data = gson.fromJson("{\"value\":\"oNe\"}", TestData.class);
+ assertThat(data.value).isNull();
+ }
+
+ @Test
+ public void lowerCaseEnumValueIsTreatedAsUnset() {
+ TestData data = gson.fromJson("{\"value\":\"one\"}", TestData.class);
+ assertThat(data.value).isNull();
+ }
+
+ @Test
+ public void notExistingEnumValueIsTreatedAsUnset() {
+ TestData data = gson.fromJson("{\"value\":\"FOUR\"}", TestData.class);
+ assertThat(data.value).isNull();
+ }
+
+ @Test
+ public void emptyEnumValueIsTreatedAsUnset() {
+ TestData data = gson.fromJson("{\"value\":\"\"}", TestData.class);
+ assertThat(data.value).isNull();
+ }
+
+ private static class TestData {
+ TestEnum value;
+ }
+
+ private enum TestEnum {
+ ONE,
+ TWO
+ }
+}
diff --git a/javatests/com/google/gerrit/json/SqlTimestampDeserializerTest.java b/javatests/com/google/gerrit/json/SqlTimestampDeserializerTest.java
new file mode 100644
index 0000000000..2699c3b793
--- /dev/null
+++ b/javatests/com/google/gerrit/json/SqlTimestampDeserializerTest.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.json;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.gson.JsonPrimitive;
+import java.sql.Timestamp;
+import org.junit.Test;
+
+public class SqlTimestampDeserializerTest {
+
+ private final SqlTimestampDeserializer deserializer = new SqlTimestampDeserializer();
+
+ @Test
+ public void emptyStringIsDeserializedToMagicTimestamp() {
+ Timestamp timestamp = deserializer.deserialize(new JsonPrimitive(""), Timestamp.class, null);
+ assertThat(timestamp).isEqualTo(TimeUtil.never());
+ }
+}
diff --git a/javatests/com/google/gerrit/mail/AbstractParserTest.java b/javatests/com/google/gerrit/mail/AbstractParserTest.java
index bcff6a75b2..3c84420717 100644
--- a/javatests/com/google/gerrit/mail/AbstractParserTest.java
+++ b/javatests/com/google/gerrit/mail/AbstractParserTest.java
@@ -16,9 +16,8 @@ package com.google.gerrit.mail;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Comment;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
@@ -26,7 +25,7 @@ import java.util.List;
import org.junit.Ignore;
@Ignore
-public class AbstractParserTest extends GerritBaseTests {
+public class AbstractParserTest {
protected static final String CHANGE_URL =
"https://gerrit-review.googlesource.com/c/project/+/123";
@@ -56,7 +55,7 @@ public class AbstractParserTest extends GerritBaseTests {
Comment c =
new Comment(
new Comment.Key(uuid, file, 1),
- new Account.Id(0),
+ Account.id(0),
new Timestamp(0L),
(short) 0,
message,
@@ -70,7 +69,7 @@ public class AbstractParserTest extends GerritBaseTests {
Comment c =
new Comment(
new Comment.Key(uuid, file, 1),
- new Account.Id(0),
+ Account.id(0),
new Timestamp(0L),
(short) 0,
message,
diff --git a/javatests/com/google/gerrit/mail/AddressTest.java b/javatests/com/google/gerrit/mail/AddressTest.java
index 53ff1fe20a..da26123bb7 100644
--- a/javatests/com/google/gerrit/mail/AddressTest.java
+++ b/javatests/com/google/gerrit/mail/AddressTest.java
@@ -15,12 +15,11 @@
package com.google.gerrit.mail;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class AddressTest extends GerritBaseTests {
+public class AddressTest {
@Test
public void parse_NameEmail1() {
final Address a = Address.parse("A U Thor <author@example.com>");
@@ -97,12 +96,9 @@ public class AddressTest extends GerritBaseTests {
}
private void assertInvalid(String in) {
- try {
- Address.parse(in);
- fail("Expected IllegalArgumentException for " + in);
- } catch (IllegalArgumentException e) {
- assertThat(e.getMessage()).isEqualTo("Invalid email address: " + in);
- }
+ IllegalArgumentException thrown =
+ assertThrows(IllegalArgumentException.class, () -> Address.parse(in));
+ assertThat(thrown).hasMessageThat().isEqualTo("Invalid email address: " + in);
}
@Test
diff --git a/javatests/com/google/gerrit/mail/BUILD b/javatests/com/google/gerrit/mail/BUILD
index b1c97120d0..bd2c478778 100644
--- a/javatests/com/google/gerrit/mail/BUILD
+++ b/javatests/com/google/gerrit/mail/BUILD
@@ -8,15 +8,15 @@ junit_tests(
),
visibility = ["//visibility:public"],
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/mail",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:gson",
"//lib:guava-retrying",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib/commons:codec",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/truth",
"//lib/truth:truth-java8-extension",
],
diff --git a/javatests/com/google/gerrit/mail/HtmlParserTest.java b/javatests/com/google/gerrit/mail/HtmlParserTest.java
index d630bd6290..345cb05544 100644
--- a/javatests/com/google/gerrit/mail/HtmlParserTest.java
+++ b/javatests/com/google/gerrit/mail/HtmlParserTest.java
@@ -16,7 +16,7 @@ package com.google.gerrit.mail;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/javatests/com/google/gerrit/mail/MailHeaderParserTest.java b/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
index cdc8d7ad6e..2d2c2ea778 100644
--- a/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
+++ b/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
@@ -16,14 +16,13 @@ package com.google.gerrit.mail;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneOffset;
import org.junit.Test;
-public class MailHeaderParserTest extends GerritBaseTests {
+public class MailHeaderParserTest {
@Test
public void parseMetadataFromHeader() {
// This tests if the metadata parser is able to parse metadata from the
diff --git a/javatests/com/google/gerrit/mail/ParserUtilTest.java b/javatests/com/google/gerrit/mail/ParserUtilTest.java
index ed40a57701..47a53673f8 100644
--- a/javatests/com/google/gerrit/mail/ParserUtilTest.java
+++ b/javatests/com/google/gerrit/mail/ParserUtilTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.mail;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class ParserUtilTest extends GerritBaseTests {
+public class ParserUtilTest {
@Test
public void trimQuotationLineOnMessageWithoutQuoatationLine() throws Exception {
assertThat(ParserUtil.trimQuotation("One line")).isEqualTo("One line");
diff --git a/javatests/com/google/gerrit/mail/RawMailParserTest.java b/javatests/com/google/gerrit/mail/RawMailParserTest.java
index 9049704f0f..0ab281114b 100644
--- a/javatests/com/google/gerrit/mail/RawMailParserTest.java
+++ b/javatests/com/google/gerrit/mail/RawMailParserTest.java
@@ -23,10 +23,9 @@ import com.google.gerrit.mail.data.NonUTF8Message;
import com.google.gerrit.mail.data.QuotedPrintableHeaderMessage;
import com.google.gerrit.mail.data.RawMailMessage;
import com.google.gerrit.mail.data.SimpleTextMessage;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class RawMailParserTest extends GerritBaseTests {
+public class RawMailParserTest {
@Test
public void parseEmail() throws Exception {
RawMailMessage[] messages =
diff --git a/javatests/com/google/gerrit/mail/TextParserTest.java b/javatests/com/google/gerrit/mail/TextParserTest.java
index a5c2152711..00d5b41c62 100644
--- a/javatests/com/google/gerrit/mail/TextParserTest.java
+++ b/javatests/com/google/gerrit/mail/TextParserTest.java
@@ -16,7 +16,7 @@ package com.google.gerrit.mail;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.entities.Comment;
import java.util.List;
import org.junit.Test;
diff --git a/javatests/com/google/gerrit/mail/data/SimpleTextMessage.java b/javatests/com/google/gerrit/mail/data/SimpleTextMessage.java
index a8f5b94a92..c4737e629b 100644
--- a/javatests/com/google/gerrit/mail/data/SimpleTextMessage.java
+++ b/javatests/com/google/gerrit/mail/data/SimpleTextMessage.java
@@ -39,7 +39,7 @@ public class SimpleTextMessage extends RawMailMessage {
+ "when I try to load this change:\n"
+ "\n"
+ " Error in GET /changes/90018/detail?O=10004\n"
- + " com.google.gwtorm.OrmException: java.lang.NullPointerException\n"
+ + " com.google.gerrit.exceptions.StorageException: java.lang.NullPointerException\n"
+ "\tat com.google.gerrit.change.ChangeJson.format(ChangeJson.java:303)\n"
+ "\tat com.google.gerrit.change.ChangeJson.format(ChangeJson.java:285)\n"
+ "\tat com.google.gerrit.change.ChangeJson.format(ChangeJson.java:263)\n"
diff --git a/javatests/com/google/gerrit/metrics/dropwizard/BUILD b/javatests/com/google/gerrit/metrics/dropwizard/BUILD
index 63d4452193..98d12b2e7b 100644
--- a/javatests/com/google/gerrit/metrics/dropwizard/BUILD
+++ b/javatests/com/google/gerrit/metrics/dropwizard/BUILD
@@ -7,7 +7,6 @@ junit_tests(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/metrics/dropwizard",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/metrics/dropwizard/DropWizardMetricMakerTest.java b/javatests/com/google/gerrit/metrics/dropwizard/DropWizardMetricMakerTest.java
index d6bcb625a8..9b21bf6c01 100644
--- a/javatests/com/google/gerrit/metrics/dropwizard/DropWizardMetricMakerTest.java
+++ b/javatests/com/google/gerrit/metrics/dropwizard/DropWizardMetricMakerTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.metrics.dropwizard;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class DropWizardMetricMakerTest extends GerritBaseTests {
+public class DropWizardMetricMakerTest {
DropWizardMetricMaker metrics =
new DropWizardMetricMaker(null /* MetricRegistry unused in tests */);
diff --git a/javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java b/javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java
index 0a5dabf68a..33919e7ad0 100644
--- a/javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java
+++ b/javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java
@@ -15,6 +15,8 @@
package com.google.gerrit.metrics.proc;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
@@ -30,7 +32,6 @@ import com.google.gerrit.metrics.Description.FieldOrdering;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
@@ -39,7 +40,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Before;
import org.junit.Test;
-public class ProcMetricModuleTest extends GerritBaseTests {
+public class ProcMetricModuleTest {
@Inject MetricMaker metrics;
@Inject MetricRegistry registry;
@@ -79,7 +80,9 @@ public class ProcMetricModuleTest extends GerritBaseTests {
public void counter1() {
Counter1<String> cntr =
metrics.newCounter(
- "test/count", new Description("simple test").setCumulative(), Field.ofString("action"));
+ "test/count",
+ new Description("simple test").setCumulative(),
+ Field.ofString("action", Field.ignoreMetadata()).build());
Counter total = get("test/count_total", Counter.class);
assertThat(total.getCount()).isEqualTo(0);
@@ -104,7 +107,7 @@ public class ProcMetricModuleTest extends GerritBaseTests {
new Description("simple test")
.setCumulative()
.setFieldOrdering(FieldOrdering.PREFIX_FIELDS_BASENAME),
- Field.ofString("action"));
+ Field.ofString("action", Field.ignoreMetadata()).build());
Counter total = get("test/count_total", Counter.class);
assertThat(total.getCount()).isEqualTo(0);
@@ -147,14 +150,16 @@ public class ProcMetricModuleTest extends GerritBaseTests {
@Test
public void invalidName1() {
- exception.expect(IllegalArgumentException.class);
- metrics.newCounter("invalid name", new Description("fail"));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> metrics.newCounter("invalid name", new Description("fail")));
}
@Test
public void invalidName2() {
- exception.expect(IllegalArgumentException.class);
- metrics.newCounter("invalid/ name", new Description("fail"));
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> metrics.newCounter("invalid/ name", new Description("fail")));
}
@SuppressWarnings({"unchecked", "cast"})
@@ -164,8 +169,8 @@ public class ProcMetricModuleTest extends GerritBaseTests {
private <M extends Metric> M get(String name, Class<M> type) {
Metric m = registry.getMetrics().get(name);
- assertThat(m).named(name).isNotNull();
- assertThat(m).named(name).isInstanceOf(type);
+ assertWithMessage(name).that(m).isNotNull();
+ assertWithMessage(name).that(m).isInstanceOf(type);
@SuppressWarnings("unchecked")
M result = (M) m;
diff --git a/javatests/com/google/gerrit/pgm/BUILD b/javatests/com/google/gerrit/pgm/BUILD
index e82b8848a3..14457070e5 100644
--- a/javatests/com/google/gerrit/pgm/BUILD
+++ b/javatests/com/google/gerrit/pgm/BUILD
@@ -5,19 +5,17 @@ junit_tests(
name = "pgm_tests",
srcs = glob(["**/*.java"]),
deps = [
- "//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/pgm",
"//java/com/google/gerrit/pgm/init",
"//java/com/google/gerrit/pgm/init/api",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/securestore/testing",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib:junit",
- "//lib/easymock",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
+ "//lib/mockito",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java b/javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java
index a34007edbf..e34b57897b 100644
--- a/javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java
+++ b/javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java
@@ -15,8 +15,6 @@
package com.google.gerrit.pgm.init.api;
import static com.google.common.truth.Truth.assertThat;
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.replay;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.server.config.SitePaths;
@@ -37,12 +35,18 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+@RunWith(MockitoJUnitRunner.class)
public class AllProjectsConfigTest {
private static final String ALL_PROJECTS = "All-The-Projects";
@Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+ @Mock ConsoleUI ui;
+
private SitePaths sitePaths;
private AllProjectsConfig allProjectsConfig;
private File allProjectsRepoFile;
@@ -70,8 +74,6 @@ public class AllProjectsConfigTest {
InMemorySecureStore secureStore = new InMemorySecureStore();
InitFlags flags = new InitFlags(sitePaths, secureStore, ImmutableList.of(), false);
- ConsoleUI ui = createStrictMock(ConsoleUI.class);
- replay(ui);
Section.Factory sections =
(name, subsection) -> new Section(flags, sitePaths, secureStore, ui, name, subsection);
allProjectsConfig =
diff --git a/javatests/com/google/gerrit/proto/ProtosTest.java b/javatests/com/google/gerrit/proto/ProtosTest.java
index 29e8fe073b..550bcc5777 100644
--- a/javatests/com/google/gerrit/proto/ProtosTest.java
+++ b/javatests/com/google/gerrit/proto/ProtosTest.java
@@ -14,17 +14,16 @@
package com.google.gerrit.proto;
-import static com.google.common.truth.Truth.assert_;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesKeyProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.protobuf.ByteString;
import java.util.Arrays;
import org.junit.Test;
-public class ProtosTest extends GerritBaseTests {
+public class ProtosTest {
@Test
public void parseUncheckedByteArrayWrongProtoType() {
ChangeNotesKeyProto proto =
@@ -34,23 +33,17 @@ public class ProtosTest extends GerritBaseTests {
.setId(ByteString.copyFromUtf8("foo"))
.build();
byte[] bytes = Protos.toByteArray(proto);
- try {
- Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes));
}
@Test
public void parseUncheckedByteArrayInvalidData() {
byte[] bytes = new byte[] {0x00};
- try {
- Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes));
}
@Test
@@ -74,23 +67,17 @@ public class ProtosTest extends GerritBaseTests {
.setId(ByteString.copyFromUtf8("foo"))
.build();
byte[] bytes = Protos.toByteArray(proto);
- try {
- Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes, 0, bytes.length);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes, 0, bytes.length));
}
@Test
public void parseUncheckedSegmentOfByteArrayInvalidData() {
byte[] bytes = new byte[] {0x00};
- try {
- Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes, 0, bytes.length);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes, 0, bytes.length));
}
@Test
@@ -123,23 +110,17 @@ public class ProtosTest extends GerritBaseTests {
.setId(ByteString.copyFromUtf8("foo"))
.build();
ByteString byteString = Protos.toByteString(proto);
- try {
- Protos.parseUnchecked(ChangeNotesStateProto.parser(), byteString);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Protos.parseUnchecked(ChangeNotesStateProto.parser(), byteString));
}
@Test
public void parseUncheckedByteStringInvalidData() {
ByteString byteString = ByteString.copyFrom(new byte[] {0x00});
- try {
- Protos.parseUnchecked(ChangeNotesStateProto.parser(), byteString);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> Protos.parseUnchecked(ChangeNotesStateProto.parser(), byteString));
}
@Test
diff --git a/javatests/com/google/gerrit/reviewdb/converter/RevIdProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/RevIdProtoConverterTest.java
deleted file mode 100644
index 2c354be94d..0000000000
--- a/javatests/com/google/gerrit/reviewdb/converter/RevIdProtoConverterTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.converter;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
-import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.proto.Entities;
-import com.google.gerrit.proto.testing.SerializedClassSubject;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.protobuf.Parser;
-import org.junit.Test;
-
-public class RevIdProtoConverterTest {
- private final RevIdProtoConverter revIdProtoConverter = RevIdProtoConverter.INSTANCE;
-
- @Test
- public void allValuesConvertedToProto() {
- RevId revId = new RevId("9903402f303249e");
-
- Entities.RevId proto = revIdProtoConverter.toProto(revId);
-
- Entities.RevId expectedProto = Entities.RevId.newBuilder().setId("9903402f303249e").build();
- assertThat(proto).isEqualTo(expectedProto);
- }
-
- @Test
- public void allValuesConvertedToProtoAndBackAgain() {
- RevId revId = new RevId("ff3934a320bb");
-
- RevId convertedRevId = revIdProtoConverter.fromProto(revIdProtoConverter.toProto(revId));
-
- assertThat(convertedRevId).isEqualTo(revId);
- }
-
- @Test
- public void protoCanBeParsedFromBytes() throws Exception {
- Entities.RevId proto = Entities.RevId.newBuilder().setId("9903402f303249e").build();
- byte[] bytes = proto.toByteArray();
-
- Parser<Entities.RevId> parser = revIdProtoConverter.getParser();
- Entities.RevId parsedProto = parser.parseFrom(bytes);
-
- assertThat(parsedProto).isEqualTo(proto);
- }
-
- /** See {@link SerializedClassSubject} for background and what to do if this test fails. */
- @Test
- public void fieldsExistAsExpected() {
- assertThatSerializedClass(RevId.class).hasFields(ImmutableMap.of("id", String.class));
- }
-}
diff --git a/javatests/com/google/gerrit/server/BUILD b/javatests/com/google/gerrit/server/BUILD
index 7b8f602106..585a2727ec 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -30,30 +30,31 @@ junit_tests(
tags = ["no_windows"],
visibility = ["//visibility:public"],
runtime_deps = [
+ "//java/com/google/gerrit/lucene",
"//lib/bouncycastle:bcprov",
"//prolog:gerrit-prolog-common",
],
deps = [
":custom-truth-subjects",
+ "//java/com/google/gerrit/acceptance/testsuite/project",
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/extensions/common/testing:common-test-util",
"//java/com/google/gerrit/git",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
"//java/com/google/gerrit/jgit",
+ "//java/com/google/gerrit/json",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/mail",
"//java/com/google/gerrit/metrics",
- "//java/com/google/gerrit/proto",
"//java/com/google/gerrit/proto/testing",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/account/externalids/testing",
"//java/com/google/gerrit/server/cache/serialize",
"//java/com/google/gerrit/server/cache/testing",
- "//java/com/google/gerrit/server/group/testing",
"//java/com/google/gerrit/server/ioutil",
"//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/project/testing:project-test-util",
@@ -62,19 +63,21 @@ junit_tests(
"//java/com/google/gerrit/server/schema/testing",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/sshd",
+ "//java/com/google/gerrit/testing:assertable-executor",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//java/com/google/gerrit/truth",
"//lib:gson",
"//lib:guava",
"//lib:guava-retrying",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib:protobuf",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/commons:codec",
"//lib/flogger:api",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
+ "//lib/mockito",
"//lib/truth",
"//lib/truth:truth-java8-extension",
"//lib/truth:truth-proto-extension",
diff --git a/javatests/com/google/gerrit/server/ChangeUtilTest.java b/javatests/com/google/gerrit/server/ChangeUtilTest.java
index 5cb474d34f..5f73d2cdf0 100644
--- a/javatests/com/google/gerrit/server/ChangeUtilTest.java
+++ b/javatests/com/google/gerrit/server/ChangeUtilTest.java
@@ -16,11 +16,10 @@ package com.google.gerrit.server;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.regex.Pattern;
import org.junit.Test;
-public class ChangeUtilTest extends GerritBaseTests {
+public class ChangeUtilTest {
@Test
public void changeMessageUuid() throws Exception {
Pattern pat = Pattern.compile("^[0-9a-f]{8}_[0-9a-f]{8}$");
diff --git a/javatests/com/google/gerrit/server/IdentifiedUserTest.java b/javatests/com/google/gerrit/server/IdentifiedUserTest.java
index 485de49720..1672ce139e 100644
--- a/javatests/com/google/gerrit/server/IdentifiedUserTest.java
+++ b/javatests/com/google/gerrit/server/IdentifiedUserTest.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server;
import static com.google.common.truth.Truth.assertThat;
import static com.google.inject.Scopes.SINGLETON;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.FakeRealm;
import com.google.gerrit.server.account.GroupBackend;
@@ -31,7 +31,6 @@ import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.FakeAccountCache;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -45,7 +44,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(ConfigSuite.class)
-public class IdentifiedUserTest extends GerritBaseTests {
+public class IdentifiedUserTest {
@ConfigSuite.Parameter public Config config;
private IdentifiedUser identifiedUser;
@@ -99,8 +98,11 @@ public class IdentifiedUserTest extends GerritBaseTests {
Injector injector = Guice.createInjector(mod);
injector.injectMembers(this);
- Account account = new Account(new Account.Id(1), TimeUtil.nowTs());
- Account.Id ownerId = account.getId();
+ Account account =
+ Account.builder(Account.id(1), TimeUtil.nowTs())
+ .setMetaId("1234567812345678123456781234567812345678")
+ .build();
+ Account.Id ownerId = account.id();
identifiedUser = identifiedUserFactory.create(ownerId);
diff --git a/javatests/com/google/gerrit/server/account/AccountResolverTest.java b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
index c37a9454fc..769370ac37 100644
--- a/javatests/com/google/gerrit/server/account/AccountResolverTest.java
+++ b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
@@ -17,26 +17,24 @@ package com.google.gerrit.server.account;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.AccountResolver.Result;
import com.google.gerrit.server.account.AccountResolver.Searcher;
import com.google.gerrit.server.account.AccountResolver.StringSearcher;
import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
-import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Arrays;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.junit.Test;
-public class AccountResolverTest extends GerritBaseTests {
+public class AccountResolverTest {
private static class TestSearcher extends StringSearcher {
private final String pattern;
private final boolean shortCircuit;
@@ -86,7 +84,7 @@ public class AccountResolverTest extends GerritBaseTests {
@Override
public String toString() {
return accounts.stream()
- .map(a -> a.getAccount().getId().toString())
+ .map(a -> a.account().id().toString())
.collect(joining(",", pattern + "(", ")"));
}
}
@@ -158,7 +156,7 @@ public class AccountResolverTest extends GerritBaseTests {
// Searchers always short-circuit when finding a non-empty result list, and this one didn't
// filter out inactive results, so the second searcher never ran.
assertThat(result.asIdSet()).containsExactlyElementsIn(ids(1));
- assertThat(getOnlyElement(result.asList()).getAccount().isActive()).isFalse();
+ assertThat(getOnlyElement(result.asList()).account().isActive()).isFalse();
assertThat(filteredInactiveIds(result)).isEmpty();
}
@@ -175,7 +173,7 @@ public class AccountResolverTest extends GerritBaseTests {
// and this one didn't filter out inactive results,
// so the second searcher never ran.
assertThat(result.asIdSet()).containsExactlyElementsIn(ids(1));
- assertThat(getOnlyElement(result.asList()).getAccount().isActive()).isFalse();
+ assertThat(getOnlyElement(result.asList()).account().isActive()).isFalse();
assertThat(filteredInactiveIds(result)).isEmpty();
}
@@ -242,15 +240,14 @@ public class AccountResolverTest extends GerritBaseTests {
@Test
public void asUniqueWithNoResults() throws Exception {
- try {
- String input = "foo";
- ImmutableList<Searcher<?>> searchers = ImmutableList.of();
- Supplier<Predicate<AccountState>> visibilitySupplier = allVisible();
- search(input, searchers, visibilitySupplier).asUnique();
- assert_().fail("Expected UnresolvableAccountException");
- } catch (UnresolvableAccountException e) {
- assertThat(e).hasMessageThat().isEqualTo("Account 'foo' not found");
- }
+ String input = "foo";
+ ImmutableList<Searcher<?>> searchers = ImmutableList.of();
+ Supplier<Predicate<AccountState>> visibilitySupplier = allVisible();
+ UnresolvableAccountException thrown =
+ assertThrows(
+ UnresolvableAccountException.class,
+ () -> search(input, searchers, visibilitySupplier).asUnique());
+ assertThat(thrown).hasMessageThat().isEqualTo("Account 'foo' not found");
}
@Test
@@ -258,22 +255,21 @@ public class AccountResolverTest extends GerritBaseTests {
AccountState account = newAccount(1);
ImmutableList<Searcher<?>> searchers =
ImmutableList.of(new TestSearcher("foo", false, account));
- assertThat(search("foo", searchers, allVisible()).asUnique().getAccount().getId())
- .isEqualTo(account.getAccount().getId());
+ assertThat(search("foo", searchers, allVisible()).asUnique().account().id())
+ .isEqualTo(account.account().id());
}
@Test
public void asUniqueWithMultipleResults() throws Exception {
ImmutableList<Searcher<?>> searchers =
ImmutableList.of(new TestSearcher("foo", false, newAccount(1), newAccount(2)));
- try {
- search("foo", searchers, allVisible()).asUnique();
- assert_().fail("Expected UnresolvableAccountException");
- } catch (UnresolvableAccountException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo("Account 'foo' is ambiguous:\n1: Anonymous Name (1)\n2: Anonymous Name (2)");
- }
+ UnresolvableAccountException thrown =
+ assertThrows(
+ UnresolvableAccountException.class,
+ () -> search("foo", searchers, allVisible()).asUnique());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo("Account 'foo' is ambiguous:\n1: Anonymous Name (1)\n2: Anonymous Name (2)");
}
@Test
@@ -359,17 +355,19 @@ public class AccountResolverTest extends GerritBaseTests {
private AccountState newAccount(int id) {
return AccountState.forAccount(
- new AllUsersName("All-Users"), new Account(new Account.Id(id), TimeUtil.nowTs()));
+ Account.builder(Account.id(id), TimeUtil.nowTs())
+ .setMetaId("1234567812345678123456781234567812345678")
+ .build());
}
private AccountState newInactiveAccount(int id) {
- Account a = new Account(new Account.Id(id), TimeUtil.nowTs());
+ Account.Builder a = Account.builder(Account.id(id), TimeUtil.nowTs());
a.setActive(false);
- return AccountState.forAccount(new AllUsersName("All-Users"), a);
+ return AccountState.forAccount(a.build());
}
private static ImmutableSet<Account.Id> ids(int... ids) {
- return Arrays.stream(ids).mapToObj(Account.Id::new).collect(toImmutableSet());
+ return Arrays.stream(ids).mapToObj(Account::id).collect(toImmutableSet());
}
private static Supplier<Predicate<AccountState>> allVisible() {
@@ -377,18 +375,16 @@ public class AccountResolverTest extends GerritBaseTests {
}
private Predicate<AccountState> activityPrediate() {
- return (AccountState accountState) -> accountState.getAccount().isActive();
+ return (AccountState accountState) -> accountState.account().isActive();
}
private static Supplier<Predicate<AccountState>> only(int... ids) {
ImmutableSet<Account.Id> idSet =
- Arrays.stream(ids).mapToObj(Account.Id::new).collect(toImmutableSet());
- return () -> a -> idSet.contains(a.getAccount().getId());
+ Arrays.stream(ids).mapToObj(Account::id).collect(toImmutableSet());
+ return () -> a -> idSet.contains(a.account().id());
}
private static ImmutableSet<Account.Id> filteredInactiveIds(Result result) {
- return result.filteredInactive().stream()
- .map(a -> a.getAccount().getId())
- .collect(toImmutableSet());
+ return result.filteredInactive().stream().map(a -> a.account().id()).collect(toImmutableSet());
}
}
diff --git a/javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java b/javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java
index 51a34f5f1a..1381c751b9 100644
--- a/javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java
+++ b/javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java
@@ -16,14 +16,13 @@ package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Account;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
-public class AuthorizedKeysTest extends GerritBaseTests {
+public class AuthorizedKeysTest {
private static final String KEY1 =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCgug5VyMXQGnem2H1KVC4/HcRcD4zzBqS"
+ "uJBRWVonSSoz3RoAZ7bWXCVVGwchtXwUURD689wFYdiPecOrWOUgeeyRq754YWRhU+W28"
@@ -55,7 +54,7 @@ public class AuthorizedKeysTest extends GerritBaseTests {
+ "zRuEL5e/QOu9yGq9xkWApCmg6edpWAHG+Bx4AldU78MiZvzoB7gMMdxc9RmZ1gYj/DjxV"
+ "w== john.doe@example.com";
- private final Account.Id accountId = new Account.Id(1);
+ private final Account.Id accountId = Account.id(1);
@Test
public void test() throws Exception {
@@ -151,7 +150,7 @@ public class AuthorizedKeysTest extends GerritBaseTests {
private static void assertParse(
StringBuilder authorizedKeys, List<Optional<AccountSshKey>> expectedKeys) {
- Account.Id accountId = new Account.Id(1);
+ Account.Id accountId = Account.id(1);
List<Optional<AccountSshKey>> parsedKeys =
AuthorizedKeys.parse(accountId, authorizedKeys.toString());
assertThat(parsedKeys).containsExactlyElementsIn(expectedKeys);
@@ -171,7 +170,7 @@ public class AuthorizedKeysTest extends GerritBaseTests {
* @return the expected line for this key in the authorized_keys file
*/
private static String addKey(List<Optional<AccountSshKey>> keys, String pub) {
- AccountSshKey key = AccountSshKey.create(new Account.Id(1), keys.size() + 1, pub);
+ AccountSshKey key = AccountSshKey.create(Account.id(1), keys.size() + 1, pub);
keys.add(Optional.of(key));
return key.sshPublicKey() + "\n";
}
@@ -182,7 +181,7 @@ public class AuthorizedKeysTest extends GerritBaseTests {
* @return the expected line for this key in the authorized_keys file
*/
private static String addInvalidKey(List<Optional<AccountSshKey>> keys, String pub) {
- AccountSshKey key = AccountSshKey.createInvalid(new Account.Id(1), keys.size() + 1, pub);
+ AccountSshKey key = AccountSshKey.createInvalid(Account.id(1), keys.size() + 1, pub);
keys.add(Optional.of(key));
return AuthorizedKeys.INVALID_KEY_COMMENT_PREFIX + key.sshPublicKey() + "\n";
}
diff --git a/javatests/com/google/gerrit/server/account/DestinationListTest.java b/javatests/com/google/gerrit/server/account/DestinationListTest.java
index e51b041569..4188f39df7 100644
--- a/javatests/com/google/gerrit/server/account/DestinationListTest.java
+++ b/javatests/com/google/gerrit/server/account/DestinationListTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.ValidationError;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
@@ -27,7 +26,7 @@ import java.util.List;
import java.util.Set;
import org.junit.Test;
-public class DestinationListTest extends GerritBaseTests {
+public class DestinationListTest {
public static final String R_FOO = "refs/heads/foo";
public static final String R_BAR = "refs/heads/bar";
@@ -55,11 +54,11 @@ public class DestinationListTest extends GerritBaseTests {
public static final String LABEL = "label";
public static final String LABEL2 = "another";
- public static final Branch.NameKey B_FOO = dest(P_MY, R_FOO);
- public static final Branch.NameKey B_BAR = dest(P_SLASH, R_BAR);
- public static final Branch.NameKey B_COMPLEX = dest(P_COMPLEX, R_FOO);
+ public static final BranchNameKey B_FOO = dest(P_MY, R_FOO);
+ public static final BranchNameKey B_BAR = dest(P_SLASH, R_BAR);
+ public static final BranchNameKey B_COMPLEX = dest(P_COMPLEX, R_FOO);
- public static final Set<Branch.NameKey> D_SIMPLE = new HashSet<>();
+ public static final Set<BranchNameKey> D_SIMPLE = new HashSet<>();
static {
D_SIMPLE.clear();
@@ -67,15 +66,15 @@ public class DestinationListTest extends GerritBaseTests {
D_SIMPLE.add(B_BAR);
}
- private static Branch.NameKey dest(String project, String ref) {
- return new Branch.NameKey(new Project.NameKey(project), ref);
+ private static BranchNameKey dest(String project, String ref) {
+ return BranchNameKey.create(Project.nameKey(project), ref);
}
@Test
public void testParseSimple() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_SIMPLE, null);
- Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
+ Set<BranchNameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@@ -83,7 +82,7 @@ public class DestinationListTest extends GerritBaseTests {
public void testParseWHeader() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, HEADER + F_SIMPLE, null);
- Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
+ Set<BranchNameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@@ -91,7 +90,7 @@ public class DestinationListTest extends GerritBaseTests {
public void testParseWComments() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, C1 + F_SIMPLE + C2, null);
- Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
+ Set<BranchNameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@@ -99,7 +98,7 @@ public class DestinationListTest extends GerritBaseTests {
public void testParseFooComment() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, "#" + L_FOO + L_BAR, null);
- Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
+ Set<BranchNameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).doesNotContain(B_FOO);
assertThat(branches).contains(B_BAR);
}
@@ -108,7 +107,7 @@ public class DestinationListTest extends GerritBaseTests {
public void testParsePaddedFronts() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_PAD_F, null);
- Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
+ Set<BranchNameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@@ -116,7 +115,7 @@ public class DestinationListTest extends GerritBaseTests {
public void testParsePaddedEnds() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_PAD_E, null);
- Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
+ Set<BranchNameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
}
@@ -124,7 +123,7 @@ public class DestinationListTest extends GerritBaseTests {
public void testParseComplex() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, L_COMPLEX, null);
- Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
+ Set<BranchNameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).contains(B_COMPLEX);
}
@@ -140,7 +139,7 @@ public class DestinationListTest extends GerritBaseTests {
public void testParse2Labels() throws Exception {
DestinationList dl = new DestinationList();
dl.parseLabel(LABEL, F_SIMPLE, null);
- Set<Branch.NameKey> branches = dl.getDestinations(LABEL);
+ Set<BranchNameKey> branches = dl.getDestinations(LABEL);
assertThat(branches).containsExactlyElementsIn(D_SIMPLE);
dl.parseLabel(LABEL2, L_COMPLEX, null);
diff --git a/javatests/com/google/gerrit/server/account/GroupUUIDTest.java b/javatests/com/google/gerrit/server/account/GroupUUIDTest.java
index 70887e6c38..a155d7fc32 100644
--- a/javatests/com/google/gerrit/server/account/GroupUUIDTest.java
+++ b/javatests/com/google/gerrit/server/account/GroupUUIDTest.java
@@ -16,12 +16,11 @@ package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.AccountGroup;
import org.eclipse.jgit.lib.PersonIdent;
import org.junit.Test;
-public class GroupUUIDTest extends GerritBaseTests {
+public class GroupUUIDTest {
@Test
public void createdUuidsForSameInputShouldBeDifferent() {
String groupName = "Users";
diff --git a/javatests/com/google/gerrit/server/account/HashedPasswordTest.java b/javatests/com/google/gerrit/server/account/HashedPasswordTest.java
index 9a0c9cb972..34437201d3 100644
--- a/javatests/com/google/gerrit/server/account/HashedPasswordTest.java
+++ b/javatests/com/google/gerrit/server/account/HashedPasswordTest.java
@@ -15,13 +15,12 @@
package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.common.base.Strings;
-import com.google.gerrit.testing.GerritBaseTests;
-import org.apache.commons.codec.DecoderException;
+import com.google.gerrit.server.account.HashedPassword.DecoderException;
import org.junit.Test;
-public class HashedPasswordTest extends GerritBaseTests {
+public class HashedPasswordTest {
@Test
public void encodeOneLine() throws Exception {
@@ -41,17 +40,9 @@ public class HashedPasswordTest extends GerritBaseTests {
assertThat(roundtrip.checkPassword("not the password")).isFalse();
}
- @Test(expected = DecoderException.class)
- public void invalidDecode() throws Exception {
- HashedPassword.decode("invalid");
- }
-
@Test
- public void lengthLimit() throws Exception {
- String password = Strings.repeat("1", 72);
-
- // make sure it fits in varchar(255).
- assertThat(HashedPassword.fromPassword(password).encode().length()).isLessThan(255);
+ public void invalidDecode() throws Exception {
+ assertThrows(DecoderException.class, () -> HashedPassword.decode("invalid"));
}
@Test
diff --git a/javatests/com/google/gerrit/server/account/PreferencesTest.java b/javatests/com/google/gerrit/server/account/PreferencesTest.java
new file mode 100644
index 0000000000..9866481de8
--- /dev/null
+++ b/javatests/com/google/gerrit/server/account/PreferencesTest.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.EditPreferencesInfo;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.json.OutputFormat;
+import com.google.gson.Gson;
+import org.junit.Test;
+
+public class PreferencesTest {
+
+ private static final Gson GSON = OutputFormat.JSON_COMPACT.newGson();
+
+ @Test
+ public void generalPreferencesRoundTrip() {
+ GeneralPreferencesInfo original = GeneralPreferencesInfo.defaults();
+ assertThat(GSON.toJson(original))
+ .isEqualTo(GSON.toJson(Preferences.General.fromInfo(original).toInfo()));
+ }
+
+ @Test
+ public void diffPreferencesRoundTrip() {
+ DiffPreferencesInfo original = DiffPreferencesInfo.defaults();
+ assertThat(GSON.toJson(original))
+ .isEqualTo(GSON.toJson(Preferences.Diff.fromInfo(original).toInfo()));
+ }
+
+ @Test
+ public void editPreferencesRoundTrip() {
+ EditPreferencesInfo original = EditPreferencesInfo.defaults();
+ assertThat(GSON.toJson(original))
+ .isEqualTo(GSON.toJson(Preferences.Edit.fromInfo(original).toInfo()));
+ }
+}
diff --git a/javatests/com/google/gerrit/server/account/QueryListTest.java b/javatests/com/google/gerrit/server/account/QueryListTest.java
index a0876e1ad9..7d491c96e8 100644
--- a/javatests/com/google/gerrit/server/account/QueryListTest.java
+++ b/javatests/com/google/gerrit/server/account/QueryListTest.java
@@ -17,12 +17,11 @@ package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.server.git.ValidationError;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
-public class QueryListTest extends GerritBaseTests {
+public class QueryListTest {
public static final String Q_P = "project:foo";
public static final String Q_B = "branch:bar";
public static final String Q_COMPLEX = "branch:bar AND peers:'is:open\t'";
diff --git a/javatests/com/google/gerrit/server/account/StoredPreferencesTest.java b/javatests/com/google/gerrit/server/account/StoredPreferencesTest.java
new file mode 100644
index 0000000000..c39e496220
--- /dev/null
+++ b/javatests/com/google/gerrit/server/account/StoredPreferencesTest.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
+import com.google.gerrit.server.git.ValidationError;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/** Tests for parsing user preferences from Git. */
+public class StoredPreferencesTest {
+
+ enum Unknown {
+ STATE
+ }
+
+ @Test
+ public void ignoreUnknownAccountPreferencesWhenParsing() {
+ ValidationError.Sink errorSink = Mockito.mock(ValidationError.Sink.class);
+ StoredPreferences preferences =
+ new StoredPreferences(Account.id(1), configWithUnknownEntries(), new Config(), errorSink);
+ GeneralPreferencesInfo parsedPreferences = preferences.getGeneralPreferences();
+
+ assertThat(parsedPreferences).isNotNull();
+ assertThat(parsedPreferences.expandInlineDiffs).isTrue();
+ verifyNoMoreInteractions(errorSink);
+ }
+
+ @Test
+ public void ignoreUnknownDefaultAccountPreferencesWhenParsing() {
+ ValidationError.Sink errorSink = Mockito.mock(ValidationError.Sink.class);
+ StoredPreferences preferences =
+ new StoredPreferences(Account.id(1), new Config(), configWithUnknownEntries(), errorSink);
+ GeneralPreferencesInfo parsedPreferences = preferences.getGeneralPreferences();
+
+ assertThat(parsedPreferences).isNotNull();
+ assertThat(parsedPreferences.expandInlineDiffs).isTrue();
+ verifyNoMoreInteractions(errorSink);
+ }
+
+ private static Config configWithUnknownEntries() {
+ Config cfg = new Config();
+ cfg.setBoolean("general", null, "expandInlineDiffs", true);
+ cfg.setBoolean("general", null, "unknown", true);
+ cfg.setEnum("general", null, "unknownenum", Unknown.STATE);
+ cfg.setString("general", null, "unknownstring", "bla");
+ return cfg;
+ }
+}
diff --git a/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java b/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
index 334c627a96..1e3063ec03 100644
--- a/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
+++ b/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
@@ -17,34 +17,32 @@ package com.google.gerrit.server.account;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.getCurrentArguments;
-import static org.easymock.EasyMock.not;
-import static org.easymock.EasyMock.replay;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.AccountGroup.UUID;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.plugincontext.PluginContext.PluginMetrics;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
-public class UniversalGroupBackendTest extends GerritBaseTests {
- private static final AccountGroup.UUID OTHER_UUID = new AccountGroup.UUID("other");
+public class UniversalGroupBackendTest {
+ private static final AccountGroup.UUID OTHER_UUID = AccountGroup.uuid("other");
private UniversalGroupBackend backend;
private IdentifiedUser user;
@@ -53,8 +51,7 @@ public class UniversalGroupBackendTest extends GerritBaseTests {
@Before
public void setup() {
- user = createNiceMock(IdentifiedUser.class);
- replay(user);
+ user = mock(IdentifiedUser.class);
backends = new DynamicSet<>();
backends.add("gerrit", new SystemGroupBackend(new Config()));
backend =
@@ -102,25 +99,26 @@ public class UniversalGroupBackendTest extends GerritBaseTests {
@Test
public void otherMemberships() {
- final AccountGroup.UUID handled = new AccountGroup.UUID("handled");
- final AccountGroup.UUID notHandled = new AccountGroup.UUID("not handled");
- final IdentifiedUser member = createNiceMock(IdentifiedUser.class);
- final IdentifiedUser notMember = createNiceMock(IdentifiedUser.class);
-
- GroupBackend backend = createMock(GroupBackend.class);
- expect(backend.handles(handled)).andStubReturn(true);
- expect(backend.handles(not(eq(handled)))).andStubReturn(false);
- expect(backend.membershipsOf(anyObject(IdentifiedUser.class)))
- .andStubAnswer(
- () -> {
- Object[] args = getCurrentArguments();
- GroupMembership membership = createMock(GroupMembership.class);
- expect(membership.contains(eq(handled))).andStubReturn(args[0] == member);
- expect(membership.contains(not(eq(notHandled)))).andStubReturn(false);
- replay(membership);
- return membership;
+ final AccountGroup.UUID handled = AccountGroup.uuid("handled");
+ final AccountGroup.UUID notHandled = AccountGroup.uuid("not handled");
+ final IdentifiedUser member = mock(IdentifiedUser.class);
+ final IdentifiedUser notMember = mock(IdentifiedUser.class);
+
+ GroupBackend backend = mock(GroupBackend.class);
+ when(backend.handles(eq(handled))).thenReturn(true);
+ when(backend.handles(not(eq(handled)))).thenReturn(false);
+ when(backend.membershipsOf(any(IdentifiedUser.class)))
+ .thenAnswer(
+ new Answer<GroupMembership>() {
+ @Override
+ public GroupMembership answer(InvocationOnMock invocation) {
+ GroupMembership membership = mock(GroupMembership.class);
+ when(membership.contains(eq(handled)))
+ .thenReturn(invocation.getArguments()[0] == member);
+ when(membership.contains(eq(notHandled))).thenReturn(false);
+ return membership;
+ }
});
- replay(member, notMember, backend);
backends = new DynamicSet<>();
backends.add("gerrit", backend);
diff --git a/javatests/com/google/gerrit/server/account/WatchConfigTest.java b/javatests/com/google/gerrit/server/account/WatchConfigTest.java
index 2ac7be70c5..95dbbde2f3 100644
--- a/javatests/com/google/gerrit/server/account/WatchConfigTest.java
+++ b/javatests/com/google/gerrit/server/account/WatchConfigTest.java
@@ -15,10 +15,11 @@
package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.account.ProjectWatches.NotifyValue;
import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
@@ -54,12 +55,12 @@ public class WatchConfigTest implements ValidationError.Sink {
+ " notify = [NEW_PATCHSETS]\n"
+ " notify = * [NEW_PATCHSETS, ALL_COMMENTS]\n");
Map<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches =
- ProjectWatches.parse(new Account.Id(1000000), cfg, this);
+ ProjectWatches.parse(Account.id(1000000), cfg, this);
assertThat(validationErrors).isEmpty();
- Project.NameKey myProject = new Project.NameKey("myProject");
- Project.NameKey otherProject = new Project.NameKey("otherProject");
+ Project.NameKey myProject = Project.nameKey("myProject");
+ Project.NameKey otherProject = Project.nameKey("otherProject");
Map<ProjectWatchKey, Set<NotifyType>> expectedProjectWatches = new HashMap<>();
expectedProjectWatches.put(
ProjectWatchKey.create(myProject, null),
@@ -87,7 +88,7 @@ public class WatchConfigTest implements ValidationError.Sink {
+ "[project \"otherProject\"]\n"
+ " notify = [NEW_PATCHSETS]\n");
- ProjectWatches.parse(new Account.Id(1000000), cfg, this);
+ ProjectWatches.parse(Account.id(1000000), cfg, this);
assertThat(validationErrors).hasSize(1);
assertThat(validationErrors.get(0).getMessage())
.isEqualTo(
@@ -170,14 +171,14 @@ public class WatchConfigTest implements ValidationError.Sink {
private void assertParseNotifyValueFails(String notifyValue) {
assertThat(validationErrors).isEmpty();
parseNotifyValue(notifyValue);
- assertThat(validationErrors)
- .named("expected validation error for notifyValue: " + notifyValue)
+ assertWithMessage("expected validation error for notifyValue: " + notifyValue)
+ .that(validationErrors)
.isNotEmpty();
validationErrors.clear();
}
private NotifyValue parseNotifyValue(String notifyValue) {
- return NotifyValue.parse(new Account.Id(1000000), "project", notifyValue, this);
+ return NotifyValue.parse(Account.id(1000000), "project", notifyValue, this);
}
@Override
diff --git a/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java b/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
index d757f7135b..45dacd9e3e 100644
--- a/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
+++ b/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
@@ -21,17 +21,16 @@ import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byt
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.AllExternalIds.Serializer;
import com.google.gerrit.server.cache.proto.Cache.AllExternalIdsProto;
import com.google.gerrit.server.cache.proto.Cache.AllExternalIdsProto.ExternalIdProto;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.TypeLiteral;
import java.util.Arrays;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class AllExternalIdsTest extends GerritBaseTests {
+public class AllExternalIdsTest {
@Test
public void serializeEmptyExternalIds() throws Exception {
assertRoundTrip(allExternalIds(), AllExternalIdsProto.getDefaultInstance());
@@ -39,8 +38,8 @@ public class AllExternalIdsTest extends GerritBaseTests {
@Test
public void serializeMultipleExternalIds() throws Exception {
- Account.Id accountId1 = new Account.Id(1001);
- Account.Id accountId2 = new Account.Id(1002);
+ Account.Id accountId1 = Account.id(1001);
+ Account.Id accountId2 = Account.id(1002);
assertRoundTrip(
allExternalIds(
ExternalId.create("scheme1", "id1", accountId1),
@@ -62,7 +61,7 @@ public class AllExternalIdsTest extends GerritBaseTests {
@Test
public void serializeExternalIdWithEmail() throws Exception {
assertRoundTrip(
- allExternalIds(ExternalId.createEmail(new Account.Id(1001), "foo@example.com")),
+ allExternalIds(ExternalId.createEmail(Account.id(1001), "foo@example.com")),
AllExternalIdsProto.newBuilder()
.addExternalId(
ExternalIdProto.newBuilder()
@@ -76,7 +75,7 @@ public class AllExternalIdsTest extends GerritBaseTests {
public void serializeExternalIdWithPassword() throws Exception {
assertRoundTrip(
allExternalIds(
- ExternalId.create("scheme", "id", new Account.Id(1001), null, "hashed password")),
+ ExternalId.create("scheme", "id", Account.id(1001), null, "hashed password")),
AllExternalIdsProto.newBuilder()
.addExternalId(
ExternalIdProto.newBuilder()
@@ -91,7 +90,7 @@ public class AllExternalIdsTest extends GerritBaseTests {
assertRoundTrip(
allExternalIds(
ExternalId.create(
- ExternalId.create("scheme", "id", new Account.Id(1001)),
+ ExternalId.create("scheme", "id", Account.id(1001)),
ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))),
AllExternalIdsProto.newBuilder()
.addExternalId(
diff --git a/javatests/com/google/gerrit/server/account/externalids/ExternalIDCacheLoaderTest.java b/javatests/com/google/gerrit/server/account/externalids/ExternalIDCacheLoaderTest.java
new file mode 100644
index 0000000000..054b1aa564
--- /dev/null
+++ b/javatests/com/google/gerrit/server/account/externalids/ExternalIDCacheLoaderTest.java
@@ -0,0 +1,307 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account.externalids;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.cache.Cache;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.server.account.externalids.testing.ExternalIdTestUtil;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.AllUsersNameProvider;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.testing.InMemoryRepositoryManager;
+import com.google.inject.util.Providers;
+import java.io.IOException;
+import java.util.function.Consumer;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ExternalIDCacheLoaderTest {
+ private static AllUsersName ALL_USERS = new AllUsersName(AllUsersNameProvider.DEFAULT);
+
+ @Mock Cache<ObjectId, AllExternalIds> externalIdCache;
+
+ private ExternalIdCacheLoader loader;
+ private GitRepositoryManager repoManager = new InMemoryRepositoryManager();
+ private ExternalIdReader externalIdReader;
+ private ExternalIdReader externalIdReaderSpy;
+
+ @Before
+ public void setUp() throws Exception {
+ repoManager.createRepository(ALL_USERS).close();
+ externalIdReader = new ExternalIdReader(repoManager, ALL_USERS, new DisabledMetricMaker());
+ externalIdReaderSpy = Mockito.spy(externalIdReader);
+ loader = createLoader(true);
+ }
+
+ @Test
+ public void worksOnSingleCommit() throws Exception {
+ ObjectId firstState = insertExternalId(1, 1);
+ assertThat(loader.load(firstState)).isEqualTo(allFromGit(firstState));
+ verify(externalIdReaderSpy, times(1)).all(firstState);
+ }
+
+ @Test
+ public void reloadsSingleUpdateUsingPartialReload() throws Exception {
+ ObjectId firstState = insertExternalId(1, 1);
+ ObjectId head = insertExternalId(2, 2);
+
+ when(externalIdCache.getIfPresent(firstState)).thenReturn(allFromGit(firstState));
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verifyZeroInteractions(externalIdReaderSpy);
+ }
+
+ @Test
+ public void reloadsMultipleUpdatesUsingPartialReload() throws Exception {
+ ObjectId firstState = insertExternalId(1, 1);
+ insertExternalId(2, 2);
+ insertExternalId(3, 3);
+ ObjectId head = insertExternalId(4, 4);
+
+ when(externalIdCache.getIfPresent(firstState)).thenReturn(allFromGit(firstState));
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verifyZeroInteractions(externalIdReaderSpy);
+ }
+
+ @Test
+ public void reloadsAllExternalIdsWhenNoOldStateIsCached() throws Exception {
+ ObjectId firstState = insertExternalId(1, 1);
+ ObjectId head = insertExternalId(2, 2);
+
+ when(externalIdCache.getIfPresent(firstState)).thenReturn(null);
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verify(externalIdReaderSpy, times(1)).all(head);
+ }
+
+ @Test
+ public void partialReloadingDisabledAlwaysTriggersFullReload() throws Exception {
+ loader = createLoader(false);
+ insertExternalId(1, 1);
+ ObjectId head = insertExternalId(2, 2);
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verify(externalIdReaderSpy, times(1)).all(head);
+ }
+
+ @Test
+ public void fallsBackToFullReloadOnManyUpdatesOnBranch() throws Exception {
+ insertExternalId(1, 1);
+ ObjectId head = null;
+ for (int i = 2; i < 20; i++) {
+ head = insertExternalId(i, i);
+ }
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verify(externalIdReaderSpy, times(1)).all(head);
+ }
+
+ @Test
+ public void doesFullReloadWhenNoCacheStateIsFound() throws Exception {
+ ObjectId head = insertExternalId(1, 1);
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verify(externalIdReaderSpy, times(1)).all(head);
+ }
+
+ @Test
+ public void handlesDeletionInPartialReload() throws Exception {
+ ObjectId firstState = insertExternalId(1, 1);
+ ObjectId head = deleteExternalId(1, 1);
+ assertThat(allFromGit(head).byAccount().size()).isEqualTo(0);
+
+ when(externalIdCache.getIfPresent(firstState)).thenReturn(allFromGit(firstState));
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verifyZeroInteractions(externalIdReaderSpy);
+ }
+
+ @Test
+ public void handlesModifyInPartialReload() throws Exception {
+ ObjectId firstState = insertExternalId(1, 1);
+ ObjectId head =
+ modifyExternalId(
+ externalId(1, 1),
+ ExternalId.create("fooschema", "bar1", Account.id(1), "foo@bar.com", "password"));
+ assertThat(allFromGit(head).byAccount().size()).isEqualTo(1);
+
+ when(externalIdCache.getIfPresent(firstState)).thenReturn(allFromGit(firstState));
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verifyZeroInteractions(externalIdReaderSpy);
+ }
+
+ @Test
+ public void ignoresInvalidExternalId() throws Exception {
+ ObjectId firstState = insertExternalId(1, 1);
+ ObjectId head;
+ try (Repository repo = repoManager.openRepository(ALL_USERS);
+ RevWalk rw = new RevWalk(repo)) {
+ ExternalIdTestUtil.insertExternalIdWithKeyThatDoesntMatchNoteId(
+ repo, rw, new PersonIdent("foo", "foo@bar.com"), Account.id(2), "test");
+ head = repo.exactRef(RefNames.REFS_EXTERNAL_IDS).getObjectId();
+ }
+
+ when(externalIdCache.getIfPresent(firstState)).thenReturn(allFromGit(firstState));
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verifyZeroInteractions(externalIdReaderSpy);
+ }
+
+ @Test
+ public void handlesTreePrefixesInDifferentialReload() throws Exception {
+ // Create more than 256 notes (NoteMap's current sharding limit) and check that we really have
+ // created a situation where NoteNames are sharded.
+ ObjectId oldState = inserExternalIds(257);
+ assertAllFilesHaveSlashesInPath();
+ ObjectId head = insertExternalId(500, 500);
+
+ when(externalIdCache.getIfPresent(oldState)).thenReturn(allFromGit(oldState));
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verifyZeroInteractions(externalIdReaderSpy);
+ }
+
+ @Test
+ public void handlesReshard() throws Exception {
+ // Create 256 notes (NoteMap's current sharding limit) and check that we are not yet sharding
+ ObjectId oldState = inserExternalIds(256);
+ assertNoFilesHaveSlashesInPath();
+ // Create one more external ID and then have the Loader compute the new state
+ ObjectId head = insertExternalId(500, 500);
+ assertAllFilesHaveSlashesInPath(); // NoteMap resharded
+
+ when(externalIdCache.getIfPresent(oldState)).thenReturn(allFromGit(oldState));
+
+ assertThat(loader.load(head)).isEqualTo(allFromGit(head));
+ verifyZeroInteractions(externalIdReaderSpy);
+ }
+
+ private ExternalIdCacheLoader createLoader(boolean allowPartial) {
+ Config cfg = new Config();
+ cfg.setBoolean("cache", "external_ids_map", "enablePartialReloads", allowPartial);
+ return new ExternalIdCacheLoader(
+ repoManager,
+ ALL_USERS,
+ externalIdReaderSpy,
+ Providers.of(externalIdCache),
+ new DisabledMetricMaker(),
+ cfg);
+ }
+
+ private AllExternalIds allFromGit(ObjectId revision) throws Exception {
+ return AllExternalIds.create(externalIdReader.all(revision));
+ }
+
+ private ObjectId inserExternalIds(int numberOfIdsToInsert) throws Exception {
+ ObjectId oldState = null;
+ // Create more than 256 notes (NoteMap's current sharding limit) and check that we really have
+ // created a situation where NoteNames are sharded.
+ for (int i = 0; i < numberOfIdsToInsert; i++) {
+ oldState = insertExternalId(i, i);
+ }
+ return oldState;
+ }
+
+ private ObjectId insertExternalId(int key, int accountId) throws Exception {
+ return performExternalIdUpdate(
+ u -> {
+ try {
+ u.insert(externalId(key, accountId));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ private ObjectId modifyExternalId(ExternalId oldId, ExternalId newId) throws Exception {
+ return performExternalIdUpdate(
+ u -> {
+ try {
+ u.replace(oldId, newId);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ private ObjectId deleteExternalId(int key, int accountId) throws Exception {
+ return performExternalIdUpdate(u -> u.delete(externalId(key, accountId)));
+ }
+
+ private ExternalId externalId(int key, int accountId) {
+ return ExternalId.create("fooschema", "bar" + key, Account.id(accountId));
+ }
+
+ private ObjectId performExternalIdUpdate(Consumer<ExternalIdNotes> update) throws Exception {
+ try (Repository repo = repoManager.openRepository(ALL_USERS)) {
+ PersonIdent updater = new PersonIdent("Foo bar", "foo@bar.com");
+ ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(ALL_USERS, repo);
+ update.accept(extIdNotes);
+ try (MetaDataUpdate metaDataUpdate =
+ new MetaDataUpdate(GitReferenceUpdated.DISABLED, null, repo)) {
+ metaDataUpdate.getCommitBuilder().setAuthor(updater);
+ metaDataUpdate.getCommitBuilder().setCommitter(updater);
+ return extIdNotes.commit(metaDataUpdate).getId();
+ }
+ }
+ }
+
+ private void assertAllFilesHaveSlashesInPath() throws Exception {
+ assertThat(allFilesInExternalIdRef().stream().allMatch(f -> f.contains("/"))).isTrue();
+ }
+
+ private void assertNoFilesHaveSlashesInPath() throws Exception {
+ assertThat(allFilesInExternalIdRef().stream().noneMatch(f -> f.contains("/"))).isTrue();
+ }
+
+ private ImmutableList<String> allFilesInExternalIdRef() throws Exception {
+ try (Repository repo = repoManager.openRepository(ALL_USERS);
+ TreeWalk treeWalk = new TreeWalk(repo);
+ RevWalk rw = new RevWalk(repo)) {
+ treeWalk.reset(
+ rw.parseCommit(repo.exactRef(RefNames.REFS_EXTERNAL_IDS).getObjectId()).getTree());
+ treeWalk.setRecursive(true);
+ ImmutableList.Builder<String> allPaths = ImmutableList.builder();
+ while (treeWalk.next()) {
+ allPaths.add(treeWalk.getPathString());
+ }
+ return allPaths.build();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java b/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java
index 13de3e7ad0..ba40d8c211 100644
--- a/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java
+++ b/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java
@@ -24,8 +24,8 @@ import static com.google.gerrit.server.auth.ldap.LdapModule.PARENT_GROUPS_CACHE;
import static com.google.gerrit.server.auth.ldap.LdapModule.USERNAME_CACHE;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.testing.InMemoryModule;
@@ -64,7 +64,7 @@ public final class LdapRealmTest {
}
private ExternalId id(String scheme, String id) {
- return ExternalId.create(scheme, id, new Account.Id(1000));
+ return ExternalId.create(scheme, id, Account.id(1000));
}
private boolean accountBelongsToRealm(ExternalId... ids) {
diff --git a/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java b/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java
index 7a0661a59b..dc62a61017 100644
--- a/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java
+++ b/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java
@@ -19,7 +19,7 @@ import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_EXT
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
-import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Guice;
@@ -39,7 +39,7 @@ public final class OAuthRealmTest {
}
private ExternalId id(String scheme, String id) {
- return ExternalId.create(scheme, id, new Account.Id(1000));
+ return ExternalId.create(scheme, id, Account.id(1000));
}
private boolean accountBelongsToRealm(ExternalId... ids) {
diff --git a/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java b/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
index 6a42577d1e..d19073db2b 100644
--- a/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
+++ b/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.cache;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.function.Supplier;
import org.junit.Test;
-public class PerThreadCacheTest extends GerritBaseTests {
+public class PerThreadCacheTest {
@Test
public void key_respectsClass() {
assertThat(PerThreadCache.Key.create(String.class))
@@ -75,9 +75,9 @@ public class PerThreadCacheTest extends GerritBaseTests {
@Test
public void doubleInstantiationFails() {
try (PerThreadCache ignored = PerThreadCache.create()) {
- exception.expect(IllegalStateException.class);
- exception.expectMessage("called create() twice on the same request");
- PerThreadCache.create();
+ IllegalStateException thrown =
+ assertThrows(IllegalStateException.class, () -> PerThreadCache.create());
+ assertThat(thrown).hasMessageThat().contains("called create() twice on the same request");
}
}
diff --git a/javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java b/javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java
index 147aeeb58c..69c2799f2d 100644
--- a/javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java
+++ b/javatests/com/google/gerrit/server/cache/h2/H2CacheTest.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.cache.h2;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
@@ -66,22 +67,22 @@ public class H2CacheTest {
return "bar";
}))
.isEqualTo("bar");
- assertThat(called.get()).named("Callable was called").isTrue();
- assertThat(impl.getIfPresent("foo")).named("in-memory value").isEqualTo("bar");
+ assertWithMessage("Callable was called").that(called.get()).isTrue();
+ assertWithMessage("in-memory value").that(impl.getIfPresent("foo")).isEqualTo("bar");
mem.invalidate("foo");
- assertThat(impl.getIfPresent("foo")).named("persistent value").isEqualTo("bar");
+ assertWithMessage("persistent value").that(impl.getIfPresent("foo")).isEqualTo("bar");
called.set(false);
- assertThat(
+ assertWithMessage("cached value")
+ .that(
impl.get(
"foo",
() -> {
called.set(true);
return "baz";
}))
- .named("cached value")
.isEqualTo("bar");
- assertThat(called.get()).named("Callable was called").isFalse();
+ assertWithMessage("Callable was called").that(called.get()).isFalse();
}
@Test
diff --git a/javatests/com/google/gerrit/server/cache/serialize/BUILD b/javatests/com/google/gerrit/server/cache/serialize/BUILD
index a0d5ea6767..ce5f273a38 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/BUILD
+++ b/javatests/com/google/gerrit/server/cache/serialize/BUILD
@@ -4,16 +4,16 @@ junit_tests(
name = "tests",
srcs = glob(["*.java"]),
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server/cache/serialize",
"//java/com/google/gerrit/server/cache/testing",
"//java/com/google/gerrit/testing:gerrit-test-util",
- "//java/com/google/gwtorm",
"//lib:guava",
+ "//lib:jgit",
"//lib:junit",
"//lib:protobuf",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/truth",
"//lib/truth:truth-proto-extension",
"//proto:cache_java_proto",
diff --git a/javatests/com/google/gerrit/server/cache/serialize/BooleanCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/BooleanCacheSerializerTest.java
index c634a785bc..ebd7d5553f 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/BooleanCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/BooleanCacheSerializerTest.java
@@ -15,14 +15,12 @@
package com.google.gerrit.server.cache.serialize;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.protobuf.TextFormat;
import org.junit.Test;
-public class BooleanCacheSerializerTest extends GerritBaseTests {
+public class BooleanCacheSerializerTest {
@Test
public void serialize() throws Exception {
assertThat(BooleanCacheSerializer.INSTANCE.serialize(true))
@@ -53,11 +51,6 @@ public class BooleanCacheSerializerTest extends GerritBaseTests {
}
private static void assertDeserializeFails(byte[] in) {
- try {
- BooleanCacheSerializer.INSTANCE.deserialize(in);
- assert_().fail("expected deserialization to fail for \"%s\"", TextFormat.escapeBytes(in));
- } catch (RuntimeException e) {
- // Expected.
- }
+ assertThrows(RuntimeException.class, () -> BooleanCacheSerializer.INSTANCE.deserialize(in));
}
}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/CacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/CacheSerializerTest.java
new file mode 100644
index 0000000000..819189f96c
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/CacheSerializerTest.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache.serialize;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Converter;
+import org.junit.Test;
+
+public class CacheSerializerTest {
+ @AutoValue
+ abstract static class MyAutoValue {
+ static MyAutoValue create(int val) {
+ return new AutoValue_CacheSerializerTest_MyAutoValue(val);
+ }
+
+ abstract int val();
+ }
+
+ private static final CacheSerializer<MyAutoValue> SERIALIZER =
+ CacheSerializer.convert(
+ IntegerCacheSerializer.INSTANCE, Converter.from(MyAutoValue::val, MyAutoValue::create));
+
+ @Test
+ public void serialize() throws Exception {
+ MyAutoValue v = MyAutoValue.create(1234);
+ byte[] serialized = SERIALIZER.serialize(v);
+ assertThat(serialized).isEqualTo(new byte[] {-46, 9});
+ assertThat(SERIALIZER.deserialize(serialized).val()).isEqualTo(1234);
+ }
+
+ @Test
+ public void deserializeNullFails() throws Exception {
+ assertThrows(RuntimeException.class, () -> SERIALIZER.deserialize(null));
+ }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java
index c6efc21513..7bfcc590cb 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java
@@ -15,13 +15,12 @@
package com.google.gerrit.server.cache.serialize;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class EnumCacheSerializerTest extends GerritBaseTests {
+public class EnumCacheSerializerTest {
@Test
public void serialize() throws Exception {
assertRoundTrip(MyEnum.FOO);
@@ -50,11 +49,6 @@ public class EnumCacheSerializerTest extends GerritBaseTests {
private static void assertDeserializeFails(byte[] in) {
CacheSerializer<MyEnum> s = new EnumCacheSerializer<>(MyEnum.class);
- try {
- s.deserialize(in);
- assert_().fail("expected RuntimeException");
- } catch (RuntimeException e) {
- // Expected.
- }
+ assertThrows(RuntimeException.class, () -> s.deserialize(in));
}
}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java
deleted file mode 100644
index 56dd6ad5a2..0000000000
--- a/javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.cache.serialize;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
-
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.client.Key;
-import org.junit.Test;
-
-public class IntKeyCacheSerializerTest extends GerritBaseTests {
-
- private static class MyIntKey extends IntKey<Key<?>> {
- private static final long serialVersionUID = 1L;
-
- private int val;
-
- MyIntKey(int val) {
- this.val = val;
- }
-
- @Override
- public int get() {
- return val;
- }
-
- @Override
- protected void set(int newValue) {
- this.val = newValue;
- }
- }
-
- private static final IntKeyCacheSerializer<MyIntKey> SERIALIZER =
- new IntKeyCacheSerializer<>(MyIntKey::new);
-
- @Test
- public void serialize() throws Exception {
- MyIntKey k = new MyIntKey(1234);
- byte[] serialized = SERIALIZER.serialize(k);
- assertThat(serialized).isEqualTo(new byte[] {-46, 9});
- assertThat(SERIALIZER.deserialize(serialized).get()).isEqualTo(1234);
- }
-
- @Test
- public void deserializeNullFails() throws Exception {
- try {
- SERIALIZER.deserialize(null);
- assert_().fail("expected RuntimeException");
- } catch (RuntimeException e) {
- // Expected.
- }
- }
-}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java
index 1d540100ae..40ff0acd11 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java
@@ -14,16 +14,15 @@
package com.google.gerrit.server.cache.serialize;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Bytes;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.protobuf.TextFormat;
import org.junit.Test;
-public class IntegerCacheSerializerTest extends GerritBaseTests {
+public class IntegerCacheSerializerTest {
@Test
public void serialize() throws Exception {
for (int i :
@@ -49,17 +48,12 @@ public class IntegerCacheSerializerTest extends GerritBaseTests {
private static void assertRoundTrip(int i) throws Exception {
byte[] serialized = IntegerCacheSerializer.INSTANCE.serialize(i);
int result = IntegerCacheSerializer.INSTANCE.deserialize(serialized);
- assertThat(result)
- .named("round-trip of %s via \"%s\"", i, TextFormat.escapeBytes(serialized))
+ assertWithMessage("round-trip of %s via \"%s\"", i, TextFormat.escapeBytes(serialized))
+ .that(result)
.isEqualTo(i);
}
private static void assertDeserializeFails(byte[] in) {
- try {
- IntegerCacheSerializer.INSTANCE.deserialize(in);
- assert_().fail("expected RuntimeException");
- } catch (RuntimeException e) {
- // Expected.
- }
+ assertThrows(RuntimeException.class, () -> IntegerCacheSerializer.INSTANCE.deserialize(in));
}
}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java
index 9fcb8a4688..effc801c54 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java
@@ -17,11 +17,11 @@ package com.google.gerrit.server.cache.serialize;
import static com.google.common.truth.Truth.assertThat;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Project;
import java.io.Serializable;
import org.junit.Test;
-public class JavaCacheSerializerTest extends GerritBaseTests {
+public class JavaCacheSerializerTest {
@Test
public void builtInTypes() throws Exception {
assertRoundTrip("foo");
@@ -34,6 +34,11 @@ public class JavaCacheSerializerTest extends GerritBaseTests {
assertRoundTrip(new AutoValue_JavaCacheSerializerTest_MyType(123, "four five six"));
}
+ @Test
+ public void gerritEntities() throws Exception {
+ assertRoundTrip(Project.nameKey("foo"));
+ }
+
@AutoValue
abstract static class MyType implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java
index 257be54bdf..7d6647a09c 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java
@@ -15,14 +15,13 @@
package com.google.gerrit.server.cache.serialize;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteArray;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class ObjectIdCacheSerializerTest extends GerritBaseTests {
+public class ObjectIdCacheSerializerTest {
@Test
public void serialize() {
ObjectId id = ObjectId.fromString("aabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
@@ -47,11 +46,7 @@ public class ObjectIdCacheSerializerTest extends GerritBaseTests {
}
private void assertDeserializeFails(byte[] bytes) {
- try {
- ObjectIdCacheSerializer.INSTANCE.deserialize(bytes);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class, () -> ObjectIdCacheSerializer.INSTANCE.deserialize(bytes));
}
}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java b/javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java
index c5ea2ea2c3..f6d6c8ab3c 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java
@@ -15,15 +15,14 @@
package com.google.gerrit.server.cache.serialize;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.protobuf.ByteString;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class ObjectIdConverterTest extends GerritBaseTests {
+public class ObjectIdConverterTest {
@Test
public void objectIdFromByteString() {
ObjectIdConverter idConverter = ObjectIdConverter.create();
@@ -43,12 +42,9 @@ public class ObjectIdConverterTest extends GerritBaseTests {
@Test
public void objectIdFromByteStringWrongSize() {
- try {
- ObjectIdConverter.create().fromByteString(ByteString.copyFromUtf8("foo"));
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> ObjectIdConverter.create().fromByteString(ByteString.copyFromUtf8("foo")));
}
@Test
diff --git a/javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.java
index 845da9b700..04d2f73800 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.java
@@ -17,10 +17,9 @@ package com.google.gerrit.server.cache.serialize;
import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.proto.testing.Test.SerializableProto;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class ProtobufSerializerTest extends GerritBaseTests {
+public class ProtobufSerializerTest {
@Test
public void requiredAndOptionalTypes() {
assertRoundTrip(SerializableProto.newBuilder().setId(123));
diff --git a/javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java
index ff0cf9af04..dc228050d6 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java
@@ -15,14 +15,13 @@
package com.google.gerrit.server.cache.serialize;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
-public class StringCacheSerializerTest extends GerritBaseTests {
+public class StringCacheSerializerTest {
@Test
public void serialize() {
assertThat(StringCacheSerializer.INSTANCE.serialize("")).isEmpty();
@@ -35,12 +34,11 @@ public class StringCacheSerializerTest extends GerritBaseTests {
@Test
public void serializeInvalidChar() {
// Can't use UTF-8 for the test, since it can encode all Unicode code points.
- try {
- StringCacheSerializer.serialize(StandardCharsets.US_ASCII, "\u1234");
- assert_().fail("expected IllegalStateException");
- } catch (IllegalStateException expected) {
- assertThat(expected).hasCauseThat().isInstanceOf(CharacterCodingException.class);
- }
+ IllegalStateException thrown =
+ assertThrows(
+ IllegalStateException.class,
+ () -> StringCacheSerializer.serialize(StandardCharsets.US_ASCII, "\u1234"));
+ assertThat(thrown).hasCauseThat().isInstanceOf(CharacterCodingException.class);
}
@Test
@@ -56,11 +54,10 @@ public class StringCacheSerializerTest extends GerritBaseTests {
@Test
public void deserializeInvalidChar() {
- try {
- StringCacheSerializer.INSTANCE.deserialize(new byte[] {(byte) 0xff});
- assert_().fail("expected IllegalStateException");
- } catch (IllegalStateException expected) {
- assertThat(expected).hasCauseThat().isInstanceOf(CharacterCodingException.class);
- }
+ IllegalStateException thrown =
+ assertThrows(
+ IllegalStateException.class,
+ () -> StringCacheSerializer.INSTANCE.deserialize(new byte[] {(byte) 0xff}));
+ assertThat(thrown).hasCauseThat().isInstanceOf(CharacterCodingException.class);
}
}
diff --git a/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java b/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
index fffb1da213..20813f6834 100644
--- a/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
+++ b/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
@@ -24,11 +24,10 @@ import com.google.gerrit.proto.testing.SerializedClassSubject;
import com.google.gerrit.server.cache.proto.Cache.ChangeKindKeyProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
import com.google.gerrit.server.change.ChangeKindCacheImpl.Key;
-import com.google.gerrit.testing.GerritBaseTests;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class ChangeKindCacheImplTest extends GerritBaseTests {
+public class ChangeKindCacheImplTest {
@Test
public void keySerializer() throws Exception {
ChangeKindCacheImpl.Key key =
diff --git a/javatests/com/google/gerrit/server/change/HashtagsTest.java b/javatests/com/google/gerrit/server/change/HashtagsTest.java
index 49d295263c..780ac71929 100644
--- a/javatests/com/google/gerrit/server/change/HashtagsTest.java
+++ b/javatests/com/google/gerrit/server/change/HashtagsTest.java
@@ -17,10 +17,9 @@ package com.google.gerrit.server.change;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.Sets;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class HashtagsTest extends GerritBaseTests {
+public class HashtagsTest {
@Test
public void emptyCommitMessage() throws Exception {
assertThat(HashtagsUtil.extractTags("")).isEmpty();
diff --git a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
index 0cfe483d0e..19c479d3b8 100644
--- a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
+++ b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
@@ -15,9 +15,8 @@
package com.google.gerrit.server.change;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_TAGS;
+import static com.google.gerrit.entities.RefNames.REFS_TAGS;
-import com.google.gerrit.testing.GerritBaseTests;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
@@ -27,7 +26,7 @@ import org.eclipse.jgit.revwalk.RevTag;
import org.junit.Before;
import org.junit.Test;
-public class IncludedInResolverTest extends GerritBaseTests {
+public class IncludedInResolverTest {
// Branch names
private static final String BRANCH_MASTER = "master";
private static final String BRANCH_1_0 = "rel-1.0";
diff --git a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
index 6e02d61713..c259e6012b 100644
--- a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
+++ b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
@@ -14,24 +14,25 @@
package com.google.gerrit.server.change;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.common.data.Permission.forLabel;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.project.testing.Util.allow;
-import static com.google.gerrit.server.project.testing.Util.category;
-import static com.google.gerrit.server.project.testing.Util.value;
+import static com.google.gerrit.server.project.testing.TestLabels.label;
+import static com.google.gerrit.server.project.testing.TestLabels.value;
import static org.junit.Assert.assertEquals;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
@@ -45,7 +46,6 @@ import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -57,7 +57,7 @@ import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link LabelNormalizer}. */
-public class LabelNormalizerTest extends GerritBaseTests {
+public class LabelNormalizerTest {
@Inject private AccountManager accountManager;
@Inject private AllProjectsName allProjects;
@Inject private GitRepositoryManager repoManager;
@@ -70,6 +70,7 @@ public class LabelNormalizerTest extends GerritBaseTests {
@Inject private ChangeNotes.Factory changeNotesFactory;
@Inject private ProjectConfig.Factory projectConfigFactory;
@Inject private GerritApi gApi;
+ @Inject private ProjectOperations projectOperations;
private LifecycleManager lifecycle;
private Account.Id userId;
@@ -103,7 +104,7 @@ public class LabelNormalizerTest extends GerritBaseTests {
}
}
LabelType lt =
- category("Verified", value(1, "Verified"), value(0, "No score"), value(-1, "Fails"));
+ label("Verified", value(1, "Verified"), value(0, "No score"), value(-1, "Fails"));
pc.getLabelSections().put(lt.getName(), lt);
save(pc);
}
@@ -115,7 +116,7 @@ public class LabelNormalizerTest extends GerritBaseTests {
input.newBranch = true;
input.subject = "Test change";
ChangeInfo info = gApi.changes().create(input).get();
- notes = changeNotesFactory.createChecked(allProjects, new Change.Id(info._number));
+ notes = changeNotesFactory.createChecked(allProjects, Change.id(info._number));
change = notes.getChange();
}
@@ -129,10 +130,11 @@ public class LabelNormalizerTest extends GerritBaseTests {
@Test
public void noNormalizeByPermission() throws Exception {
- ProjectConfig pc = loadAllProjects();
- allow(pc, forLabel("Code-Review"), -1, 1, REGISTERED_USERS, "refs/heads/*");
- allow(pc, forLabel("Verified"), -1, 1, REGISTERED_USERS, "refs/heads/*");
- save(pc);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-1, 1))
+ .add(allowLabel("Verified").ref("refs/heads/*").group(REGISTERED_USERS).range(-1, 1))
+ .update();
PatchSetApproval cr = psa(userId, "Code-Review", 2);
PatchSetApproval v = psa(userId, "Verified", 1);
@@ -141,10 +143,11 @@ public class LabelNormalizerTest extends GerritBaseTests {
@Test
public void normalizeByType() throws Exception {
- ProjectConfig pc = loadAllProjects();
- allow(pc, forLabel("Code-Review"), -5, 5, REGISTERED_USERS, "refs/heads/*");
- allow(pc, forLabel("Verified"), -5, 5, REGISTERED_USERS, "refs/heads/*");
- save(pc);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-5, 5))
+ .add(allowLabel("Verified").ref("refs/heads/*").group(REGISTERED_USERS).range(-5, 5))
+ .update();
PatchSetApproval cr = psa(userId, "Code-Review", 5);
PatchSetApproval v = psa(userId, "Verified", 5);
@@ -162,9 +165,10 @@ public class LabelNormalizerTest extends GerritBaseTests {
@Test
public void explicitZeroVoteOnNonEmptyRangeIsPresent() throws Exception {
- ProjectConfig pc = loadAllProjects();
- allow(pc, forLabel("Code-Review"), -1, 1, REGISTERED_USERS, "refs/heads/*");
- save(pc);
+ projectOperations
+ .allProjectsForUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-1, 1))
+ .update();
PatchSetApproval cr = psa(userId, "Code-Review", 0);
PatchSetApproval v = psa(userId, "Verified", 0);
@@ -187,16 +191,15 @@ public class LabelNormalizerTest extends GerritBaseTests {
}
private PatchSetApproval psa(Account.Id accountId, String label, int value) {
- return new PatchSetApproval(
- new PatchSetApproval.Key(change.currentPatchSetId(), accountId, new LabelId(label)),
- (short) value,
- TimeUtil.nowTs());
+ return PatchSetApproval.builder()
+ .key(PatchSetApproval.key(change.currentPatchSetId(), accountId, LabelId.create(label)))
+ .value(value)
+ .granted(TimeUtil.nowTs())
+ .build();
}
private PatchSetApproval copy(PatchSetApproval src, int newValue) {
- PatchSetApproval result = new PatchSetApproval(src.getKey().getParentKey(), src);
- result.setValue((short) newValue);
- return result;
+ return src.toBuilder().value(newValue).build();
}
private static List<PatchSetApproval> list(PatchSetApproval... psas) {
diff --git a/javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java b/javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java
index 46ddbc27e0..19c8998516 100644
--- a/javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java
+++ b/javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java
@@ -23,11 +23,10 @@ import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.proto.testing.SerializedClassSubject;
import com.google.gerrit.server.cache.proto.Cache.MergeabilityKeyProto;
-import com.google.gerrit.testing.GerritBaseTests;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class MergeabilityCacheImplTest extends GerritBaseTests {
+public class MergeabilityCacheImplTest {
@Test
public void keySerializer() throws Exception {
MergeabilityCacheImpl.EntryKey key =
diff --git a/javatests/com/google/gerrit/server/change/WalkSorterTest.java b/javatests/com/google/gerrit/server/change/WalkSorterTest.java
index 189dfbcbe8..2c4c98fb5e 100644
--- a/javatests/com/google/gerrit/server/change/WalkSorterTest.java
+++ b/javatests/com/google/gerrit/server/change/WalkSorterTest.java
@@ -19,14 +19,12 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.change.WalkSorter.PatchSetData;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.InMemoryRepositoryManager.Repo;
import com.google.gerrit.testing.TestChanges;
@@ -39,13 +37,13 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
import org.junit.Test;
-public class WalkSorterTest extends GerritBaseTests {
+public class WalkSorterTest {
private Account.Id userId;
private InMemoryRepositoryManager repoManager;
@Before
public void setUp() {
- userId = new Account.Id(1);
+ userId = Account.id(1);
repoManager = new InMemoryRepositoryManager();
}
@@ -280,7 +278,7 @@ public class WalkSorterTest extends GerritBaseTests {
// If we restrict to PS1 of each change, the sorter uses that commit.
sorter.includePatchSets(
- ImmutableSet.of(new PatchSet.Id(cd1.getId(), 1), new PatchSet.Id(cd2.getId(), 1)));
+ ImmutableSet.of(PatchSet.id(cd1.getId(), 1), PatchSet.id(cd2.getId(), 1)));
assertSorted(
sorter, changes, ImmutableList.of(patchSetData(cd2, 1, c2_1), patchSetData(cd1, 1, c1_1)));
}
@@ -297,8 +295,7 @@ public class WalkSorterTest extends GerritBaseTests {
List<ChangeData> changes = ImmutableList.of(cd1, cd2);
WalkSorter sorter =
- new WalkSorter(repoManager)
- .includePatchSets(ImmutableSet.of(cd1.currentPatchSet().getId()));
+ new WalkSorter(repoManager).includePatchSets(ImmutableSet.of(cd1.currentPatchSet().id()));
assertSorted(sorter, changes, ImmutableList.of(patchSetData(cd1, c1)));
}
@@ -335,17 +332,15 @@ public class WalkSorterTest extends GerritBaseTests {
private ChangeData newChange(TestRepository<Repo> tr, ObjectId id) throws Exception {
Project.NameKey project = tr.getRepository().getDescription().getProject();
Change c = TestChanges.newChange(project, userId);
- ChangeData cd = ChangeData.createForTest(project, c.getId(), 1);
+ ChangeData cd = ChangeData.createForTest(project, c.getId(), 1, id);
cd.setChange(c);
- cd.currentPatchSet().setRevision(new RevId(id.name()));
cd.setPatchSets(ImmutableList.of(cd.currentPatchSet()));
return cd;
}
private PatchSet addPatchSet(ChangeData cd, ObjectId id) throws Exception {
TestChanges.incrementPatchSet(cd.change());
- PatchSet ps = new PatchSet(cd.change().currentPatchSetId());
- ps.setRevision(new RevId(id.name()));
+ PatchSet ps = TestChanges.newPatchSet(cd.change().currentPatchSetId(), id.name(), userId);
List<PatchSet> patchSets = new ArrayList<>(cd.patchSets());
patchSets.add(ps);
cd.setPatchSets(patchSets);
@@ -353,7 +348,7 @@ public class WalkSorterTest extends GerritBaseTests {
}
private TestRepository<Repo> newRepo(String name) throws Exception {
- return new TestRepository<>(repoManager.createRepository(new Project.NameKey(name)));
+ return new TestRepository<>(repoManager.createRepository(Project.nameKey(name)));
}
private static PatchSetData patchSetData(ChangeData cd, RevCommit commit) throws Exception {
@@ -362,7 +357,7 @@ public class WalkSorterTest extends GerritBaseTests {
private static PatchSetData patchSetData(ChangeData cd, int psId, RevCommit commit)
throws Exception {
- return PatchSetData.create(cd, cd.patchSet(new PatchSet.Id(cd.getId(), psId)), commit);
+ return PatchSetData.create(cd, cd.patchSet(PatchSet.id(cd.getId(), psId)), commit);
}
private static void assertSorted(
diff --git a/javatests/com/google/gerrit/server/config/AllProjectsNameTest.java b/javatests/com/google/gerrit/server/config/AllProjectsNameTest.java
new file mode 100644
index 0000000000..64a5f834cf
--- /dev/null
+++ b/javatests/com/google/gerrit/server/config/AllProjectsNameTest.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.entities.Project;
+import org.junit.Test;
+
+public class AllProjectsNameTest {
+ @Test
+ public void equalToProjectNameKey() {
+ String name = "a-project";
+ AllProjectsName allProjectsName = new AllProjectsName(name);
+ Project.NameKey projectName = Project.nameKey(name);
+ assertThat(allProjectsName.get()).isEqualTo(projectName.get());
+ assertThat(allProjectsName).isEqualTo(projectName);
+ }
+
+ @Test
+ public void equalToAllUsersName() {
+ String name = "a-project";
+ AllProjectsName allProjectsName = new AllProjectsName(name);
+ AllUsersName allUsersName = new AllUsersName(name);
+ assertThat(allProjectsName.get()).isEqualTo(allUsersName.get());
+ assertThat(allProjectsName).isEqualTo(allUsersName);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/config/AllUsersNameTest.java b/javatests/com/google/gerrit/server/config/AllUsersNameTest.java
new file mode 100644
index 0000000000..d46b7a640a
--- /dev/null
+++ b/javatests/com/google/gerrit/server/config/AllUsersNameTest.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.entities.Project;
+import org.junit.Test;
+
+public class AllUsersNameTest {
+ @Test
+ public void equalToProjectNameKey() {
+ String name = "a-project";
+ AllUsersName allUsersName = new AllUsersName(name);
+ Project.NameKey projectName = Project.nameKey(name);
+ assertThat(allUsersName.get()).isEqualTo(projectName.get());
+ assertThat(allUsersName).isEqualTo(projectName);
+ }
+
+ @Test
+ public void equalToAllProjectsName() {
+ String name = "a-project";
+ AllUsersName allUsersName = new AllUsersName(name);
+ AllProjectsName allProjectsName = new AllProjectsName(name);
+ assertThat(allUsersName.get()).isEqualTo(allProjectsName.get());
+ assertThat(allUsersName).isEqualTo(allProjectsName);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/config/ConfigUtilTest.java b/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
index b1378ad265..035878caf9 100644
--- a/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
+++ b/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
@@ -15,20 +15,20 @@
package com.google.gerrit.server.config;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.truth.ConfigSubject.assertThat;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
-public class ConfigUtilTest extends GerritBaseTests {
+public class ConfigUtilTest {
private static final String SECT = "foo";
private static final String SUB = "bar";
@@ -85,17 +85,17 @@ public class ConfigUtilTest extends GerritBaseTests {
Config cfg = new Config();
ConfigUtil.storeSection(cfg, SECT, SUB, in, d);
- assertThat(cfg.getString(SECT, SUB, "CONSTANT")).isNull();
- assertThat(cfg.getString(SECT, SUB, "missing")).isNull();
- assertThat(cfg.getBoolean(SECT, SUB, "b", false)).isEqualTo(in.b);
- assertThat(cfg.getBoolean(SECT, SUB, "bb", false)).isEqualTo(in.bb);
- assertThat(cfg.getInt(SECT, SUB, "i", 0)).isEqualTo(0);
- assertThat(cfg.getInt(SECT, SUB, "ii", 0)).isEqualTo(in.ii);
- assertThat(cfg.getLong(SECT, SUB, "l", 0L)).isEqualTo(0L);
- assertThat(cfg.getLong(SECT, SUB, "ll", 0L)).isEqualTo(in.ll);
- assertThat(cfg.getString(SECT, SUB, "s")).isEqualTo(in.s);
- assertThat(cfg.getString(SECT, SUB, "sd")).isNull();
- assertThat(cfg.getString(SECT, SUB, "nd")).isNull();
+ assertThat(cfg).stringValue(SECT, SUB, "CONSTANT").isNull();
+ assertThat(cfg).stringValue(SECT, SUB, "missing").isNull();
+ assertThat(cfg).booleanValue(SECT, SUB, "b", false).isEqualTo(in.b);
+ assertThat(cfg).booleanValue(SECT, SUB, "bb", false).isEqualTo(in.bb);
+ assertThat(cfg).intValue(SECT, SUB, "i", 0).isEqualTo(0);
+ assertThat(cfg).intValue(SECT, SUB, "ii", 0).isEqualTo(in.ii);
+ assertThat(cfg).longValue(SECT, SUB, "l", 0L).isEqualTo(0L);
+ assertThat(cfg).longValue(SECT, SUB, "ll", 0L).isEqualTo(in.ll);
+ assertThat(cfg).stringValue(SECT, SUB, "s").isEqualTo(in.s);
+ assertThat(cfg).stringValue(SECT, SUB, "sd").isNull();
+ assertThat(cfg).stringValue(SECT, SUB, "nd").isNull();
SectionInfo out = new SectionInfo();
ConfigUtil.loadSection(cfg, SECT, SUB, out, d, null);
diff --git a/javatests/com/google/gerrit/server/config/GitwebConfigTest.java b/javatests/com/google/gerrit/server/config/GitwebConfigTest.java
index bf7e4fd237..cb6de34b76 100644
--- a/javatests/com/google/gerrit/server/config/GitwebConfigTest.java
+++ b/javatests/com/google/gerrit/server/config/GitwebConfigTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.server.config;
import static com.google.common.truth.Truth.assertWithMessage;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class GitwebConfigTest extends GerritBaseTests {
+public class GitwebConfigTest {
private static final String VALID_CHARACTERS = "*()";
private static final String SOME_INVALID_CHARACTERS = "09AZaz$-_.+!',";
diff --git a/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java b/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java
index 30fabdc521..708e2474e4 100644
--- a/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java
+++ b/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java
@@ -18,16 +18,15 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.config.CapabilityDefinition;
import com.google.gerrit.extensions.config.PluginProjectPermissionDefinition;
import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.restapi.config.ListCapabilities;
import com.google.gerrit.server.restapi.config.ListCapabilities.CapabilityInfo;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@@ -36,7 +35,7 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
-public class ListCapabilitiesTest extends GerritBaseTests {
+public class ListCapabilitiesTest {
private Injector injector;
@Before
@@ -74,7 +73,7 @@ public class ListCapabilitiesTest extends GerritBaseTests {
@Test
public void list() throws Exception {
Map<String, CapabilityInfo> m =
- injector.getInstance(ListCapabilities.class).apply(new ConfigResource());
+ injector.getInstance(ListCapabilities.class).apply(new ConfigResource()).value();
for (String id : GlobalCapability.getAllNames()) {
assertThat(m).containsKey(id);
assertThat(m.get(id).id).isEqualTo(id);
diff --git a/javatests/com/google/gerrit/server/config/RepositoryConfigTest.java b/javatests/com/google/gerrit/server/config/RepositoryConfigTest.java
index 2a473f42e5..d7aae6a0fe 100644
--- a/javatests/com/google/gerrit/server/config/RepositoryConfigTest.java
+++ b/javatests/com/google/gerrit/server/config/RepositoryConfigTest.java
@@ -18,9 +18,8 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.testing.GerritBaseTests;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@@ -28,7 +27,7 @@ import org.eclipse.jgit.lib.Config;
import org.junit.Before;
import org.junit.Test;
-public class RepositoryConfigTest extends GerritBaseTests {
+public class RepositoryConfigTest {
private Config cfg;
private RepositoryConfig repoCfg;
@@ -42,35 +41,35 @@ public class RepositoryConfigTest extends GerritBaseTests {
@Test
public void defaultSubmitTypeWhenNotConfigured() {
// Check expected value explicitly rather than depending on constant.
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("someProject")))
.isEqualTo(SubmitType.INHERIT);
}
@Test
public void defaultSubmitTypeForStarFilter() {
configureDefaultSubmitType("*", SubmitType.CHERRY_PICK);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("someProject")))
.isEqualTo(SubmitType.CHERRY_PICK);
configureDefaultSubmitType("*", SubmitType.FAST_FORWARD_ONLY);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("someProject")))
.isEqualTo(SubmitType.FAST_FORWARD_ONLY);
configureDefaultSubmitType("*", SubmitType.REBASE_IF_NECESSARY);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("someProject")))
.isEqualTo(SubmitType.REBASE_IF_NECESSARY);
configureDefaultSubmitType("*", SubmitType.REBASE_ALWAYS);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("someProject")))
.isEqualTo(SubmitType.REBASE_ALWAYS);
}
@Test
public void defaultSubmitTypeForSpecificFilter() {
configureDefaultSubmitType("someProject", SubmitType.CHERRY_PICK);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someOtherProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("someOtherProject")))
.isEqualTo(RepositoryConfig.DEFAULT_SUBMIT_TYPE);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("someProject")))
.isEqualTo(SubmitType.CHERRY_PICK);
}
@@ -80,13 +79,13 @@ public class RepositoryConfigTest extends GerritBaseTests {
configureDefaultSubmitType("somePath/*", SubmitType.CHERRY_PICK);
configureDefaultSubmitType("*", SubmitType.MERGE_ALWAYS);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("someProject")))
.isEqualTo(SubmitType.MERGE_ALWAYS);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("somePath/someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("somePath/someProject")))
.isEqualTo(SubmitType.CHERRY_PICK);
- assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("somePath/somePath/someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(Project.nameKey("somePath/somePath/someProject")))
.isEqualTo(SubmitType.REBASE_IF_NECESSARY);
}
@@ -100,14 +99,14 @@ public class RepositoryConfigTest extends GerritBaseTests {
@Test
public void ownerGroupsWhenNotConfigured() {
- assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someProject"))).isEmpty();
+ assertThat(repoCfg.getOwnerGroups(Project.nameKey("someProject"))).isEmpty();
}
@Test
public void ownerGroupsForStarFilter() {
ImmutableList<String> ownerGroups = ImmutableList.of("group1", "group2");
configureOwnerGroups("*", ownerGroups);
- assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getOwnerGroups(Project.nameKey("someProject")))
.containsExactlyElementsIn(ownerGroups);
}
@@ -115,8 +114,8 @@ public class RepositoryConfigTest extends GerritBaseTests {
public void ownerGroupsForSpecificFilter() {
ImmutableList<String> ownerGroups = ImmutableList.of("group1", "group2");
configureOwnerGroups("someProject", ownerGroups);
- assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someOtherProject"))).isEmpty();
- assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getOwnerGroups(Project.nameKey("someOtherProject"))).isEmpty();
+ assertThat(repoCfg.getOwnerGroups(Project.nameKey("someProject")))
.containsExactlyElementsIn(ownerGroups);
}
@@ -130,13 +129,13 @@ public class RepositoryConfigTest extends GerritBaseTests {
configureOwnerGroups("somePath/*", ownerGroups2);
configureOwnerGroups("somePath/somePath/*", ownerGroups3);
- assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someProject")))
+ assertThat(repoCfg.getOwnerGroups(Project.nameKey("someProject")))
.containsExactlyElementsIn(ownerGroups1);
- assertThat(repoCfg.getOwnerGroups(new Project.NameKey("somePath/someProject")))
+ assertThat(repoCfg.getOwnerGroups(Project.nameKey("somePath/someProject")))
.containsExactlyElementsIn(ownerGroups2);
- assertThat(repoCfg.getOwnerGroups(new Project.NameKey("somePath/somePath/someProject")))
+ assertThat(repoCfg.getOwnerGroups(Project.nameKey("somePath/somePath/someProject")))
.containsExactlyElementsIn(ownerGroups3);
}
@@ -150,24 +149,22 @@ public class RepositoryConfigTest extends GerritBaseTests {
@Test
public void basePathWhenNotConfigured() {
- assertThat(repoCfg.getBasePath(new Project.NameKey("someProject"))).isNull();
+ assertThat(repoCfg.getBasePath(Project.nameKey("someProject"))).isNull();
}
@Test
public void basePathForStarFilter() {
String basePath = "/someAbsolutePath/someDirectory";
configureBasePath("*", basePath);
- assertThat(repoCfg.getBasePath(new Project.NameKey("someProject")).toString())
- .isEqualTo(basePath);
+ assertThat(repoCfg.getBasePath(Project.nameKey("someProject")).toString()).isEqualTo(basePath);
}
@Test
public void basePathForSpecificFilter() {
String basePath = "/someAbsolutePath/someDirectory";
configureBasePath("someProject", basePath);
- assertThat(repoCfg.getBasePath(new Project.NameKey("someOtherProject"))).isNull();
- assertThat(repoCfg.getBasePath(new Project.NameKey("someProject")).toString())
- .isEqualTo(basePath);
+ assertThat(repoCfg.getBasePath(Project.nameKey("someOtherProject"))).isNull();
+ assertThat(repoCfg.getBasePath(Project.nameKey("someProject")).toString()).isEqualTo(basePath);
}
@Test
@@ -182,14 +179,12 @@ public class RepositoryConfigTest extends GerritBaseTests {
configureBasePath("project/*", basePath3);
configureBasePath("*", basePath4);
- assertThat(repoCfg.getBasePath(new Project.NameKey("project1")).toString())
- .isEqualTo(basePath1);
- assertThat(repoCfg.getBasePath(new Project.NameKey("project/project/someProject")).toString())
+ assertThat(repoCfg.getBasePath(Project.nameKey("project1")).toString()).isEqualTo(basePath1);
+ assertThat(repoCfg.getBasePath(Project.nameKey("project/project/someProject")).toString())
.isEqualTo(basePath2);
- assertThat(repoCfg.getBasePath(new Project.NameKey("project/someProject")).toString())
+ assertThat(repoCfg.getBasePath(Project.nameKey("project/someProject")).toString())
.isEqualTo(basePath3);
- assertThat(repoCfg.getBasePath(new Project.NameKey("someProject")).toString())
- .isEqualTo(basePath4);
+ assertThat(repoCfg.getBasePath(Project.nameKey("someProject")).toString()).isEqualTo(basePath4);
}
@Test
diff --git a/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java b/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java
index 69260523a7..55f0374b56 100644
--- a/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java
+++ b/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java
@@ -22,7 +22,6 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import com.google.gerrit.server.config.ScheduleConfig.Schedule;
-import com.google.gerrit.testing.GerritBaseTests;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneOffset;
@@ -32,7 +31,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
-public class ScheduleConfigTest extends GerritBaseTests {
+public class ScheduleConfigTest {
// Friday June 13, 2014 10:00 UTC
private static final ZonedDateTime NOW =
diff --git a/javatests/com/google/gerrit/server/config/SitePathsTest.java b/javatests/com/google/gerrit/server/config/SitePathsTest.java
index b4cde14e7e..1e5f41d228 100644
--- a/javatests/com/google/gerrit/server/config/SitePathsTest.java
+++ b/javatests/com/google/gerrit/server/config/SitePathsTest.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.config;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.server.ioutil.HostPlatform;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NotDirectoryException;
@@ -26,7 +26,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;
-public class SitePathsTest extends GerritBaseTests {
+public class SitePathsTest {
@Test
public void create_NotExisting() throws IOException {
final Path root = random();
@@ -72,8 +72,8 @@ public class SitePathsTest extends GerritBaseTests {
final Path root = random();
try {
Files.createFile(root);
- exception.expect(NotDirectoryException.class);
- new SitePaths(root);
+ assertThrows(NotDirectoryException.class, () -> new SitePaths(root));
+
} finally {
Files.delete(root);
}
diff --git a/javatests/com/google/gerrit/server/edit/ChangeEditTest.java b/javatests/com/google/gerrit/server/edit/ChangeEditTest.java
index 4c0b5a18c3..2bad8152cd 100644
--- a/javatests/com/google/gerrit/server/edit/ChangeEditTest.java
+++ b/javatests/com/google/gerrit/server/edit/ChangeEditTest.java
@@ -16,19 +16,18 @@ package com.google.gerrit.server.edit;
import static org.junit.Assert.assertEquals;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.RefNames;
import org.junit.Test;
-public class ChangeEditTest extends GerritBaseTests {
+public class ChangeEditTest {
@Test
public void changeEditRef() throws Exception {
- Account.Id accountId = new Account.Id(1000042);
- Change.Id changeId = new Change.Id(56414);
- PatchSet.Id psId = new PatchSet.Id(changeId, 50);
+ Account.Id accountId = Account.id(1000042);
+ Change.Id changeId = Change.id(56414);
+ PatchSet.Id psId = PatchSet.id(changeId, 50);
String refName = RefNames.refsEdit(accountId, changeId, psId);
assertEquals("refs/users/42/1000042/edit-56414/50", refName);
}
diff --git a/javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java b/javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java
index 265b24e46e..a618c9ee1a 100644
--- a/javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java
+++ b/javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java
@@ -25,31 +25,38 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
-public class ChangeFileContentModificationSubject
- extends Subject<ChangeFileContentModificationSubject, ChangeFileContentModification> {
+public class ChangeFileContentModificationSubject extends Subject {
public static ChangeFileContentModificationSubject assertThat(
ChangeFileContentModification modification) {
- return assertAbout(ChangeFileContentModificationSubject::new).that(modification);
+ return assertAbout(modifications()).that(modification);
}
+ public static Factory<ChangeFileContentModificationSubject, ChangeFileContentModification>
+ modifications() {
+ return ChangeFileContentModificationSubject::new;
+ }
+
+ private final ChangeFileContentModification modification;
+
private ChangeFileContentModificationSubject(
FailureMetadata failureMetadata, ChangeFileContentModification modification) {
super(failureMetadata, modification);
+ this.modification = modification;
}
public StringSubject filePath() {
isNotNull();
- return check("filePath()").that(actual().getFilePath());
+ return check("getFilePath()").that(modification.getFilePath());
}
public StringSubject newContent() throws IOException {
isNotNull();
- RawInput newContent = actual().getNewContent();
- check("newContent()").that(newContent).isNotNull();
+ RawInput newContent = modification.getNewContent();
+ check("getNewContent()").that(newContent).isNotNull();
String contentString =
CharStreams.toString(
new InputStreamReader(newContent.getInputStream(), StandardCharsets.UTF_8));
- return check("newContent()").that(contentString);
+ return check("getNewContent()").that(contentString);
}
}
diff --git a/javatests/com/google/gerrit/server/edit/tree/TreeModificationSubject.java b/javatests/com/google/gerrit/server/edit/tree/TreeModificationSubject.java
index bd9d4df862..d5b70bb7a3 100644
--- a/javatests/com/google/gerrit/server/edit/tree/TreeModificationSubject.java
+++ b/javatests/com/google/gerrit/server/edit/tree/TreeModificationSubject.java
@@ -21,7 +21,7 @@ import com.google.common.truth.Subject;
import com.google.gerrit.truth.ListSubject;
import java.util.List;
-public class TreeModificationSubject extends Subject<TreeModificationSubject, TreeModification> {
+public class TreeModificationSubject extends Subject {
public static TreeModificationSubject assertThat(TreeModification treeModification) {
return assertAbout(treeModifications()).that(treeModification);
@@ -33,19 +33,21 @@ public class TreeModificationSubject extends Subject<TreeModificationSubject, Tr
public static ListSubject<TreeModificationSubject, TreeModification> assertThatList(
List<TreeModification> treeModifications) {
- return assertAbout(ListSubject.elements())
- .thatCustom(treeModifications, treeModifications())
- .named("treeModifications");
+ return ListSubject.assertThat(treeModifications, treeModifications());
}
+ private final TreeModification treeModification;
+
private TreeModificationSubject(
FailureMetadata failureMetadata, TreeModification treeModification) {
super(failureMetadata, treeModification);
+ this.treeModification = treeModification;
}
public ChangeFileContentModificationSubject asChangeFileContentModification() {
isInstanceOf(ChangeFileContentModification.class);
- return ChangeFileContentModificationSubject.assertThat(
- (ChangeFileContentModification) actual());
+ return check("asChangeFileContentModification()")
+ .about(ChangeFileContentModificationSubject.modifications())
+ .that((ChangeFileContentModification) treeModification);
}
}
diff --git a/javatests/com/google/gerrit/server/events/BUILD b/javatests/com/google/gerrit/server/events/BUILD
new file mode 100644
index 0000000000..eed83c8ac8
--- /dev/null
+++ b/javatests/com/google/gerrit/server/events/BUILD
@@ -0,0 +1,15 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+
+junit_tests(
+ name = "events_tests",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/com/google/gerrit/entities",
+ "//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/util/time",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:gson",
+ "//lib:guava",
+ "//lib/truth",
+ ],
+)
diff --git a/javatests/com/google/gerrit/server/events/EventDeserializerTest.java b/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
index eac8d0dcab..e0223e4885 100644
--- a/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
+++ b/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
@@ -18,38 +18,32 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.data.AccountAttribute;
+import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
+import java.sql.Timestamp;
import org.junit.Test;
-public class EventDeserializerTest extends GerritBaseTests {
+public class EventDeserializerTest {
+ private final Gson gson = new EventGsonProvider().get();
@Test
public void refUpdatedEvent() {
- RefUpdatedEvent refUpdatedEvent = new RefUpdatedEvent();
-
+ RefUpdatedEvent orig = new RefUpdatedEvent();
RefUpdateAttribute refUpdatedAttribute = new RefUpdateAttribute();
refUpdatedAttribute.refName = "refs/heads/master";
- refUpdatedEvent.refUpdate = createSupplier(refUpdatedAttribute);
+ orig.refUpdate = createSupplier(refUpdatedAttribute);
AccountAttribute accountAttribute = new AccountAttribute();
accountAttribute.email = "some.user@domain.com";
- refUpdatedEvent.submitter = createSupplier(accountAttribute);
-
- Gson gsonSerializer =
- new GsonBuilder().registerTypeAdapter(Supplier.class, new SupplierSerializer()).create();
- String serializedEvent = gsonSerializer.toJson(refUpdatedEvent);
+ orig.submitter = createSupplier(accountAttribute);
- Gson gsonDeserializer =
- new GsonBuilder()
- .registerTypeAdapter(Event.class, new EventDeserializer())
- .registerTypeAdapter(Supplier.class, new SupplierDeserializer())
- .create();
-
- RefUpdatedEvent e = (RefUpdatedEvent) gsonDeserializer.fromJson(serializedEvent, Event.class);
+ RefUpdatedEvent e = roundTrip(orig);
assertThat(e).isNotNull();
assertThat(e.refUpdate).isInstanceOf(Supplier.class);
@@ -58,7 +52,271 @@ public class EventDeserializerTest extends GerritBaseTests {
assertThat(e.submitter.get().email).isEqualTo(accountAttribute.email);
}
+ @Test
+ public void patchSetCreatedEvent() {
+ Change change = newChange();
+ PatchSetCreatedEvent orig = new PatchSetCreatedEvent(change);
+ orig.change = asChangeAttribute(change);
+ orig.uploader = newAccount("uploader");
+
+ PatchSetCreatedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ assertSameAccount(e.uploader, orig.uploader);
+ }
+
+ @Test
+ public void assigneeChangedEvent() {
+ Change change = newChange();
+ AssigneeChangedEvent orig = new AssigneeChangedEvent(change);
+ orig.change = asChangeAttribute(change);
+ orig.changer = newAccount("changer");
+ orig.oldAssignee = newAccount("oldAssignee");
+
+ AssigneeChangedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ assertSameAccount(e.changer, orig.changer);
+ assertSameAccount(e.oldAssignee, orig.oldAssignee);
+ }
+
+ @Test
+ public void changeDeletedEvent() {
+ Change change = newChange();
+ ChangeDeletedEvent orig = new ChangeDeletedEvent(change);
+ orig.change = asChangeAttribute(change);
+ orig.deleter = newAccount("deleter");
+
+ ChangeDeletedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ assertSameAccount(e.deleter, orig.deleter);
+ }
+
+ @Test
+ public void hashtagsChangedEvent() {
+ Change change = newChange();
+ HashtagsChangedEvent orig = new HashtagsChangedEvent(change);
+ orig.change = asChangeAttribute(change);
+ orig.editor = newAccount("editor");
+ orig.added = new String[] {"added"};
+ orig.removed = new String[] {"removed"};
+ orig.hashtags = new String[] {"hashtags"};
+
+ HashtagsChangedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ assertSameAccount(e.editor, orig.editor);
+ assertThat(e.added).isEqualTo(orig.added);
+ assertThat(e.removed).isEqualTo(orig.removed);
+ assertThat(e.hashtags).isEqualTo(orig.hashtags);
+ }
+
+ @Test
+ public void changeAbandonedEvent() {
+ Change change = newChange();
+ ChangeAbandonedEvent orig = new ChangeAbandonedEvent(change);
+ orig.change = asChangeAttribute(change);
+ orig.abandoner = newAccount("abandoner");
+ orig.reason = "some reason";
+
+ ChangeAbandonedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ assertSameAccount(e.abandoner, orig.abandoner);
+ assertThat(e.reason).isEqualTo(orig.reason);
+ }
+
+ @Test
+ public void changeMergedEvent() {
+ Change change = newChange();
+ ChangeMergedEvent orig = new ChangeMergedEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ ChangeMergedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
+ @Test
+ public void changeRestoredEvent() {
+ Change change = newChange();
+ ChangeRestoredEvent orig = new ChangeRestoredEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ ChangeRestoredEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
+ @Test
+ public void commentAddedEvent() {
+ Change change = newChange();
+ CommentAddedEvent orig = new CommentAddedEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ CommentAddedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
+ @Test
+ public void privateStateChangedEvent() {
+ Change change = newChange();
+ PrivateStateChangedEvent orig = new PrivateStateChangedEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ PrivateStateChangedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
+ @Test
+ public void reviewerAddedEvent() {
+ Change change = newChange();
+ ReviewerAddedEvent orig = new ReviewerAddedEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ ReviewerAddedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
+ @Test
+ public void reviewerDeletedEvent() {
+ Change change = newChange();
+ ReviewerDeletedEvent orig = new ReviewerDeletedEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ ReviewerDeletedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
+ @Test
+ public void voteDeletedEvent() {
+ Change change = newChange();
+ VoteDeletedEvent orig = new VoteDeletedEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ VoteDeletedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
+ @Test
+ public void workinProgressStateChangedEvent() {
+ Change change = newChange();
+ WorkInProgressStateChangedEvent orig = new WorkInProgressStateChangedEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ WorkInProgressStateChangedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
+ @Test
+ public void topicChangedEvent() {
+ Change change = newChange();
+ TopicChangedEvent orig = new TopicChangedEvent(change);
+ orig.change = asChangeAttribute(change);
+
+ TopicChangedEvent e = roundTrip(orig);
+
+ assertThat(e).isNotNull();
+ assertSameChangeEvent(e, orig);
+ }
+
private <T> Supplier<T> createSupplier(T value) {
return Suppliers.memoize(() -> value);
}
+
+ private Change newChange() {
+ Change change =
+ new Change(
+ Change.key("Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+ Change.id(1000),
+ Account.id(1000),
+ BranchNameKey.create(Project.nameKey("myproject"), "mybranch"),
+ new Timestamp(System.currentTimeMillis()));
+ return change;
+ }
+
+ private Supplier<AccountAttribute> newAccount(String name) {
+ AccountAttribute account = new AccountAttribute();
+ account.name = name;
+ account.email = name + "@somewhere.com";
+ account.username = name;
+ return Suppliers.ofInstance(account);
+ }
+
+ private void assertSameChangeEvent(ChangeEvent current, ChangeEvent expected) {
+ assertThat(current.changeKey.get()).isEqualTo(expected.changeKey.get());
+ assertThat(current.refName).isEqualTo(expected.refName);
+ assertThat(current.project).isEqualTo(expected.project);
+ assertSameChange(current.change, expected.change);
+ }
+
+ private void assertSameChange(
+ Supplier<ChangeAttribute> currentSupplier, Supplier<ChangeAttribute> expectedSupplier) {
+ ChangeAttribute current = currentSupplier.get();
+ ChangeAttribute expected = expectedSupplier.get();
+ assertThat(current.project).isEqualTo(expected.project);
+ assertThat(current.branch).isEqualTo(expected.branch);
+ assertThat(current.topic).isEqualTo(expected.topic);
+ assertThat(current.id).isEqualTo(expected.id);
+ assertThat(current.number).isEqualTo(expected.number);
+ assertThat(current.subject).isEqualTo(expected.subject);
+ assertThat(current.commitMessage).isEqualTo(expected.commitMessage);
+ assertThat(current.url).isEqualTo(expected.url);
+ assertThat(current.status).isEqualTo(expected.status);
+ assertThat(current.createdOn).isEqualTo(expected.createdOn);
+ assertThat(current.wip).isEqualTo(expected.wip);
+ assertThat(current.isPrivate).isEqualTo(expected.isPrivate);
+ }
+
+ private void assertSameAccount(
+ Supplier<AccountAttribute> currentSupplier, Supplier<AccountAttribute> expectedSupplier) {
+ AccountAttribute current = currentSupplier.get();
+ AccountAttribute expected = expectedSupplier.get();
+ assertThat(current.name).isEqualTo(expected.name);
+ assertThat(current.email).isEqualTo(expected.email);
+ assertThat(current.username).isEqualTo(expected.username);
+ }
+
+ public Supplier<ChangeAttribute> asChangeAttribute(Change change) {
+ ChangeAttribute a = new ChangeAttribute();
+ a.project = change.getProject().get();
+ a.branch = change.getDest().shortName();
+ a.topic = change.getTopic();
+ a.id = change.getKey().get();
+ a.number = change.getId().get();
+ a.subject = change.getSubject();
+ a.commitMessage = "This is a test commit message";
+ a.url = "http://somewhere.com";
+ a.status = change.getStatus();
+ a.createdOn = change.getCreatedOn().getTime() / 1000L;
+ a.wip = change.isWorkInProgress() ? true : null;
+ a.isPrivate = change.isPrivate() ? true : null;
+ return Suppliers.ofInstance(a);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <E extends Event> E roundTrip(E event) {
+ String json = gson.toJson(event);
+ return (E) gson.fromJson(json, event.getClass());
+ }
}
diff --git a/javatests/com/google/gerrit/server/events/EventJsonTest.java b/javatests/com/google/gerrit/server/events/EventJsonTest.java
index b59641d70e..6163ea7f3c 100644
--- a/javatests/com/google/gerrit/server/events/EventJsonTest.java
+++ b/javatests/com/google/gerrit/server/events/EventJsonTest.java
@@ -15,32 +15,30 @@
package com.google.gerrit.server.events;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.Change.Status.NEW;
+import static com.google.gerrit.entities.Change.Status.NEW;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.truth.MapSubject;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.TestTimeUtil;
import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
-public class EventJsonTest extends GerritBaseTests {
+public class EventJsonTest {
private static final String BRANCH = "mybranch";
private static final String CHANGE_ID = "Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
private static final int CHANGE_NUM = 1000;
@@ -52,12 +50,7 @@ public class EventJsonTest extends GerritBaseTests {
private static final double TS2 = 1.254344401E9;
private static final String URL = "http://somewhere.com";
- // Must match StreamEvents#gson. (In master, the definition is refactored to be hared.)
- private final Gson gson =
- new GsonBuilder()
- .registerTypeAdapter(Supplier.class, new SupplierSerializer())
- .registerTypeAdapter(Project.NameKey.class, new ProjectNameKeySerializer())
- .create();
+ private final Gson gson = new EventGsonProvider().get();
@Before
public void setTimeForTesting() {
@@ -580,7 +573,7 @@ public class EventJsonTest extends GerritBaseTests {
Change.key(CHANGE_ID),
Change.id(CHANGE_NUM),
Account.id(9999),
- Branch.nameKey(Project.nameKey(PROJECT), BRANCH),
+ BranchNameKey.create(Project.nameKey(PROJECT), BRANCH),
TimeUtil.nowTs());
}
@@ -591,7 +584,7 @@ public class EventJsonTest extends GerritBaseTests {
private Supplier<ChangeAttribute> asChangeAttribute(Change change) {
ChangeAttribute a = new ChangeAttribute();
a.project = change.getProject().get();
- a.branch = change.getDest().getShortName();
+ a.branch = change.getDest().shortName();
a.topic = change.getTopic();
a.id = change.getKey().get();
a.number = change.getId().get();
@@ -609,8 +602,9 @@ public class EventJsonTest extends GerritBaseTests {
// Parse JSON into a raw Java map:
// * Doesn't depend on field iteration order.
// * Avoids excessively long string literals in asserts.
+ String json = gson.toJson(src);
Map<Object, Object> map =
- gson.fromJson(gson.toJson(src), new TypeToken<Map<Object, Object>>() {}.getType());
+ gson.fromJson(json, new TypeToken<Map<Object, Object>>() {}.getType());
return assertThat(map);
}
diff --git a/javatests/com/google/gerrit/server/events/EventTypesTest.java b/javatests/com/google/gerrit/server/events/EventTypesTest.java
index dd5c7f91f1..c822d6c68c 100644
--- a/javatests/com/google/gerrit/server/events/EventTypesTest.java
+++ b/javatests/com/google/gerrit/server/events/EventTypesTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.server.events;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class EventTypesTest extends GerritBaseTests {
+public class EventTypesTest {
public static class TestEvent extends Event {
private static final String TYPE = "test-event";
diff --git a/javatests/com/google/gerrit/server/extensions/webui/UiActionsTest.java b/javatests/com/google/gerrit/server/extensions/webui/UiActionsTest.java
index 4a1f47cd85..2485613641 100644
--- a/javatests/com/google/gerrit/server/extensions/webui/UiActionsTest.java
+++ b/javatests/com/google/gerrit/server/extensions/webui/UiActionsTest.java
@@ -15,14 +15,17 @@
package com.google.gerrit.server.extensions.webui;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.verify;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -32,16 +35,14 @@ import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackendCondition;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
-import org.easymock.EasyMock;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.junit.Test;
-public class UiActionsTest extends GerritBaseTests {
+public class UiActionsTest {
private static class FakeForProject extends ForProject {
private boolean allowValueQueries = true;
@@ -112,7 +113,7 @@ public class UiActionsTest extends GerritBaseTests {
@Override
public Account.Id getAccountId() {
- return new Account.Id(1);
+ return Account.id(1);
}
};
}
@@ -132,9 +133,7 @@ public class UiActionsTest extends GerritBaseTests {
// Set up the Mock to expect a call of bulkEvaluateTest to only contain cond{1,2} since cond3
// needs to be identified as duplicate and not called out explicitly.
- PermissionBackend permissionBackendMock = EasyMock.createMock(PermissionBackend.class);
- permissionBackendMock.bulkEvaluateTest(ImmutableSet.of(cond1, cond2));
- EasyMock.replay(permissionBackendMock);
+ PermissionBackend permissionBackendMock = mock(PermissionBackend.class);
UiActions.evaluatePermissionBackendConditions(
permissionBackendMock, ImmutableList.of(cond1, cond2, cond3));
@@ -143,6 +142,8 @@ public class UiActionsTest extends GerritBaseTests {
// the value of cond1 and issues no additional call to PermissionBackend.
forProject.disallowValueQueries();
+ verify(permissionBackendMock, only()).bulkEvaluateTest(ImmutableSet.of(cond1, cond2));
+
// Assert the values of all conditions
assertThat(cond1.value()).isFalse();
assertThat(cond2.value()).isTrue();
diff --git a/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java b/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
index cc648bfa7a..8b5705b1fd 100644
--- a/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
+++ b/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
@@ -15,32 +15,31 @@
package com.google.gerrit.server.fixes;
import static com.google.gerrit.server.edit.tree.TreeModificationSubject.assertThatList;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.replay;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Comment.Range;
+import com.google.gerrit.entities.FixReplacement;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Comment.Range;
-import com.google.gerrit.reviewdb.client.FixReplacement;
import com.google.gerrit.server.change.FileContentUtil;
import com.google.gerrit.server.edit.tree.TreeModification;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
-import org.easymock.EasyMock;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
-public class FixReplacementInterpreterTest extends GerritBaseTests {
- private final FileContentUtil fileContentUtil = createMock(FileContentUtil.class);
- private final Repository repository = createMock(Repository.class);
- private final ProjectState projectState = createMock(ProjectState.class);
- private final ObjectId patchSetCommitId = createMock(ObjectId.class);
+public class FixReplacementInterpreterTest {
+ private final FileContentUtil fileContentUtil = mock(FileContentUtil.class);
+ private final Repository repository = mock(Repository.class);
+ private final ProjectState projectState = mock(ProjectState.class);
+ private final ObjectId patchSetCommitId = mock(ObjectId.class);
private final String filePath1 = "an/arbitrary/file.txt";
private final String filePath2 = "another/arbitrary/file.txt";
@@ -68,7 +67,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath2, new Range(2, 0, 3, 0), "Another modified content");
mockFileContent(filePath2, "1st line\n2nd line\n3rd line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications =
toTreeModifications(fixReplacement, fixReplacement3, fixReplacement2);
List<TreeModification> sortedTreeModifications = getSortedCopy(treeModifications);
@@ -99,7 +97,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(2, 0, 3, 0), "");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
assertThatList(treeModifications)
.onlyElement()
@@ -114,7 +111,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(2, 0, 2, 0), "A new line\n");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
assertThatList(treeModifications)
.onlyElement()
@@ -128,7 +124,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(1, 6, 3, 1), "and t");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
assertThatList(treeModifications)
.onlyElement()
@@ -144,7 +139,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(2, 7, 2, 11), "modification");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications =
toTreeModifications(fixReplacement1, fixReplacement2);
assertThatList(treeModifications)
@@ -154,6 +148,22 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
.isEqualTo("First line\nA modification\nThird line\n");
}
+ @Test()
+ public void startAfterEndOfLineMarkThrowsAnException() throws Exception {
+ FixReplacement fixReplacement =
+ new FixReplacement(filePath1, new Range(1, 11, 2, 6), "A modification");
+ mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
+ assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
+ }
+
+ @Test()
+ public void endAfterEndOfLineMarkThrowsAnException() throws Exception {
+ FixReplacement fixReplacement =
+ new FixReplacement(filePath1, new Range(2, 0, 2, 12), "A modification");
+ mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
+ assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
+ }
+
@Test
public void replacementsMayTouch() throws Exception {
FixReplacement fixReplacement1 =
@@ -162,7 +172,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(2, 7, 3, 5), "content");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications =
toTreeModifications(fixReplacement1, fixReplacement2);
assertThatList(treeModifications)
@@ -178,7 +187,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(4, 0, 4, 0), "New content");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
assertThatList(treeModifications)
.onlyElement()
@@ -188,6 +196,34 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
}
@Test
+ public void replacementsCanChangeLastLine() throws Exception {
+ FixReplacement fixReplacement =
+ new FixReplacement(filePath1, new Range(3, 0, 4, 0), "New content\n");
+ mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
+
+ List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
+ assertThatList(treeModifications)
+ .onlyElement()
+ .asChangeFileContentModification()
+ .newContent()
+ .isEqualTo("First line\nSecond line\nNew content\n");
+ }
+
+ @Test
+ public void replacementsCanChangeLastLineWithoutEOLMark() throws Exception {
+ FixReplacement fixReplacement =
+ new FixReplacement(filePath1, new Range(3, 0, 3, 10), "New content\n");
+ mockFileContent(filePath1, "First line\nSecond line\nThird line");
+
+ List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
+ assertThatList(treeModifications)
+ .onlyElement()
+ .asChangeFileContentModification()
+ .newContent()
+ .isEqualTo("First line\nSecond line\nNew content\n");
+ }
+
+ @Test
public void replacementsCanModifySeveralFilesInAnyOrder() throws Exception {
FixReplacement fixReplacement1 =
new FixReplacement(filePath1, new Range(1, 1, 3, 2), "Modified content");
@@ -198,7 +234,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath2, new Range(3, 0, 4, 0), "Second modification\n");
mockFileContent(filePath2, "1st line\n2nd line\n3rd line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications =
toTreeModifications(fixReplacement3, fixReplacement1, fixReplacement2);
List<TreeModification> sortedTreeModifications = getSortedCopy(treeModifications);
@@ -219,7 +254,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(2, 11, 3, 0), "\r");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications = toTreeModifications(fixReplacement);
assertThatList(treeModifications)
.onlyElement()
@@ -238,7 +272,6 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(4, 0, 5, 0), "3rd modification\n");
mockFileContent(filePath1, "First line\nSecond line\nThird line\nFourth line\nFifth line\n");
- replay(fileContentUtil);
List<TreeModification> treeModifications =
toTreeModifications(fixReplacement2, fixReplacement1, fixReplacement3);
assertThatList(treeModifications)
@@ -255,10 +288,7 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(5, 0, 5, 0), "A new line\n");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
-
- exception.expect(ResourceConflictException.class);
- toTreeModifications(fixReplacement);
+ assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
}
@Test
@@ -267,10 +297,7 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(0, 0, 0, 0), "A new line\n");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
-
- exception.expect(ResourceConflictException.class);
- toTreeModifications(fixReplacement);
+ assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
}
@Test
@@ -279,10 +306,7 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(1, 0, 1, 11), "modified");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
-
- exception.expect(ResourceConflictException.class);
- toTreeModifications(fixReplacement);
+ assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
}
@Test
@@ -291,10 +315,7 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(3, 0, 3, 11), "modified");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
-
- exception.expect(ResourceConflictException.class);
- toTreeModifications(fixReplacement);
+ assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
}
@Test
@@ -303,16 +324,12 @@ public class FixReplacementInterpreterTest extends GerritBaseTests {
new FixReplacement(filePath1, new Range(1, -1, 1, 5), "modified");
mockFileContent(filePath1, "First line\nSecond line\nThird line\n");
- replay(fileContentUtil);
-
- exception.expect(ResourceConflictException.class);
- toTreeModifications(fixReplacement);
+ assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement));
}
private void mockFileContent(String filePath, String fileContent) throws Exception {
- EasyMock.expect(
- fileContentUtil.getContent(repository, projectState, patchSetCommitId, filePath))
- .andReturn(BinaryResult.create(fileContent));
+ when(fileContentUtil.getContent(repository, projectState, patchSetCommitId, filePath))
+ .thenReturn(BinaryResult.create(fileContent));
}
private List<TreeModification> toTreeModifications(FixReplacement... fixReplacements)
diff --git a/javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java b/javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java
index 309f726ed2..ba80c02189 100644
--- a/javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java
+++ b/javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java
@@ -15,25 +15,27 @@
package com.google.gerrit.server.fixes;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class LineIdentifierTest extends GerritBaseTests {
+public class LineIdentifierTest {
@Test
public void lineNumberMustBePositive() {
LineIdentifier lineIdentifier = new LineIdentifier("First line\nSecond line");
- exception.expect(StringIndexOutOfBoundsException.class);
- exception.expectMessage("positive");
- lineIdentifier.getStartIndexOfLine(0);
+ StringIndexOutOfBoundsException thrown =
+ assertThrows(
+ StringIndexOutOfBoundsException.class, () -> lineIdentifier.getStartIndexOfLine(0));
+ assertThat(thrown).hasMessageThat().contains("positive");
}
@Test
public void lineNumberMustIndicateAnAvailableLine() {
LineIdentifier lineIdentifier = new LineIdentifier("First line\nSecond line");
- exception.expect(StringIndexOutOfBoundsException.class);
- exception.expectMessage("Line 3 isn't available");
- lineIdentifier.getStartIndexOfLine(3);
+ StringIndexOutOfBoundsException thrown =
+ assertThrows(
+ StringIndexOutOfBoundsException.class, () -> lineIdentifier.getStartIndexOfLine(3));
+ assertThat(thrown).hasMessageThat().contains("Line 3 isn't available");
}
@Test
diff --git a/javatests/com/google/gerrit/server/fixes/StringModifierTest.java b/javatests/com/google/gerrit/server/fixes/StringModifierTest.java
index 185b58c21c..34472489af 100644
--- a/javatests/com/google/gerrit/server/fixes/StringModifierTest.java
+++ b/javatests/com/google/gerrit/server/fixes/StringModifierTest.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.fixes;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Before;
import org.junit.Test;
-public class StringModifierTest extends GerritBaseTests {
+public class StringModifierTest {
private final String originalString = "This is the original, unmodified string.";
private StringModifier stringModifier;
@@ -63,20 +63,20 @@ public class StringModifierTest extends GerritBaseTests {
@Test
public void replacedPartsMustNotOverlap() {
stringModifier.replace(0, 9, "");
- exception.expect(StringIndexOutOfBoundsException.class);
- stringModifier.replace(8, 32, "The modified");
+ assertThrows(
+ StringIndexOutOfBoundsException.class, () -> stringModifier.replace(8, 32, "The modified"));
}
@Test
public void startIndexMustNotBeGreaterThanEndIndex() {
- exception.expect(StringIndexOutOfBoundsException.class);
- stringModifier.replace(10, 9, "something");
+ assertThrows(
+ StringIndexOutOfBoundsException.class, () -> stringModifier.replace(10, 9, "something"));
}
@Test
public void startIndexMustNotBeNegative() {
- exception.expect(StringIndexOutOfBoundsException.class);
- stringModifier.replace(-1, 9, "something");
+ assertThrows(
+ StringIndexOutOfBoundsException.class, () -> stringModifier.replace(-1, 9, "something"));
}
@Test
@@ -90,13 +90,17 @@ public class StringModifierTest extends GerritBaseTests {
@Test
public void startIndexMustNotBeGreaterThanLengthOfString() {
- exception.expect(StringIndexOutOfBoundsException.class);
- stringModifier.replace(originalString.length() + 1, originalString.length() + 1, "something");
+ assertThrows(
+ StringIndexOutOfBoundsException.class,
+ () ->
+ stringModifier.replace(
+ originalString.length() + 1, originalString.length() + 1, "something"));
}
@Test
public void endIndexMustNotBeGreaterThanLengthOfString() {
- exception.expect(StringIndexOutOfBoundsException.class);
- stringModifier.replace(8, originalString.length() + 1, "something");
+ assertThrows(
+ StringIndexOutOfBoundsException.class,
+ () -> stringModifier.replace(8, originalString.length() + 1, "something"));
}
}
diff --git a/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java b/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
index b278bfea45..3a8d7e46f2 100644
--- a/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
+++ b/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
@@ -18,11 +18,11 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.notedb.DeleteZombieCommentsRefs;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -50,7 +50,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class DeleteZombieCommentsRefsTest {
private InMemoryRepositoryManager repoManager = new InMemoryRepositoryManager();
- private Project.NameKey allUsersProject = new Project.NameKey("All-Users");
+ private Project.NameKey allUsersProject = Project.nameKey("All-Users");
@Test
public void cleanZombieDraftsSmall() throws Exception {
@@ -169,8 +169,8 @@ public class DeleteZombieCommentsRefsTest {
}
private static String getRefName(int changeId, int userId) {
- Change.Id cId = new Change.Id(changeId);
- Account.Id aId = new Account.Id(userId);
+ Change.Id cId = Change.id(changeId);
+ Account.Id aId = Account.id(userId);
return RefNames.refsDraftComments(cId, aId);
}
diff --git a/javatests/com/google/gerrit/server/git/GroupCollectorTest.java b/javatests/com/google/gerrit/server/git/GroupCollectorTest.java
index f6942999e5..6175385cb2 100644
--- a/javatests/com/google/gerrit/server/git/GroupCollectorTest.java
+++ b/javatests/com/google/gerrit/server/git/GroupCollectorTest.java
@@ -19,9 +19,8 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SortedSetMultimap;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
@@ -32,7 +31,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
import org.junit.Test;
-public class GroupCollectorTest extends GerritBaseTests {
+public class GroupCollectorTest {
private TestRepository<?> tr;
@Before
@@ -285,7 +284,7 @@ public class GroupCollectorTest extends GerritBaseTests {
// TODO(dborowitz): Tests for octopus merges.
private static PatchSet.Id psId(int c, int p) {
- return new PatchSet.Id(new Change.Id(c), p);
+ return PatchSet.id(Change.id(c), p);
}
private RevWalk newWalk(ObjectId start, ObjectId branchTip) throws Exception {
diff --git a/javatests/com/google/gerrit/server/git/JGitConfigTest.java b/javatests/com/google/gerrit/server/git/JGitConfigTest.java
new file mode 100644
index 0000000000..9f6b47e7ae
--- /dev/null
+++ b/javatests/com/google/gerrit/server/git/JGitConfigTest.java
@@ -0,0 +1,84 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.server.config.SitePaths;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class JGitConfigTest {
+
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private SitePaths site;
+ private Path gitPath;
+
+ @Before
+ public void setUp() throws IOException {
+ site = new SitePaths(temporaryFolder.newFolder().toPath());
+ Files.createDirectories(site.etc_dir);
+ gitPath = Files.createDirectories(site.resolve("git"));
+
+ Files.write(
+ site.jgit_config, "[core]\n trustFolderStat = false\n".getBytes(StandardCharsets.UTF_8));
+ new SystemReaderInstaller(site).start();
+ }
+
+ @Test
+ public void test() throws IOException {
+ try (Repository repo = new FileRepository(gitPath.resolve("foo").toFile())) {
+ assertThat(repo.getConfig().getString("core", null, "trustFolderStat")).isEqualTo("false");
+ }
+ }
+
+ @Test
+ public void openSystemConfigRespectsParent() throws Exception {
+ Config parent = new Config();
+ parent.setString("foo", null, "bar", "value");
+ FileBasedConfig system = SystemReader.getInstance().openSystemConfig(parent, FS.DETECTED);
+ system.load();
+ assertThat(system.getString("core", null, "trustFolderStat")).isEqualTo("false");
+ assertThat(system.getString("foo", null, "bar")).isEqualTo("value");
+ }
+
+ @Test
+ public void openSystemConfigReturnsDifferentInstances() throws Exception {
+ FileBasedConfig system1 = SystemReader.getInstance().openSystemConfig(null, FS.DETECTED);
+ system1.load();
+ assertThat(system1.getString("core", null, "trustFolderStat")).isEqualTo("false");
+
+ FileBasedConfig system2 = SystemReader.getInstance().openSystemConfig(null, FS.DETECTED);
+ system2.load();
+ assertThat(system2.getString("core", null, "trustFolderStat")).isEqualTo("false");
+
+ system1.setString("core", null, "trustFolderStat", "true");
+ assertThat(system1.getString("core", null, "trustFolderStat")).isEqualTo("true");
+ assertThat(system2.getString("core", null, "trustFolderStat")).isEqualTo("false");
+ }
+}
diff --git a/javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java b/javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java
index 821a6e6b10..8ab7dd2134 100644
--- a/javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java
+++ b/javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java
@@ -16,11 +16,11 @@ package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.ioutil.HostPlatform;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -36,7 +36,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-public class LocalDiskRepositoryManagerTest extends GerritBaseTests {
+public class LocalDiskRepositoryManagerTest {
@Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
private Config cfg;
@@ -52,14 +52,15 @@ public class LocalDiskRepositoryManagerTest extends GerritBaseTests {
repoManager = new LocalDiskRepositoryManager(site, cfg);
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testThatNullBasePathThrowsAnException() {
- new LocalDiskRepositoryManager(site, new Config());
+ assertThrows(
+ IllegalStateException.class, () -> new LocalDiskRepositoryManager(site, new Config()));
}
@Test
public void projectCreation() throws Exception {
- Project.NameKey projectA = new Project.NameKey("projectA");
+ Project.NameKey projectA = Project.nameKey("projectA");
try (Repository repo = repoManager.createRepository(projectA)) {
assertThat(repo).isNotNull();
}
@@ -69,112 +70,149 @@ public class LocalDiskRepositoryManagerTest extends GerritBaseTests {
assertThat(repoManager.list()).containsExactly(projectA);
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithEmptyName() throws Exception {
- repoManager.createRepository(new Project.NameKey(""));
+ assertThrows(
+ RepositoryNotFoundException.class, () -> repoManager.createRepository(Project.nameKey("")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithTrailingSlash() throws Exception {
- repoManager.createRepository(new Project.NameKey("projectA/"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("projectA/")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithBackSlash() throws Exception {
- repoManager.createRepository(new Project.NameKey("a\\projectA"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("a\\projectA")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationAbsolutePath() throws Exception {
- repoManager.createRepository(new Project.NameKey("/projectA"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("/projectA")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationStartingWithDotDot() throws Exception {
- repoManager.createRepository(new Project.NameKey("../projectA"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("../projectA")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationContainsDotDot() throws Exception {
- repoManager.createRepository(new Project.NameKey("a/../projectA"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("a/../projectA")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationDotPathSegment() throws Exception {
- repoManager.createRepository(new Project.NameKey("a/./projectA"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("a/./projectA")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithTwoSlashes() throws Exception {
- repoManager.createRepository(new Project.NameKey("a//projectA"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("a//projectA")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithPathSegmentEndingByDotGit() throws Exception {
- repoManager.createRepository(new Project.NameKey("a/b.git/projectA"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("a/b.git/projectA")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithQuestionMark() throws Exception {
- repoManager.createRepository(new Project.NameKey("project?A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project?A")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithPercentageSign() throws Exception {
- repoManager.createRepository(new Project.NameKey("project%A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project%A")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithWidlcard() throws Exception {
- repoManager.createRepository(new Project.NameKey("project*A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project*A")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithColon() throws Exception {
- repoManager.createRepository(new Project.NameKey("project:A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project:A")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithLessThatSign() throws Exception {
- repoManager.createRepository(new Project.NameKey("project<A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project<A")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithGreaterThatSign() throws Exception {
- repoManager.createRepository(new Project.NameKey("project>A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project>A")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithPipe() throws Exception {
- repoManager.createRepository(new Project.NameKey("project|A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project|A")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithDollarSign() throws Exception {
- repoManager.createRepository(new Project.NameKey("project$A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project$A")));
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testProjectCreationWithCarriageReturn() throws Exception {
- repoManager.createRepository(new Project.NameKey("project\\rA"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.createRepository(Project.nameKey("project\\rA")));
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testProjectRecreation() throws Exception {
- repoManager.createRepository(new Project.NameKey("a"));
- repoManager.createRepository(new Project.NameKey("a"));
+ repoManager.createRepository(Project.nameKey("a"));
+ assertThrows(
+ IllegalStateException.class, () -> repoManager.createRepository(Project.nameKey("a")));
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testProjectRecreationAfterRestart() throws Exception {
- repoManager.createRepository(new Project.NameKey("a"));
+ repoManager.createRepository(Project.nameKey("a"));
LocalDiskRepositoryManager newRepoManager = new LocalDiskRepositoryManager(site, cfg);
- newRepoManager.createRepository(new Project.NameKey("a"));
+ assertThrows(
+ IllegalStateException.class, () -> newRepoManager.createRepository(Project.nameKey("a")));
}
@Test
public void openRepositoryCreatedDirectlyOnDisk() throws Exception {
- Project.NameKey projectA = new Project.NameKey("projectA");
+ Project.NameKey projectA = Project.nameKey("projectA");
createRepository(repoManager.getBasePath(projectA), projectA.get());
try (Repository repo = repoManager.openRepository(projectA)) {
assertThat(repo).isNotNull();
@@ -182,30 +220,36 @@ public class LocalDiskRepositoryManagerTest extends GerritBaseTests {
assertThat(repoManager.list()).containsExactly(projectA);
}
- @Test(expected = RepositoryCaseMismatchException.class)
+ @Test
public void testNameCaseMismatch() throws Exception {
assume().that(HostPlatform.isWin32() || HostPlatform.isMac()).isTrue();
- repoManager.createRepository(new Project.NameKey("a"));
- repoManager.createRepository(new Project.NameKey("A"));
+ repoManager.createRepository(Project.nameKey("a"));
+ assertThrows(
+ RepositoryCaseMismatchException.class,
+ () -> repoManager.createRepository(Project.nameKey("A")));
}
- @Test(expected = RepositoryCaseMismatchException.class)
+ @Test
public void testNameCaseMismatchWithSymlink() throws Exception {
assume().that(HostPlatform.isWin32() || HostPlatform.isMac()).isTrue();
- Project.NameKey name = new Project.NameKey("a");
+ Project.NameKey name = Project.nameKey("a");
repoManager.createRepository(name);
createSymLink(name, "b.git");
- repoManager.createRepository(new Project.NameKey("B"));
+ assertThrows(
+ RepositoryCaseMismatchException.class,
+ () -> repoManager.createRepository(Project.nameKey("B")));
}
- @Test(expected = RepositoryCaseMismatchException.class)
+ @Test
public void testNameCaseMismatchAfterRestart() throws Exception {
assume().that(HostPlatform.isWin32() || HostPlatform.isMac()).isTrue();
- Project.NameKey name = new Project.NameKey("a");
+ Project.NameKey name = Project.nameKey("a");
repoManager.createRepository(name);
LocalDiskRepositoryManager newRepoManager = new LocalDiskRepositoryManager(site, cfg);
- newRepoManager.createRepository(new Project.NameKey("A"));
+ assertThrows(
+ RepositoryCaseMismatchException.class,
+ () -> newRepoManager.createRepository(Project.nameKey("A")));
}
private void createSymLink(Project.NameKey project, String link) throws IOException {
@@ -215,20 +259,22 @@ public class LocalDiskRepositoryManagerTest extends GerritBaseTests {
Files.createSymbolicLink(symlink, projectDir);
}
- @Test(expected = RepositoryNotFoundException.class)
+ @Test
public void testOpenRepositoryInvalidName() throws Exception {
- repoManager.openRepository(new Project.NameKey("project%?|<>A"));
+ assertThrows(
+ RepositoryNotFoundException.class,
+ () -> repoManager.openRepository(Project.nameKey("project%?|<>A")));
}
@Test
public void list() throws Exception {
- Project.NameKey projectA = new Project.NameKey("projectA");
+ Project.NameKey projectA = Project.nameKey("projectA");
createRepository(repoManager.getBasePath(projectA), projectA.get());
- Project.NameKey projectB = new Project.NameKey("path/projectB");
+ Project.NameKey projectB = Project.nameKey("path/projectB");
createRepository(repoManager.getBasePath(projectB), projectB.get());
- Project.NameKey projectC = new Project.NameKey("anotherPath/path/projectC");
+ Project.NameKey projectC = Project.nameKey("anotherPath/path/projectC");
createRepository(repoManager.getBasePath(projectC), projectC.get());
// create an invalid git repo named only .git
repoManager.getBasePath(null).resolve(".git").toFile().mkdir();
diff --git a/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java b/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java
index fc79a6de5f..29f520b0b7 100644
--- a/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java
+++ b/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java
@@ -15,16 +15,14 @@
package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reset;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.config.RepositoryConfig;
import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -41,7 +39,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
+public class MultiBaseLocalDiskRepositoryManagerTest {
@Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
private Config cfg;
@@ -55,16 +53,15 @@ public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
site.resolve("git").toFile().mkdir();
cfg = new Config();
cfg.setString("gerrit", null, "basePath", "git");
- configMock = createNiceMock(RepositoryConfig.class);
- expect(configMock.getAllBasePaths()).andReturn(ImmutableList.of()).anyTimes();
- replay(configMock);
+ configMock = mock(RepositoryConfig.class);
+ when(configMock.getAllBasePaths()).thenReturn(ImmutableList.of());
repoManager = new MultiBaseLocalDiskRepositoryManager(site, cfg, configMock);
}
@Test
public void defaultRepositoryLocation()
throws RepositoryCaseMismatchException, RepositoryNotFoundException, IOException {
- Project.NameKey someProjectKey = new Project.NameKey("someProject");
+ Project.NameKey someProjectKey = Project.nameKey("someProject");
Repository repo = repoManager.createRepository(someProjectKey);
assertThat(repo.getDirectory()).isNotNull();
assertThat(repo.getDirectory().exists()).isTrue();
@@ -89,11 +86,9 @@ public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
@Test
public void alternateRepositoryLocation() throws IOException {
Path alternateBasePath = temporaryFolder.newFolder().toPath();
- Project.NameKey someProjectKey = new Project.NameKey("someProject");
- reset(configMock);
- expect(configMock.getBasePath(someProjectKey)).andReturn(alternateBasePath).anyTimes();
- expect(configMock.getAllBasePaths()).andReturn(ImmutableList.of(alternateBasePath)).anyTimes();
- replay(configMock);
+ Project.NameKey someProjectKey = Project.nameKey("someProject");
+ when(configMock.getBasePath(someProjectKey)).thenReturn(alternateBasePath);
+ when(configMock.getAllBasePaths()).thenReturn(ImmutableList.of(alternateBasePath));
Repository repo = repoManager.createRepository(someProjectKey);
assertThat(repo.getDirectory()).isNotNull();
@@ -116,18 +111,16 @@ public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
@Test
public void listReturnRepoFromProperLocation() throws IOException {
- Project.NameKey basePathProject = new Project.NameKey("basePathProject");
- Project.NameKey altPathProject = new Project.NameKey("altPathProject");
- Project.NameKey misplacedProject1 = new Project.NameKey("misplacedProject1");
- Project.NameKey misplacedProject2 = new Project.NameKey("misplacedProject2");
+ Project.NameKey basePathProject = Project.nameKey("basePathProject");
+ Project.NameKey altPathProject = Project.nameKey("altPathProject");
+ Project.NameKey misplacedProject1 = Project.nameKey("misplacedProject1");
+ Project.NameKey misplacedProject2 = Project.nameKey("misplacedProject2");
Path alternateBasePath = temporaryFolder.newFolder().toPath();
- reset(configMock);
- expect(configMock.getBasePath(altPathProject)).andReturn(alternateBasePath).anyTimes();
- expect(configMock.getBasePath(misplacedProject2)).andReturn(alternateBasePath).anyTimes();
- expect(configMock.getAllBasePaths()).andReturn(ImmutableList.of(alternateBasePath)).anyTimes();
- replay(configMock);
+ when(configMock.getBasePath(altPathProject)).thenReturn(alternateBasePath);
+ when(configMock.getBasePath(misplacedProject2)).thenReturn(alternateBasePath);
+ when(configMock.getAllBasePaths()).thenReturn(ImmutableList.of(alternateBasePath));
repoManager.createRepository(basePathProject);
repoManager.createRepository(altPathProject);
@@ -150,11 +143,14 @@ public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
}
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testRelativeAlternateLocation() {
- configMock = createNiceMock(RepositoryConfig.class);
- expect(configMock.getAllBasePaths()).andReturn(ImmutableList.of(Paths.get("repos"))).anyTimes();
- replay(configMock);
- repoManager = new MultiBaseLocalDiskRepositoryManager(site, cfg, configMock);
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ configMock = mock(RepositoryConfig.class);
+ when(configMock.getAllBasePaths()).thenReturn(ImmutableList.of(Paths.get("repos")));
+ repoManager = new MultiBaseLocalDiskRepositoryManager(site, cfg, configMock);
+ });
}
}
diff --git a/javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.java b/javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.java
index 8c1707556e..a12a8f750d 100644
--- a/javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.java
+++ b/javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteArray;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.cache.proto.Cache;
import com.google.protobuf.ByteString;
import org.eclipse.jgit.lib.ObjectId;
@@ -36,8 +36,7 @@ public class PureRevertCacheKeyTest {
0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb);
- Cache.PureRevertKeyProto key =
- PureRevertCache.key(new Project.NameKey("test"), revert, original);
+ Cache.PureRevertKeyProto key = PureRevertCache.key(Project.nameKey("test"), revert, original);
assertThat(key)
.isEqualTo(
Cache.PureRevertKeyProto.newBuilder()
diff --git a/javatests/com/google/gerrit/server/git/TagSetHolderTest.java b/javatests/com/google/gerrit/server/git/TagSetHolderTest.java
index 87ddc7582a..4fa09039d0 100644
--- a/javatests/com/google/gerrit/server/git/TagSetHolderTest.java
+++ b/javatests/com/google/gerrit/server/git/TagSetHolderTest.java
@@ -19,15 +19,14 @@ import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class TagSetHolderTest extends GerritBaseTests {
+public class TagSetHolderTest {
@Test
public void serializerWithTagSet() throws Exception {
- TagSetHolder holder = new TagSetHolder(new Project.NameKey("project"));
+ TagSetHolder holder = new TagSetHolder(Project.nameKey("project"));
holder.setTagSet(new TagSet(holder.getProjectName()));
byte[] serialized = TagSetHolder.Serializer.INSTANCE.serialize(holder);
@@ -46,7 +45,7 @@ public class TagSetHolderTest extends GerritBaseTests {
@Test
public void serializerWithoutTagSet() throws Exception {
- TagSetHolder holder = new TagSetHolder(new Project.NameKey("project"));
+ TagSetHolder holder = new TagSetHolder(Project.nameKey("project"));
byte[] serialized = TagSetHolder.Serializer.INSTANCE.serialize(holder);
assertThat(TagSetHolderProto.parseFrom(serialized))
diff --git a/javatests/com/google/gerrit/server/git/TagSetTest.java b/javatests/com/google/gerrit/server/git/TagSetTest.java
index 3ac72be63e..4a3c930464 100644
--- a/javatests/com/google/gerrit/server/git/TagSetTest.java
+++ b/javatests/com/google/gerrit/server/git/TagSetTest.java
@@ -24,13 +24,12 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto.TagSetProto;
import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto.TagSetProto.CachedRefProto;
import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto.TagSetProto.TagProto;
import com.google.gerrit.server.git.TagSet.CachedRef;
import com.google.gerrit.server.git.TagSet.Tag;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.TypeLiteral;
import java.lang.reflect.Type;
import java.util.Arrays;
@@ -43,7 +42,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
import org.junit.Test;
-public class TagSetTest extends GerritBaseTests {
+public class TagSetTest {
@Test
public void roundTripToProto() {
HashMap<String, CachedRef> refs = new HashMap<>();
@@ -60,7 +59,7 @@ public class TagSetTest extends GerritBaseTests {
tags.add(
new Tag(
ObjectId.fromString("dddddddddddddddddddddddddddddddddddddddd"), newBitSet(2, 4, 6)));
- TagSet tagSet = new TagSet(new Project.NameKey("project"), refs, tags);
+ TagSet tagSet = new TagSet(Project.nameKey("project"), refs, tags);
TagSetProto proto = tagSet.toProto();
assertThat(proto)
@@ -156,22 +155,24 @@ public class TagSetTest extends GerritBaseTests {
Map<String, CachedRef> aRefs = a.getRefsForTesting();
Map<String, CachedRef> bRefs = b.getRefsForTesting();
- assertThat(ImmutableSortedSet.copyOf(aRefs.keySet()))
- .named("ref name set")
+ assertWithMessage("ref name set")
+ .that(ImmutableSortedSet.copyOf(aRefs.keySet()))
.isEqualTo(ImmutableSortedSet.copyOf(bRefs.keySet()));
for (String name : aRefs.keySet()) {
CachedRef aRef = aRefs.get(name);
CachedRef bRef = bRefs.get(name);
- assertThat(aRef.get()).named("value of ref %s", name).isEqualTo(bRef.get());
- assertThat(aRef.flag).named("flag of ref %s", name).isEqualTo(bRef.flag);
+ assertWithMessage("value of ref %s", name).that(aRef.get()).isEqualTo(bRef.get());
+ assertWithMessage("flag of ref %s", name).that(aRef.flag).isEqualTo(bRef.flag);
}
ObjectIdOwnerMap<Tag> aTags = a.getTagsForTesting();
ObjectIdOwnerMap<Tag> bTags = b.getTagsForTesting();
- assertThat(getTagIds(aTags)).named("tag ID set").isEqualTo(getTagIds(bTags));
+ assertWithMessage("tag ID set").that(getTagIds(aTags)).isEqualTo(getTagIds(bTags));
for (Tag aTag : aTags) {
Tag bTag = bTags.get(aTag);
- assertThat(aTag.refFlags).named("flags for tag %s", aTag.name()).isEqualTo(bTag.refFlags);
+ assertWithMessage("flags for tag %s", aTag.name())
+ .that(aTag.refFlags)
+ .isEqualTo(bTag.refFlags);
}
}
diff --git a/javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java b/javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java
index dedccc23ab..690a5ccfc3 100644
--- a/javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java
+++ b/javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java
@@ -22,12 +22,11 @@ import static com.google.common.truth.Truth8.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.meta.VersionedMetaData.BatchMetaDataUpdate;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.TestTimeUtil;
import java.io.IOException;
import java.util.Arrays;
@@ -51,7 +50,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class VersionedMetaDataTest extends GerritBaseTests {
+public class VersionedMetaDataTest {
// If you're considering fleshing out this test and making it more comprehensive, please consider
// instead coming up with a replacement interface for
// VersionedMetaData/BatchMetaDataUpdate/MetaDataUpdate that is easier to use correctly.
@@ -65,7 +64,7 @@ public class VersionedMetaDataTest extends GerritBaseTests {
@Before
public void setUp() {
TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- project = new Project.NameKey("repo");
+ project = Project.nameKey("repo");
repo = new InMemoryRepository(new DfsRepositoryDescription(project.get()));
}
diff --git a/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java b/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
index 9fc6da1874..c749b77549 100644
--- a/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
+++ b/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
@@ -18,17 +18,16 @@ import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.asse
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.common.CommitInfo;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import java.io.IOException;
import java.sql.Timestamp;
@@ -44,7 +43,7 @@ import org.junit.Before;
import org.junit.Ignore;
@Ignore
-public class AbstractGroupTest extends GerritBaseTests {
+public class AbstractGroupTest {
protected static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
protected static final String SERVER_ID = "server-id";
protected static final String SERVER_NAME = "Gerrit Server";
@@ -65,9 +64,9 @@ public class AbstractGroupTest extends GerritBaseTests {
allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
repoManager = new InMemoryRepositoryManager();
allUsersRepo = repoManager.createRepository(allUsersName);
- serverAccountId = new Account.Id(SERVER_ACCOUNT_NUMBER);
+ serverAccountId = Account.id(SERVER_ACCOUNT_NUMBER);
serverIdent = new PersonIdent(SERVER_NAME, SERVER_EMAIL, TimeUtil.nowTs(), TZ);
- userId = new Account.Id(USER_ACCOUNT_NUMBER);
+ userId = Account.id(USER_ACCOUNT_NUMBER);
userIdent = newPersonIdent(userId, serverIdent);
}
@@ -124,9 +123,9 @@ public class AbstractGroupTest extends GerritBaseTests {
}
private static Optional<Account> getAccount(Account.Id id) {
- Account account = new Account(id, TimeUtil.nowTs());
+ Account.Builder account = Account.builder(id, TimeUtil.nowTs());
account.setFullName("Account " + id);
- return Optional.of(account);
+ return Optional.of(account.build());
}
private Optional<GroupDescription.Basic> getGroup(AccountGroup.UUID uuid) {
diff --git a/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java b/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java
index 120f026b27..e54ab5d242 100644
--- a/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java
+++ b/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java
@@ -18,10 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.AccountGroupByIdAudit;
+import com.google.gerrit.entities.AccountGroupMemberAudit;
import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.server.group.InternalGroup;
import java.sql.Timestamp;
@@ -37,7 +37,7 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
@Before
public void setUp() throws Exception {
- auditLogReader = new AuditLogReader(SERVER_ID, allUsersName);
+ auditLogReader = new AuditLogReader(allUsersName);
}
@Test
@@ -66,7 +66,7 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
assertThat(auditLogReader.getMembersAudit(allUsersRepo, uuid)).containsExactly(expAudit1);
// User adds account 100002 to the group.
- Account.Id id = new Account.Id(100002);
+ Account.Id id = Account.id(100002);
addMembers(uuid, ImmutableSet.of(id));
AccountGroupMemberAudit expAudit2 =
@@ -78,7 +78,7 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
// User removes account 100002 from the group.
removeMembers(uuid, ImmutableSet.of(id));
- expAudit2.removed(userId, getTipTimestamp(uuid));
+ expAudit2 = expAudit2.toBuilder().removed(userId, getTipTimestamp(uuid)).build();
assertThat(auditLogReader.getMembersAudit(allUsersRepo, uuid))
.containsExactly(expAudit1, expAudit2)
.inOrder();
@@ -94,8 +94,8 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
createExpMemberAudit(groupId, userId, userId, getTipTimestamp(uuid));
assertThat(auditLogReader.getMembersAudit(allUsersRepo, uuid)).containsExactly(expAudit1);
- Account.Id id1 = new Account.Id(100002);
- Account.Id id2 = new Account.Id(100003);
+ Account.Id id1 = Account.id(100002);
+ Account.Id id2 = Account.id(100003);
addMembers(uuid, ImmutableSet.of(id1, id2));
AccountGroupMemberAudit expAudit2 =
@@ -118,13 +118,13 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
addSubgroups(uuid, ImmutableSet.of(subgroupUuid));
- AccountGroupByIdAud expAudit =
+ AccountGroupByIdAudit expAudit =
createExpGroupAudit(group.getId(), subgroupUuid, userId, getTipTimestamp(uuid));
assertThat(auditLogReader.getSubgroupsAudit(allUsersRepo, uuid)).containsExactly(expAudit);
removeSubgroups(uuid, ImmutableSet.of(subgroupUuid));
- expAudit.removed(userId, getTipTimestamp(uuid));
+ expAudit = expAudit.toBuilder().removed(userId, getTipTimestamp(uuid)).build();
assertThat(auditLogReader.getSubgroupsAudit(allUsersRepo, uuid)).containsExactly(expAudit);
}
@@ -140,9 +140,9 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
addSubgroups(uuid, ImmutableSet.of(subgroupUuid1, subgroupUuid2));
- AccountGroupByIdAud expAudit1 =
+ AccountGroupByIdAudit expAudit1 =
createExpGroupAudit(group.getId(), subgroupUuid1, userId, getTipTimestamp(uuid));
- AccountGroupByIdAud expAudit2 =
+ AccountGroupByIdAudit expAudit2 =
createExpGroupAudit(group.getId(), subgroupUuid2, userId, getTipTimestamp(uuid));
assertThat(auditLogReader.getSubgroupsAudit(allUsersRepo, uuid))
.containsExactly(expAudit1, expAudit2)
@@ -158,9 +158,9 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
createExpMemberAudit(groupId, userId, userId, getTipTimestamp(uuid));
assertThat(auditLogReader.getMembersAudit(allUsersRepo, uuid)).containsExactly(expMemberAudit);
- Account.Id id1 = new Account.Id(100002);
- Account.Id id2 = new Account.Id(100003);
- Account.Id id3 = new Account.Id(100004);
+ Account.Id id1 = Account.id(100002);
+ Account.Id id2 = Account.id(100003);
+ Account.Id id3 = Account.id(100004);
InternalGroup subgroup1 = createGroupAsUser(2, "test-group-2");
InternalGroup subgroup2 = createGroupAsUser(3, "test-group-3");
InternalGroup subgroup3 = createGroupAsUser(4, "test-group-4");
@@ -180,23 +180,23 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
// Add one subgroup.
addSubgroups(uuid, ImmutableSet.of(subgroupUuid1));
- AccountGroupByIdAud expGroupAudit1 =
+ AccountGroupByIdAudit expGroupAudit1 =
createExpGroupAudit(group.getId(), subgroupUuid1, userId, getTipTimestamp(uuid));
assertThat(auditLogReader.getSubgroupsAudit(allUsersRepo, uuid))
.containsExactly(expGroupAudit1);
// Remove one account.
removeMembers(uuid, ImmutableSet.of(id2));
- expMemberAudit2.removed(userId, getTipTimestamp(uuid));
+ expMemberAudit2 = expMemberAudit2.toBuilder().removed(userId, getTipTimestamp(uuid)).build();
assertThat(auditLogReader.getMembersAudit(allUsersRepo, uuid))
.containsExactly(expMemberAudit, expMemberAudit1, expMemberAudit2)
.inOrder();
// Add two subgroups.
addSubgroups(uuid, ImmutableSet.of(subgroupUuid2, subgroupUuid3));
- AccountGroupByIdAud expGroupAudit2 =
+ AccountGroupByIdAudit expGroupAudit2 =
createExpGroupAudit(group.getId(), subgroupUuid2, userId, getTipTimestamp(uuid));
- AccountGroupByIdAud expGroupAudit3 =
+ AccountGroupByIdAudit expGroupAudit3 =
createExpGroupAudit(group.getId(), subgroupUuid3, userId, getTipTimestamp(uuid));
assertThat(auditLogReader.getSubgroupsAudit(allUsersRepo, uuid))
.containsExactly(expGroupAudit1, expGroupAudit2, expGroupAudit3)
@@ -215,15 +215,15 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
// Remove two subgroups.
removeSubgroups(uuid, ImmutableSet.of(subgroupUuid1, subgroupUuid3));
- expGroupAudit1.removed(userId, getTipTimestamp(uuid));
- expGroupAudit3.removed(userId, getTipTimestamp(uuid));
+ expGroupAudit1 = expGroupAudit1.toBuilder().removed(userId, getTipTimestamp(uuid)).build();
+ expGroupAudit3 = expGroupAudit3.toBuilder().removed(userId, getTipTimestamp(uuid)).build();
assertThat(auditLogReader.getSubgroupsAudit(allUsersRepo, uuid))
.containsExactly(expGroupAudit1, expGroupAudit2, expGroupAudit3)
.inOrder();
// Add back one removed subgroup.
addSubgroups(uuid, ImmutableSet.of(subgroupUuid1));
- AccountGroupByIdAud expGroupAudit4 =
+ AccountGroupByIdAudit expGroupAudit4 =
createExpGroupAudit(group.getId(), subgroupUuid1, userId, getTipTimestamp(uuid));
assertThat(auditLogReader.getSubgroupsAudit(allUsersRepo, uuid))
.containsExactly(expGroupAudit1, expGroupAudit2, expGroupAudit3, expGroupAudit4)
@@ -239,8 +239,8 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
InternalGroupCreation groupCreation =
InternalGroupCreation.builder()
.setGroupUUID(GroupUUID.make(groupName, serverIdent))
- .setNameKey(new AccountGroup.NameKey(groupName))
- .setId(new AccountGroup.Id(next))
+ .setNameKey(AccountGroup.nameKey(groupName))
+ .setId(AccountGroup.id(next))
.build();
InternalGroupUpdate groupUpdate =
authorIdent.equals(serverIdent)
@@ -303,12 +303,21 @@ public final class AuditLogReaderTest extends AbstractGroupTest {
private static AccountGroupMemberAudit createExpMemberAudit(
AccountGroup.Id groupId, Account.Id id, Account.Id addedBy, Timestamp addedOn) {
- return new AccountGroupMemberAudit(
- new AccountGroupMemberAudit.Key(id, groupId, addedOn), addedBy);
+ return AccountGroupMemberAudit.builder()
+ .groupId(groupId)
+ .memberId(id)
+ .addedOn(addedOn)
+ .addedBy(addedBy)
+ .build();
}
- private static AccountGroupByIdAud createExpGroupAudit(
+ private static AccountGroupByIdAudit createExpGroupAudit(
AccountGroup.Id groupId, AccountGroup.UUID uuid, Account.Id addedBy, Timestamp addedOn) {
- return new AccountGroupByIdAud(new AccountGroupByIdAud.Key(groupId, uuid, addedOn), addedBy);
+ return AccountGroupByIdAudit.builder()
+ .groupId(groupId)
+ .includeUuid(uuid)
+ .addedOn(addedOn)
+ .addedBy(addedBy)
+ .build();
}
}
diff --git a/javatests/com/google/gerrit/server/group/db/BUILD b/javatests/com/google/gerrit/server/group/db/BUILD
index b4652c9b6b..33033386d9 100644
--- a/javatests/com/google/gerrit/server/group/db/BUILD
+++ b/javatests/com/google/gerrit/server/group/db/BUILD
@@ -8,11 +8,11 @@ junit_tests(
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/common/data/testing:common-data-test-util",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/extensions/common/testing:common-test-util",
"//java/com/google/gerrit/git",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/group/db/testing",
"//java/com/google/gerrit/server/group/testing",
@@ -20,8 +20,8 @@ junit_tests(
"//java/com/google/gerrit/testing:gerrit-test-util",
"//java/com/google/gerrit/truth",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
index c9ba72ea2d..b7fe23d339 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
@@ -17,23 +17,24 @@ package com.google.gerrit.server.group.db;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.server.group.testing.InternalGroupSubject.internalGroups;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static com.google.gerrit.truth.OptionalSubject.assertThat;
import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.testing.InternalGroupSubject;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.truth.OptionalSubject;
import java.io.IOException;
import java.sql.Timestamp;
@@ -55,20 +56,20 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
import org.junit.Test;
-public class GroupConfigTest extends GerritBaseTests {
+public class GroupConfigTest {
private Project.NameKey projectName;
private Repository repository;
private TestRepository<?> testRepository;
- private final AccountGroup.UUID groupUuid = new AccountGroup.UUID("users-XYZ");
- private final AccountGroup.NameKey groupName = new AccountGroup.NameKey("users");
- private final AccountGroup.Id groupId = new AccountGroup.Id(123);
+ private final AccountGroup.UUID groupUuid = AccountGroup.uuid("users-XYZ");
+ private final AccountGroup.NameKey groupName = AccountGroup.nameKey("users");
+ private final AccountGroup.Id groupId = AccountGroup.id(123);
private final AuditLogFormatter auditLogFormatter =
AuditLogFormatter.createBackedBy(ImmutableSet.of(), ImmutableSet.of(), "server-id");
private final TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
@Before
public void setUp() throws Exception {
- projectName = new Project.NameKey("Test Repository");
+ projectName = Project.nameKey("Test Repository");
repository = new InMemoryRepository(new DfsRepositoryDescription("Test Repository"));
testRepository = new TestRepository<>(repository);
}
@@ -95,7 +96,7 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void nameOfGroupUpdateOverridesGroupCreation() throws Exception {
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("Another name");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("Another name");
InternalGroupCreation groupCreation =
getPrefilledGroupCreationBuilder().setNameKey(groupName).build();
@@ -109,26 +110,13 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void nameOfNewGroupMustNotBeEmpty() throws Exception {
InternalGroupCreation groupCreation =
- getPrefilledGroupCreationBuilder().setNameKey(new AccountGroup.NameKey("")).build();
+ getPrefilledGroupCreationBuilder().setNameKey(AccountGroup.nameKey("")).build();
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("Name of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
- }
- }
-
- @Test
- public void nameOfNewGroupMustNotBeNull() throws Exception {
- InternalGroupCreation groupCreation =
- getPrefilledGroupCreationBuilder().setNameKey(new AccountGroup.NameKey(null)).build();
- GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
-
- try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("Name of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
+ Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
+ assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+ assertThat(thrown).hasMessageThat().contains("Name of the group " + groupUuid);
}
}
@@ -144,13 +132,13 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void idOfNewGroupMustNotBeNegative() throws Exception {
InternalGroupCreation groupCreation =
- getPrefilledGroupCreationBuilder().setId(new AccountGroup.Id(-2)).build();
+ getPrefilledGroupCreationBuilder().setId(AccountGroup.id(-2)).build();
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("ID of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
+ Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
+ assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+ assertThat(thrown).hasMessageThat().contains("ID of the group " + groupUuid);
}
}
@@ -207,7 +195,7 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void specifiedOwnerGroupUuidIsRespectedForNewGroup() throws Exception {
- AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID("anotherOwnerUuid");
+ AccountGroup.UUID ownerGroupUuid = AccountGroup.uuid("anotherOwnerUuid");
InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
InternalGroupUpdate groupUpdate =
@@ -219,32 +207,17 @@ public class GroupConfigTest extends GerritBaseTests {
}
@Test
- public void ownerGroupUuidOfNewGroupMustNotBeNull() throws Exception {
- InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
- InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setOwnerGroupUUID(new AccountGroup.UUID(null)).build();
- GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
- groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
-
- try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("Owner UUID of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
- }
- }
-
- @Test
public void ownerGroupUuidOfNewGroupMustNotBeEmpty() throws Exception {
InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setOwnerGroupUUID(new AccountGroup.UUID("")).build();
+ InternalGroupUpdate.builder().setOwnerGroupUUID(AccountGroup.uuid("")).build();
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("Owner UUID of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
+ Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
+ assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+ assertThat(thrown).hasMessageThat().contains("Owner UUID of the group " + groupUuid);
}
}
@@ -303,8 +276,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void specifiedMembersAreRespectedForNewGroup() throws Exception {
- Account.Id member1 = new Account.Id(1);
- Account.Id member2 = new Account.Id(2);
+ Account.Id member1 = Account.id(1);
+ Account.Id member2 = Account.id(2);
InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
InternalGroupUpdate groupUpdate =
@@ -319,8 +292,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void specifiedSubgroupsAreRespectedForNewGroup() throws Exception {
- AccountGroup.UUID subgroup1 = new AccountGroup.UUID("subgroup1");
- AccountGroup.UUID subgroup2 = new AccountGroup.UUID("subgroup2");
+ AccountGroup.UUID subgroup1 = AccountGroup.uuid("subgroup1");
+ AccountGroup.UUID subgroup2 = AccountGroup.uuid("subgroup2");
InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
InternalGroupUpdate groupUpdate =
@@ -353,9 +326,11 @@ public class GroupConfigTest extends GerritBaseTests {
public void idInConfigMustBeDefined() throws Exception {
populateGroupConfig(groupUuid, "[group]\n\tname = users\n\townerGroupUuid = owners\n");
- exception.expect(ConfigInvalidException.class);
- exception.expectMessage("ID of the group " + groupUuid);
- GroupConfig.loadForGroup(projectName, repository, groupUuid);
+ ConfigInvalidException thrown =
+ assertThrows(
+ ConfigInvalidException.class,
+ () -> GroupConfig.loadForGroup(projectName, repository, groupUuid));
+ assertThat(thrown).hasMessageThat().contains("ID of the group " + groupUuid);
}
@Test
@@ -363,9 +338,11 @@ public class GroupConfigTest extends GerritBaseTests {
populateGroupConfig(
groupUuid, "[group]\n\tname = users\n\tid = -5\n\townerGroupUuid = owners\n");
- exception.expect(ConfigInvalidException.class);
- exception.expectMessage("ID of the group " + groupUuid);
- GroupConfig.loadForGroup(projectName, repository, groupUuid);
+ ConfigInvalidException thrown =
+ assertThrows(
+ ConfigInvalidException.class,
+ () -> GroupConfig.loadForGroup(projectName, repository, groupUuid));
+ assertThat(thrown).hasMessageThat().contains("ID of the group " + groupUuid);
}
@Test
@@ -389,9 +366,11 @@ public class GroupConfigTest extends GerritBaseTests {
public void ownerGroupUuidInConfigMustBeDefined() throws Exception {
populateGroupConfig(groupUuid, "[group]\n\tname = users\n\tid = 42\n");
- exception.expect(ConfigInvalidException.class);
- exception.expectMessage("Owner UUID of the group " + groupUuid);
- GroupConfig.loadForGroup(projectName, repository, groupUuid);
+ ConfigInvalidException thrown =
+ assertThrows(
+ ConfigInvalidException.class,
+ () -> GroupConfig.loadForGroup(projectName, repository, groupUuid));
+ assertThat(thrown).hasMessageThat().contains("Owner UUID of the group " + groupUuid);
}
@Test
@@ -430,12 +409,12 @@ public class GroupConfigTest extends GerritBaseTests {
.value()
.members()
.containsExactly(
- new Account.Id(1),
- new Account.Id(2),
- new Account.Id(3),
- new Account.Id(4),
- new Account.Id(5),
- new Account.Id(6));
+ Account.id(1),
+ Account.id(2),
+ Account.id(3),
+ Account.id(4),
+ Account.id(5),
+ Account.id(6));
}
@Test
@@ -443,9 +422,9 @@ public class GroupConfigTest extends GerritBaseTests {
populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
populateMembersFile(groupUuid, "One");
- exception.expect(ConfigInvalidException.class);
- exception.expectMessage("Invalid file members");
- loadGroup(groupUuid);
+ ConfigInvalidException thrown =
+ assertThrows(ConfigInvalidException.class, () -> loadGroup(groupUuid));
+ assertThat(thrown).hasMessageThat().contains("Invalid file members");
}
@Test
@@ -453,9 +432,9 @@ public class GroupConfigTest extends GerritBaseTests {
populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
populateMembersFile(groupUuid, "1\t2");
- exception.expect(ConfigInvalidException.class);
- exception.expectMessage("Invalid file members");
- loadGroup(groupUuid);
+ ConfigInvalidException thrown =
+ assertThrows(ConfigInvalidException.class, () -> loadGroup(groupUuid));
+ assertThat(thrown).hasMessageThat().contains("Invalid file members");
}
@Test
@@ -494,12 +473,12 @@ public class GroupConfigTest extends GerritBaseTests {
.value()
.subgroups()
.containsExactly(
- new AccountGroup.UUID("1"),
- new AccountGroup.UUID("2"),
- new AccountGroup.UUID("3"),
- new AccountGroup.UUID("4"),
- new AccountGroup.UUID("5"),
- new AccountGroup.UUID("6"));
+ AccountGroup.uuid("1"),
+ AccountGroup.uuid("2"),
+ AccountGroup.uuid("3"),
+ AccountGroup.uuid("4"),
+ AccountGroup.uuid("5"),
+ AccountGroup.uuid("6"));
}
@Test
@@ -508,7 +487,7 @@ public class GroupConfigTest extends GerritBaseTests {
populateSubgroupsFile(groupUuid, "1\t2 3");
Optional<InternalGroup> group = loadGroup(groupUuid);
- assertThatGroup(group).value().subgroups().containsExactly(new AccountGroup.UUID("1\t2 3"));
+ assertThatGroup(group).value().subgroups().containsExactly(AccountGroup.uuid("1\t2 3"));
}
@Test
@@ -520,13 +499,13 @@ public class GroupConfigTest extends GerritBaseTests {
assertThatGroup(group)
.value()
.subgroups()
- .containsExactly(new AccountGroup.UUID("1\t2"), new AccountGroup.UUID("3"));
+ .containsExactly(AccountGroup.uuid("1\t2"), AccountGroup.uuid("3"));
}
@Test
public void nameCanBeUpdated() throws Exception {
createArbitraryGroup(groupUuid);
- AccountGroup.NameKey newName = new AccountGroup.NameKey("New name");
+ AccountGroup.NameKey newName = AccountGroup.nameKey("New name");
InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setName(newName).build();
updateGroup(groupUuid, groupUpdate);
@@ -536,41 +515,25 @@ public class GroupConfigTest extends GerritBaseTests {
}
@Test
- public void nameCannotBeUpdatedToNull() throws Exception {
- createArbitraryGroup(groupUuid);
-
- GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repository, groupUuid);
- InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey(null)).build();
- groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
-
- try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("Name of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
- }
- }
-
- @Test
public void nameCannotBeUpdatedToEmptyString() throws Exception {
createArbitraryGroup(groupUuid);
GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repository, groupUuid);
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("")).build();
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("Name of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
+ Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
+ assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+ assertThat(thrown).hasMessageThat().contains("Name of the group " + groupUuid);
}
}
@Test
public void nameCanBeUpdatedToEmptyStringIfExplicitlySpecified() throws Exception {
createArbitraryGroup(groupUuid);
- AccountGroup.NameKey emptyName = new AccountGroup.NameKey("");
+ AccountGroup.NameKey emptyName = AccountGroup.nameKey("");
GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repository, groupUuid);
groupConfig.setAllowSaveEmptyName();
@@ -608,7 +571,7 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void ownerGroupUuidCanBeUpdated() throws Exception {
createArbitraryGroup(groupUuid);
- AccountGroup.UUID newOwnerGroupUuid = new AccountGroup.UUID("New owner");
+ AccountGroup.UUID newOwnerGroupUuid = AccountGroup.uuid("New owner");
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setOwnerGroupUUID(newOwnerGroupUuid).build();
@@ -619,34 +582,18 @@ public class GroupConfigTest extends GerritBaseTests {
}
@Test
- public void ownerGroupUuidCannotBeUpdatedToNull() throws Exception {
- createArbitraryGroup(groupUuid);
-
- GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repository, groupUuid);
- InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setOwnerGroupUUID(new AccountGroup.UUID(null)).build();
- groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
-
- try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("Owner UUID of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
- }
- }
-
- @Test
public void ownerGroupUuidCannotBeUpdatedToEmptyString() throws Exception {
createArbitraryGroup(groupUuid);
GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repository, groupUuid);
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setOwnerGroupUUID(new AccountGroup.UUID("")).build();
+ InternalGroupUpdate.builder().setOwnerGroupUUID(AccountGroup.uuid("")).build();
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- exception.expectCause(instanceOf(ConfigInvalidException.class));
- exception.expectMessage("Owner UUID of the group " + groupUuid);
- groupConfig.commit(metaDataUpdate);
+ Throwable thrown = assertThrows(Throwable.class, () -> groupConfig.commit(metaDataUpdate));
+ assertThat(thrown.getCause(), instanceOf(ConfigInvalidException.class));
+ assertThat(thrown).hasMessageThat().contains("Owner UUID of the group " + groupUuid);
}
}
@@ -675,7 +622,7 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate laterGroupUpdate =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(updatedOn)
.build();
Optional<InternalGroup> group = updateGroup(groupCreation.getGroupUUID(), laterGroupUpdate);
@@ -688,8 +635,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void membersCanBeAdded() throws Exception {
createArbitraryGroup(groupUuid);
- Account.Id member1 = new Account.Id(1);
- Account.Id member2 = new Account.Id(2);
+ Account.Id member1 = Account.id(1);
+ Account.Id member2 = Account.id(2);
InternalGroupUpdate groupUpdate1 =
InternalGroupUpdate.builder()
@@ -710,8 +657,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void membersCanBeDeleted() throws Exception {
createArbitraryGroup(groupUuid);
- Account.Id member1 = new Account.Id(1);
- Account.Id member2 = new Account.Id(2);
+ Account.Id member1 = Account.id(1);
+ Account.Id member2 = Account.id(2);
InternalGroupUpdate groupUpdate1 =
InternalGroupUpdate.builder()
@@ -732,8 +679,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void subgroupsCanBeAdded() throws Exception {
createArbitraryGroup(groupUuid);
- AccountGroup.UUID subgroup1 = new AccountGroup.UUID("subgroups1");
- AccountGroup.UUID subgroup2 = new AccountGroup.UUID("subgroups2");
+ AccountGroup.UUID subgroup1 = AccountGroup.uuid("subgroups1");
+ AccountGroup.UUID subgroup2 = AccountGroup.uuid("subgroups2");
InternalGroupUpdate groupUpdate1 =
InternalGroupUpdate.builder()
@@ -754,8 +701,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void subgroupsCanBeDeleted() throws Exception {
createArbitraryGroup(groupUuid);
- AccountGroup.UUID subgroup1 = new AccountGroup.UUID("subgroups1");
- AccountGroup.UUID subgroup2 = new AccountGroup.UUID("subgroups2");
+ AccountGroup.UUID subgroup1 = AccountGroup.uuid("subgroups1");
+ AccountGroup.UUID subgroup2 = AccountGroup.uuid("subgroups2");
InternalGroupUpdate groupUpdate1 =
InternalGroupUpdate.builder()
@@ -798,13 +745,12 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setDescription("A test group")
- .setOwnerGroupUUID(new AccountGroup.UUID("another owner"))
+ .setOwnerGroupUUID(AccountGroup.uuid("another owner"))
.setVisibleToAll(true)
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(new Timestamp(92900892))
- .setMemberModification(members -> ImmutableSet.of(new Account.Id(1), new Account.Id(2)))
- .setSubgroupModification(
- subgroups -> ImmutableSet.of(new AccountGroup.UUID("subgroup")))
+ .setMemberModification(members -> ImmutableSet.of(Account.id(1), Account.id(2)))
+ .setSubgroupModification(subgroups -> ImmutableSet.of(AccountGroup.uuid("subgroup")))
.build();
Optional<InternalGroup> createdGroup = createGroup(groupCreation, groupUpdate);
@@ -820,13 +766,12 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setDescription("A test group")
- .setOwnerGroupUUID(new AccountGroup.UUID("another owner"))
+ .setOwnerGroupUUID(AccountGroup.uuid("another owner"))
.setVisibleToAll(true)
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(new Timestamp(92900892))
- .setMemberModification(members -> ImmutableSet.of(new Account.Id(1), new Account.Id(2)))
- .setSubgroupModification(
- subgroups -> ImmutableSet.of(new AccountGroup.UUID("subgroup")))
+ .setMemberModification(members -> ImmutableSet.of(Account.id(1), Account.id(2)))
+ .setSubgroupModification(subgroups -> ImmutableSet.of(AccountGroup.uuid("subgroup")))
.build();
Optional<InternalGroup> updatedGroup = updateGroup(groupUuid, groupUpdate);
@@ -843,19 +788,18 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate initialGroupUpdate =
InternalGroupUpdate.builder()
.setDescription("A test group")
- .setOwnerGroupUUID(new AccountGroup.UUID("another owner"))
+ .setOwnerGroupUUID(AccountGroup.uuid("another owner"))
.setVisibleToAll(true)
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(new Timestamp(92900892))
- .setMemberModification(members -> ImmutableSet.of(new Account.Id(1), new Account.Id(2)))
- .setSubgroupModification(
- subgroups -> ImmutableSet.of(new AccountGroup.UUID("subgroup")))
+ .setMemberModification(members -> ImmutableSet.of(Account.id(1), Account.id(2)))
+ .setSubgroupModification(subgroups -> ImmutableSet.of(AccountGroup.uuid("subgroup")))
.build();
createGroup(groupCreation, initialGroupUpdate);
// Only update one of the properties.
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("Another name")).build();
Optional<InternalGroup> updatedGroup = updateGroup(groupCreation.getGroupUUID(), groupUpdate);
Optional<InternalGroup> reloadedGroup = loadGroup(groupCreation.getGroupUUID());
@@ -870,7 +814,7 @@ public class GroupConfigTest extends GerritBaseTests {
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
commit(groupConfig);
- AccountGroup.NameKey name = new AccountGroup.NameKey("Robots");
+ AccountGroup.NameKey name = AccountGroup.nameKey("Robots");
InternalGroupUpdate groupUpdate1 = InternalGroupUpdate.builder().setName(name).build();
groupConfig.setGroupUpdate(groupUpdate1, auditLogFormatter);
commit(groupConfig);
@@ -907,7 +851,7 @@ public class GroupConfigTest extends GerritBaseTests {
RevCommit commitAfterCreation = getLatestCommitForGroup(groupUuid);
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("Another name")).build();
updateGroup(groupUuid, groupUpdate);
RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
@@ -990,9 +934,7 @@ public class GroupConfigTest extends GerritBaseTests {
createArbitraryGroup(groupUuid);
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder()
- .setOwnerGroupUUID(new AccountGroup.UUID("Another owner"))
- .build();
+ InternalGroupUpdate.builder().setOwnerGroupUUID(AccountGroup.uuid("Another owner")).build();
updateGroup(groupUuid, groupUpdate);
RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
@@ -1008,8 +950,7 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setMemberModification(
- members -> Sets.union(members, ImmutableSet.of(new Account.Id(10))))
+ .setMemberModification(members -> Sets.union(members, ImmutableSet.of(Account.id(10))))
.build();
updateGroup(groupUuid, groupUpdate);
@@ -1027,8 +968,7 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setSubgroupModification(
- subgroups ->
- Sets.union(subgroups, ImmutableSet.of(new AccountGroup.UUID("subgroup"))))
+ subgroups -> Sets.union(subgroups, ImmutableSet.of(AccountGroup.uuid("subgroup"))))
.build();
updateGroup(groupUuid, groupUpdate);
@@ -1045,7 +985,7 @@ public class GroupConfigTest extends GerritBaseTests {
getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("Another name")).build();
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
@@ -1128,7 +1068,7 @@ public class GroupConfigTest extends GerritBaseTests {
.build();
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(createdOn)
.build();
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
@@ -1161,7 +1101,7 @@ public class GroupConfigTest extends GerritBaseTests {
.build();
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(createdOn)
.build();
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
@@ -1187,7 +1127,7 @@ public class GroupConfigTest extends GerritBaseTests {
createArbitraryGroup(groupUuid);
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("Another name")).build();
updateGroup(groupUuid, groupUpdate);
RevCommit revCommit = getLatestCommitForGroup(groupUuid);
@@ -1202,7 +1142,7 @@ public class GroupConfigTest extends GerritBaseTests {
createArbitraryGroup(groupUuid);
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(new Timestamp(updatedOnAsSecondsSinceEpoch * 1000))
.build();
updateGroup(groupUuid, groupUpdate);
@@ -1220,7 +1160,7 @@ public class GroupConfigTest extends GerritBaseTests {
createArbitraryGroup(groupUuid);
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(updatedOn)
.build();
GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repository, groupUuid);
@@ -1248,7 +1188,7 @@ public class GroupConfigTest extends GerritBaseTests {
createArbitraryGroup(groupUuid);
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("Another name"))
+ .setName(AccountGroup.nameKey("Another name"))
.setUpdatedOn(updatedOn)
.build();
GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repository, groupUuid);
@@ -1281,14 +1221,14 @@ public class GroupConfigTest extends GerritBaseTests {
public void groupCanBeLoadedAtASpecificRevision() throws Exception {
createArbitraryGroup(groupUuid);
- AccountGroup.NameKey firstName = new AccountGroup.NameKey("Bots");
+ AccountGroup.NameKey firstName = AccountGroup.nameKey("Bots");
InternalGroupUpdate groupUpdate1 = InternalGroupUpdate.builder().setName(firstName).build();
updateGroup(groupUuid, groupUpdate1);
RevCommit commitAfterUpdate1 = getLatestCommitForGroup(groupUuid);
InternalGroupUpdate groupUpdate2 =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Robots")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("Robots")).build();
updateGroup(groupUuid, groupUpdate2);
GroupConfig groupConfig =
@@ -1315,7 +1255,7 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupCreation groupCreation =
getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("Another name")).build();
createGroup(groupCreation, groupUpdate);
RevCommit revCommit = getLatestCommitForGroup(groupUuid);
@@ -1324,8 +1264,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void commitMessageOfNewGroupWithMembersContainsFooters() throws Exception {
- Account account13 = createAccount(new Account.Id(13), "John");
- Account account7 = createAccount(new Account.Id(7), "Jane");
+ Account account13 = createAccount(Account.id(13), "John");
+ Account account7 = createAccount(Account.id(7), "Jane");
ImmutableSet<Account> accounts = ImmutableSet.of(account13, account7);
AuditLogFormatter auditLogFormatter =
@@ -1335,7 +1275,7 @@ public class GroupConfigTest extends GerritBaseTests {
getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setMemberModification(members -> ImmutableSet.of(account13.getId(), account7.getId()))
+ .setMemberModification(members -> ImmutableSet.of(account13.id(), account7.id()))
.build();
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
@@ -1349,8 +1289,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void commitMessageOfNewGroupWithSubgroupsContainsFooters() throws Exception {
- GroupDescription.Basic group1 = createGroup(new AccountGroup.UUID("129403"), "Bots");
- GroupDescription.Basic group2 = createGroup(new AccountGroup.UUID("8903493"), "Verifiers");
+ GroupDescription.Basic group1 = createGroup(AccountGroup.uuid("129403"), "Bots");
+ GroupDescription.Basic group2 = createGroup(AccountGroup.uuid("8903493"), "Verifiers");
ImmutableSet<GroupDescription.Basic> groups = ImmutableSet.of(group1, group2);
AuditLogFormatter auditLogFormatter =
@@ -1374,8 +1314,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void commitMessageOfMemberAdditionContainsFooters() throws Exception {
- Account account13 = createAccount(new Account.Id(13), "John");
- Account account7 = createAccount(new Account.Id(7), "Jane");
+ Account account13 = createAccount(Account.id(13), "John");
+ Account account7 = createAccount(Account.id(7), "Jane");
ImmutableSet<Account> accounts = ImmutableSet.of(account13, account7);
createArbitraryGroup(groupUuid);
@@ -1385,7 +1325,7 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
- .setMemberModification(members -> ImmutableSet.of(account13.getId(), account7.getId()))
+ .setMemberModification(members -> ImmutableSet.of(account13.id(), account7.id()))
.build();
updateGroup(groupUuid, groupUpdate, auditLogFormatter);
@@ -1396,8 +1336,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void commitMessageOfMemberRemovalContainsFooters() throws Exception {
- Account account13 = createAccount(new Account.Id(13), "John");
- Account account7 = createAccount(new Account.Id(7), "Jane");
+ Account account13 = createAccount(Account.id(13), "John");
+ Account account7 = createAccount(Account.id(7), "Jane");
ImmutableSet<Account> accounts = ImmutableSet.of(account13, account7);
createArbitraryGroup(groupUuid);
@@ -1407,13 +1347,13 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate groupUpdate1 =
InternalGroupUpdate.builder()
- .setMemberModification(members -> ImmutableSet.of(account13.getId(), account7.getId()))
+ .setMemberModification(members -> ImmutableSet.of(account13.id(), account7.id()))
.build();
updateGroup(groupUuid, groupUpdate1, auditLogFormatter);
InternalGroupUpdate groupUpdate2 =
InternalGroupUpdate.builder()
- .setMemberModification(members -> ImmutableSet.of(account7.getId()))
+ .setMemberModification(members -> ImmutableSet.of(account7.id()))
.build();
updateGroup(groupUuid, groupUpdate2, auditLogFormatter);
@@ -1423,8 +1363,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void commitMessageOfSubgroupAdditionContainsFooters() throws Exception {
- GroupDescription.Basic group1 = createGroup(new AccountGroup.UUID("129403"), "Bots");
- GroupDescription.Basic group2 = createGroup(new AccountGroup.UUID("8903493"), "Verifiers");
+ GroupDescription.Basic group1 = createGroup(AccountGroup.uuid("129403"), "Bots");
+ GroupDescription.Basic group2 = createGroup(AccountGroup.uuid("8903493"), "Verifiers");
ImmutableSet<GroupDescription.Basic> groups = ImmutableSet.of(group1, group2);
createArbitraryGroup(groupUuid);
@@ -1446,8 +1386,8 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void commitMessageOfSubgroupRemovalContainsFooters() throws Exception {
- GroupDescription.Basic group1 = createGroup(new AccountGroup.UUID("129403"), "Bots");
- GroupDescription.Basic group2 = createGroup(new AccountGroup.UUID("8903493"), "Verifiers");
+ GroupDescription.Basic group1 = createGroup(AccountGroup.uuid("129403"), "Bots");
+ GroupDescription.Basic group2 = createGroup(AccountGroup.uuid("8903493"), "Verifiers");
ImmutableSet<GroupDescription.Basic> groups = ImmutableSet.of(group1, group2);
createArbitraryGroup(groupUuid);
@@ -1478,11 +1418,11 @@ public class GroupConfigTest extends GerritBaseTests {
createArbitraryGroup(groupUuid);
InternalGroupUpdate groupUpdate1 =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Old name")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("Old name")).build();
updateGroup(groupUuid, groupUpdate1);
InternalGroupUpdate groupUpdate2 =
- InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("New name")).build();
+ InternalGroupUpdate.builder().setName(AccountGroup.nameKey("New name")).build();
updateGroup(groupUuid, groupUpdate2);
RevCommit revCommit = getLatestCommitForGroup(groupUuid);
@@ -1492,11 +1432,11 @@ public class GroupConfigTest extends GerritBaseTests {
@Test
public void commitMessageFootersCanBeMixed() throws Exception {
- Account account13 = createAccount(new Account.Id(13), "John");
- Account account7 = createAccount(new Account.Id(7), "Jane");
+ Account account13 = createAccount(Account.id(13), "John");
+ Account account7 = createAccount(Account.id(7), "Jane");
ImmutableSet<Account> accounts = ImmutableSet.of(account13, account7);
- GroupDescription.Basic group1 = createGroup(new AccountGroup.UUID("129403"), "Bots");
- GroupDescription.Basic group2 = createGroup(new AccountGroup.UUID("8903493"), "Verifiers");
+ GroupDescription.Basic group1 = createGroup(AccountGroup.uuid("129403"), "Bots");
+ GroupDescription.Basic group2 = createGroup(AccountGroup.uuid("8903493"), "Verifiers");
ImmutableSet<GroupDescription.Basic> groups = ImmutableSet.of(group1, group2);
createArbitraryGroup(groupUuid);
@@ -1506,16 +1446,16 @@ public class GroupConfigTest extends GerritBaseTests {
InternalGroupUpdate groupUpdate1 =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("Old name"))
- .setMemberModification(members -> ImmutableSet.of(account7.getId()))
+ .setName(AccountGroup.nameKey("Old name"))
+ .setMemberModification(members -> ImmutableSet.of(account7.id()))
.setSubgroupModification(subgroups -> ImmutableSet.of(group2.getGroupUUID()))
.build();
updateGroup(groupUuid, groupUpdate1, auditLogFormatter);
InternalGroupUpdate groupUpdate2 =
InternalGroupUpdate.builder()
- .setName(new AccountGroup.NameKey("New name"))
- .setMemberModification(members -> ImmutableSet.of(account13.getId()))
+ .setName(AccountGroup.nameKey("New name"))
+ .setMemberModification(members -> ImmutableSet.of(account13.id()))
.setSubgroupModification(subgroups -> ImmutableSet.of(group1.getGroupUUID()))
.build();
updateGroup(groupUuid, groupUpdate2, auditLogFormatter);
@@ -1623,7 +1563,7 @@ public class GroupConfigTest extends GerritBaseTests {
MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(
- GitReferenceUpdated.DISABLED, new Project.NameKey("Test Repository"), repository);
+ GitReferenceUpdated.DISABLED, Project.nameKey("Test Repository"), repository);
metaDataUpdate.getCommitBuilder().setCommitter(serverIdent);
metaDataUpdate.getCommitBuilder().setAuthor(serverIdent);
return metaDataUpdate;
@@ -1641,9 +1581,9 @@ public class GroupConfigTest extends GerritBaseTests {
}
private static Account createAccount(Account.Id id, String name) {
- Account account = new Account(id, TimeUtil.nowTs());
+ Account.Builder account = Account.builder(id, TimeUtil.nowTs());
account.setFullName(name);
- return account;
+ return account.build();
}
private static GroupDescription.Basic createGroup(AccountGroup.UUID uuid, String name) {
diff --git a/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java b/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
index 2a8d55162e..df97e8809d 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
@@ -15,11 +15,11 @@
package com.google.gerrit.server.group.db;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.common.data.testing.GroupReferenceSubject.groupReferences;
+import static com.google.gerrit.entities.RefNames.REFS_GROUPNAMES;
import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.commits;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_GROUPNAMES;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static com.google.gerrit.truth.OptionalSubject.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -27,19 +27,18 @@ import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.testing.GroupReferenceSubject;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.testing.CommitInfoSubject;
import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.GitTestUtil;
import com.google.gerrit.testing.TestTimeUtil;
import com.google.gerrit.truth.ListSubject;
@@ -69,13 +68,13 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class GroupNameNotesTest extends GerritBaseTests {
+public class GroupNameNotesTest {
private static final String SERVER_NAME = "Gerrit Server";
private static final String SERVER_EMAIL = "noreply@gerritcodereview.com";
private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
- private final AccountGroup.UUID groupUuid = new AccountGroup.UUID("users-XYZ");
- private final AccountGroup.NameKey groupName = new AccountGroup.NameKey("users");
+ private final AccountGroup.UUID groupUuid = AccountGroup.uuid("users-XYZ");
+ private final AccountGroup.NameKey groupName = AccountGroup.nameKey("users");
private AtomicInteger idCounter;
private AllUsersName allUsersName;
@@ -105,19 +104,21 @@ public class GroupNameNotesTest extends GerritBaseTests {
@Test
public void uuidOfNewGroupMustNotBeNull() throws Exception {
- exception.expect(NullPointerException.class);
- GroupNameNotes.forNewGroup(allUsersName, repo, null, groupName);
+ assertThrows(
+ NullPointerException.class,
+ () -> GroupNameNotes.forNewGroup(allUsersName, repo, null, groupName));
}
@Test
public void nameOfNewGroupMustNotBeNull() throws Exception {
- exception.expect(NullPointerException.class);
- GroupNameNotes.forNewGroup(allUsersName, repo, groupUuid, null);
+ assertThrows(
+ NullPointerException.class,
+ () -> GroupNameNotes.forNewGroup(allUsersName, repo, groupUuid, null));
}
@Test
public void nameOfNewGroupMayBeEmpty() throws Exception {
- AccountGroup.NameKey emptyName = new AccountGroup.NameKey("");
+ AccountGroup.NameKey emptyName = AccountGroup.nameKey("");
createGroup(groupUuid, emptyName);
Optional<GroupReference> groupReference = loadGroup(emptyName);
@@ -128,17 +129,19 @@ public class GroupNameNotesTest extends GerritBaseTests {
public void newGroupMustNotReuseNameOfAnotherGroup() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("AnotherGroup");
- exception.expect(DuplicateKeyException.class);
- exception.expectMessage(groupName.get());
- GroupNameNotes.forNewGroup(allUsersName, repo, anotherGroupUuid, groupName);
+ AccountGroup.UUID anotherGroupUuid = AccountGroup.uuid("AnotherGroup");
+ DuplicateKeyException thrown =
+ assertThrows(
+ DuplicateKeyException.class,
+ () -> GroupNameNotes.forNewGroup(allUsersName, repo, anotherGroupUuid, groupName));
+ assertThat(thrown).hasMessageThat().contains(groupName.get());
}
@Test
public void newGroupMayReuseUuidOfAnotherGroup() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
createGroup(groupUuid, anotherName);
Optional<GroupReference> group1 = loadGroup(groupName);
@@ -151,7 +154,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
public void groupCanBeRenamed() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
renameGroup(groupUuid, groupName, anotherName);
Optional<GroupReference> groupReference = loadGroup(anotherName);
@@ -163,7 +166,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
public void previousNameOfGroupCannotBeUsedAfterRename() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
renameGroup(groupUuid, groupName, anotherName);
Optional<GroupReference> group = loadGroup(groupName);
@@ -173,61 +176,75 @@ public class GroupNameNotesTest extends GerritBaseTests {
@Test
public void groupCannotBeRenamedToNull() throws Exception {
createGroup(groupUuid, groupName);
-
- exception.expect(NullPointerException.class);
- GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, null);
+ assertThrows(
+ NullPointerException.class,
+ () -> GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, null));
}
@Test
public void oldNameOfGroupMustBeSpecifiedForRename() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
- exception.expect(NullPointerException.class);
- GroupNameNotes.forRename(allUsersName, repo, groupUuid, null, anotherName);
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
+ assertThrows(
+ NullPointerException.class,
+ () -> GroupNameNotes.forRename(allUsersName, repo, groupUuid, null, anotherName));
}
@Test
public void groupCannotBeRenamedWhenOldNameIsWrong() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherOldName = new AccountGroup.NameKey("contributors");
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
- exception.expect(ConfigInvalidException.class);
- exception.expectMessage(anotherOldName.get());
- GroupNameNotes.forRename(allUsersName, repo, groupUuid, anotherOldName, anotherName);
+ AccountGroup.NameKey anotherOldName = AccountGroup.nameKey("contributors");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
+ ConfigInvalidException thrown =
+ assertThrows(
+ ConfigInvalidException.class,
+ () ->
+ GroupNameNotes.forRename(
+ allUsersName, repo, groupUuid, anotherOldName, anotherName));
+ assertThat(thrown).hasMessageThat().contains(anotherOldName.get());
}
@Test
public void groupCannotBeRenamedToNameOfAnotherGroup() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("admins-ABC");
- AccountGroup.NameKey anotherGroupName = new AccountGroup.NameKey("admins");
+ AccountGroup.UUID anotherGroupUuid = AccountGroup.uuid("admins-ABC");
+ AccountGroup.NameKey anotherGroupName = AccountGroup.nameKey("admins");
createGroup(anotherGroupUuid, anotherGroupName);
- exception.expect(DuplicateKeyException.class);
- exception.expectMessage(anotherGroupName.get());
- GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, anotherGroupName);
+ DuplicateKeyException thrown =
+ assertThrows(
+ DuplicateKeyException.class,
+ () ->
+ GroupNameNotes.forRename(
+ allUsersName, repo, groupUuid, groupName, anotherGroupName));
+ assertThat(thrown).hasMessageThat().contains(anotherGroupName.get());
}
@Test
public void groupCannotBeRenamedWithoutSpecifiedUuid() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
- exception.expect(NullPointerException.class);
- GroupNameNotes.forRename(allUsersName, repo, null, groupName, anotherName);
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
+ assertThrows(
+ NullPointerException.class,
+ () -> GroupNameNotes.forRename(allUsersName, repo, null, groupName, anotherName));
}
@Test
public void groupCannotBeRenamedWhenUuidIsWrong() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("admins-ABC");
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
- exception.expect(ConfigInvalidException.class);
- exception.expectMessage(groupUuid.get());
- GroupNameNotes.forRename(allUsersName, repo, anotherGroupUuid, groupName, anotherName);
+ AccountGroup.UUID anotherGroupUuid = AccountGroup.uuid("admins-ABC");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
+ ConfigInvalidException thrown =
+ assertThrows(
+ ConfigInvalidException.class,
+ () ->
+ GroupNameNotes.forRename(
+ allUsersName, repo, anotherGroupUuid, groupName, anotherName));
+ assertThat(thrown).hasMessageThat().contains(groupUuid.get());
}
@Test
@@ -248,8 +265,8 @@ public class GroupNameNotesTest extends GerritBaseTests {
createGroup(groupUuid, groupName);
ImmutableList<CommitInfo> commitsAfterCreation = log();
- AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("admins-ABC");
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
+ AccountGroup.UUID anotherGroupUuid = AccountGroup.uuid("admins-ABC");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
createGroup(anotherGroupUuid, anotherName);
ImmutableList<CommitInfo> commitsAfterFurtherGroup = log();
@@ -262,7 +279,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
createGroup(groupUuid, groupName);
ImmutableList<CommitInfo> commitsAfterCreation = log();
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
renameGroup(groupUuid, groupName, anotherName);
ImmutableList<CommitInfo> commitsAfterRename = log();
@@ -298,7 +315,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
public void newCommitIsNotCreatedWhenCommittingGroupRenamingTwice() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
GroupNameNotes groupNameNotes =
GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, anotherName);
@@ -323,7 +340,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
public void commitMessageMentionsGroupRenaming() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
+ AccountGroup.NameKey anotherName = AccountGroup.nameKey("admins");
renameGroup(groupUuid, groupName, anotherName);
ImmutableList<CommitInfo> commits = log();
@@ -341,18 +358,18 @@ public class GroupNameNotesTest extends GerritBaseTests {
@Test
public void nonExistentGroupCannotBeLoaded() throws Exception {
- createGroup(new AccountGroup.UUID("contributors-MN"), new AccountGroup.NameKey("contributors"));
+ createGroup(AccountGroup.uuid("contributors-MN"), AccountGroup.nameKey("contributors"));
createGroup(groupUuid, groupName);
- Optional<GroupReference> group = loadGroup(new AccountGroup.NameKey("admins"));
+ Optional<GroupReference> group = loadGroup(AccountGroup.nameKey("admins"));
assertThatGroup(group).isAbsent();
}
@Test
public void specificGroupCanBeLoaded() throws Exception {
- createGroup(new AccountGroup.UUID("contributors-MN"), new AccountGroup.NameKey("contributors"));
+ createGroup(AccountGroup.uuid("contributors-MN"), AccountGroup.nameKey("contributors"));
createGroup(groupUuid, groupName);
- createGroup(new AccountGroup.UUID("admins-ABC"), new AccountGroup.NameKey("admins"));
+ createGroup(AccountGroup.uuid("admins-ABC"), AccountGroup.nameKey("admins"));
Optional<GroupReference> group = loadGroup(groupName);
assertThatGroup(group).value().groupUuid().isEqualTo(groupUuid);
@@ -367,11 +384,11 @@ public class GroupNameNotesTest extends GerritBaseTests {
@Test
public void allGroupsCanBeLoaded() throws Exception {
- AccountGroup.UUID groupUuid1 = new AccountGroup.UUID("contributors-MN");
- AccountGroup.NameKey groupName1 = new AccountGroup.NameKey("contributors");
+ AccountGroup.UUID groupUuid1 = AccountGroup.uuid("contributors-MN");
+ AccountGroup.NameKey groupName1 = AccountGroup.nameKey("contributors");
createGroup(groupUuid1, groupName1);
- AccountGroup.UUID groupUuid2 = new AccountGroup.UUID("admins-ABC");
- AccountGroup.NameKey groupName2 = new AccountGroup.NameKey("admins");
+ AccountGroup.UUID groupUuid2 = AccountGroup.uuid("admins-ABC");
+ AccountGroup.NameKey groupName2 = AccountGroup.nameKey("admins");
createGroup(groupUuid2, groupName2);
ImmutableList<GroupReference> allGroups = GroupNameNotes.loadAllGroups(repo);
@@ -384,7 +401,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
@Test
public void loadedGroupsContainGroupsWithDuplicateGroupUuids() throws Exception {
createGroup(groupUuid, groupName);
- AccountGroup.NameKey anotherGroupName = new AccountGroup.NameKey("admins");
+ AccountGroup.NameKey anotherGroupName = AccountGroup.nameKey("admins");
createGroup(groupUuid, anotherGroupName);
ImmutableList<GroupReference> allGroups = GroupNameNotes.loadAllGroups(repo);
@@ -424,10 +441,10 @@ public class GroupNameNotesTest extends GerritBaseTests {
GroupReference g1 = newGroup("a");
GroupReference g2 = newGroup("b");
- try (TestRepository<?> tr = new TestRepository<>(repo)) {
+ try (TestRepository<Repository> tr = new TestRepository<>(repo)) {
ObjectId k1 = getNoteKey(g1);
ObjectId k2 = getNoteKey(g2);
- ObjectId k3 = GroupNameNotes.getNoteKey(new AccountGroup.NameKey("c"));
+ ObjectId k3 = GroupNameNotes.getNoteKey(AccountGroup.nameKey("c"));
PersonIdent ident = newPersonIdent();
ObjectId origCommitId =
tr.branch(REFS_GROUPNAMES)
@@ -481,14 +498,14 @@ public class GroupNameNotesTest extends GerritBaseTests {
@Test
public void updateGroupNamesRejectsNonOneToOneGroupReferences() throws Exception {
assertIllegalArgument(
- new GroupReference(new AccountGroup.UUID("uuid1"), "name1"),
- new GroupReference(new AccountGroup.UUID("uuid1"), "name2"));
+ new GroupReference(AccountGroup.uuid("uuid1"), "name1"),
+ new GroupReference(AccountGroup.uuid("uuid1"), "name2"));
assertIllegalArgument(
- new GroupReference(new AccountGroup.UUID("uuid1"), "name1"),
- new GroupReference(new AccountGroup.UUID("uuid2"), "name1"));
+ new GroupReference(AccountGroup.uuid("uuid1"), "name1"),
+ new GroupReference(AccountGroup.uuid("uuid2"), "name1"));
assertIllegalArgument(
- new GroupReference(new AccountGroup.UUID("uuid1"), "name1"),
- new GroupReference(new AccountGroup.UUID("uuid1"), "name1"));
+ new GroupReference(AccountGroup.uuid("uuid1"), "name1"),
+ new GroupReference(AccountGroup.uuid("uuid1"), "name1"));
}
@Test
@@ -529,8 +546,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
PersonIdent serverIdent = newPersonIdent();
MetaDataUpdate metaDataUpdate =
- new MetaDataUpdate(
- GitReferenceUpdated.DISABLED, new Project.NameKey("Test Repository"), repo);
+ new MetaDataUpdate(GitReferenceUpdated.DISABLED, Project.nameKey("Test Repository"), repo);
metaDataUpdate.getCommitBuilder().setCommitter(serverIdent);
metaDataUpdate.getCommitBuilder().setAuthor(serverIdent);
return metaDataUpdate;
@@ -538,7 +554,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
private GroupReference newGroup(String name) {
int id = idCounter.incrementAndGet();
- return new GroupReference(new AccountGroup.UUID(name + "-" + id), name);
+ return new GroupReference(AccountGroup.uuid(name + "-" + id), name);
}
private static PersonIdent newPersonIdent() {
@@ -546,7 +562,7 @@ public class GroupNameNotesTest extends GerritBaseTests {
}
private static ObjectId getNoteKey(GroupReference g) {
- return GroupNameNotes.getNoteKey(new AccountGroup.NameKey(g.getName()));
+ return GroupNameNotes.getNoteKey(AccountGroup.nameKey(g.getName()));
}
private void updateAllGroups(PersonIdent ident, GroupReference... groupRefs) throws Exception {
@@ -562,12 +578,13 @@ public class GroupNameNotesTest extends GerritBaseTests {
try (ObjectInserter inserter = repo.newObjectInserter()) {
BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
PersonIdent ident = newPersonIdent();
- try {
- GroupNameNotes.updateAllGroups(repo, inserter, bru, Arrays.asList(groupRefs), ident);
- assert_().fail("Expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- assertThat(e).hasMessageThat().isEqualTo(GroupNameNotes.UNIQUE_REF_ERROR);
- }
+ IllegalArgumentException thrown =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ GroupNameNotes.updateAllGroups(
+ repo, inserter, bru, Arrays.asList(groupRefs), ident));
+ assertThat(thrown).hasMessageThat().isEqualTo(GroupNameNotes.UNIQUE_REF_ERROR);
}
}
diff --git a/javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java b/javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java
index a5b04eebc8..9025691c58 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyCheckerTest.java
@@ -17,9 +17,9 @@ package com.google.gerrit.server.group.db;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo.warning;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.group.db.testing.GroupTestUtil;
import java.util.List;
import org.junit.Test;
@@ -30,7 +30,7 @@ public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
public void groupNamesRefIsMissing() throws Exception {
List<ConsistencyProblemInfo> problems =
GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
- allUsersRepo, new AccountGroup.NameKey("g-1"), new AccountGroup.UUID("uuid-1"));
+ allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
assertThat(problems)
.containsExactly(warning("Group with name 'g-1' doesn't exist in the list of all names"));
}
@@ -40,7 +40,7 @@ public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
updateGroupNamesRef("g-2", "[group]\n\tuuid = uuid-2\n\tname = g-2\n");
List<ConsistencyProblemInfo> problems =
GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
- allUsersRepo, new AccountGroup.NameKey("g-1"), new AccountGroup.UUID("uuid-1"));
+ allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
assertThat(problems)
.containsExactly(warning("Group with name 'g-1' doesn't exist in the list of all names"));
}
@@ -50,7 +50,7 @@ public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
updateGroupNamesRef("g-1", "[group]\n\tuuid = uuid-1\n\tname = g-1\n");
List<ConsistencyProblemInfo> problems =
GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
- allUsersRepo, new AccountGroup.NameKey("g-1"), new AccountGroup.UUID("uuid-1"));
+ allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
assertThat(problems).isEmpty();
}
@@ -59,7 +59,7 @@ public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
updateGroupNamesRef("g-1", "[group]\n\tuuid = uuid-2\n\tname = g-1\n");
List<ConsistencyProblemInfo> problems =
GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
- allUsersRepo, new AccountGroup.NameKey("g-1"), new AccountGroup.UUID("uuid-1"));
+ allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
assertThat(problems)
.containsExactly(
warning(
@@ -72,7 +72,7 @@ public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
updateGroupNamesRef("g-1", "[group]\n\tuuid = uuid-1\n\tname = g-2\n");
List<ConsistencyProblemInfo> problems =
GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
- allUsersRepo, new AccountGroup.NameKey("g-1"), new AccountGroup.UUID("uuid-1"));
+ allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
assertThat(problems)
.containsExactly(warning("group note of name 'g-1' claims to represent name of 'g-2'"));
}
@@ -82,7 +82,7 @@ public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
updateGroupNamesRef("g-1", "[group]\n\tuuid = uuid-2\n\tname = g-2\n");
List<ConsistencyProblemInfo> problems =
GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
- allUsersRepo, new AccountGroup.NameKey("g-1"), new AccountGroup.UUID("uuid-1"));
+ allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
assertThat(problems)
.containsExactly(
warning(
@@ -97,7 +97,7 @@ public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
updateGroupNamesRef("g-1", "[invalid");
List<ConsistencyProblemInfo> problems =
GroupsNoteDbConsistencyChecker.checkWithGroupNameNotes(
- allUsersRepo, new AccountGroup.NameKey("g-1"), new AccountGroup.UUID("uuid-1"));
+ allUsersRepo, AccountGroup.nameKey("g-1"), AccountGroup.uuid("uuid-1"));
assertThat(problems)
.containsExactly(
warning(
@@ -105,7 +105,7 @@ public class GroupsNoteDbConsistencyCheckerTest extends AbstractGroupTest {
}
private void updateGroupNamesRef(String groupName, String content) throws Exception {
- String nameKey = GroupNameNotes.getNoteKey(new AccountGroup.NameKey(groupName)).getName();
+ String nameKey = GroupNameNotes.getNoteKey(AccountGroup.nameKey(groupName)).getName();
GroupTestUtil.updateGroupFile(
allUsersRepo, serverIdent, RefNames.REFS_GROUPNAMES, nameKey, content);
}
diff --git a/javatests/com/google/gerrit/server/index/account/AccountFieldTest.java b/javatests/com/google/gerrit/server/index/account/AccountFieldTest.java
index c69fa200e1..92a5fbed13 100644
--- a/javatests/com/google/gerrit/server/index/account/AccountFieldTest.java
+++ b/javatests/com/google/gerrit/server/index/account/AccountFieldTest.java
@@ -21,37 +21,39 @@ import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.List;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class AccountFieldTest extends GerritBaseTests {
+public class AccountFieldTest {
@Test
public void refStateFieldValues() throws Exception {
AllUsersName allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
- Account account = new Account(new Account.Id(1), TimeUtil.nowTs());
+ Account.Builder account = Account.builder(Account.id(1), TimeUtil.nowTs());
String metaId = "0e39795bb25dc914118224995c53c5c36923a461";
account.setMetaId(metaId);
List<String> values =
- toStrings(AccountField.REF_STATE.get(AccountState.forAccount(allUsersName, account)));
+ toStrings(AccountField.REF_STATE.get(AccountState.forAccount(account.build())));
assertThat(values).hasSize(1);
String expectedValue =
- allUsersName.get() + ":" + RefNames.refsUsers(account.getId()) + ":" + metaId;
+ allUsersName.get() + ":" + RefNames.refsUsers(account.id()) + ":" + metaId;
assertThat(Iterables.getOnlyElement(values)).isEqualTo(expectedValue);
}
@Test
public void externalIdStateFieldValues() throws Exception {
- Account.Id id = new Account.Id(1);
- Account account = new Account(id, TimeUtil.nowTs());
+ Account.Id id = Account.id(1);
+ Account account =
+ Account.builder(id, TimeUtil.nowTs())
+ .setMetaId("1234567812345678123456781234567812345678")
+ .build();
ExternalId extId1 =
ExternalId.create(
ExternalId.Key.create(ExternalId.SCHEME_MAILTO, "foo.bar@example.com"),
@@ -69,7 +71,7 @@ public class AccountFieldTest extends GerritBaseTests {
List<String> values =
toStrings(
AccountField.EXTERNAL_ID_STATE.get(
- AccountState.forAccount(null, account, ImmutableSet.of(extId1, extId2))));
+ AccountState.forAccount(account, ImmutableSet.of(extId1, extId2))));
String expectedValue1 = extId1.key().sha1().name() + ":" + extId1.blobId().name();
String expectedValue2 = extId2.key().sha1().name() + ":" + extId2.blobId().name();
assertThat(values).containsExactly(expectedValue1, expectedValue2);
diff --git a/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java b/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
index 758c304822..b817b80741 100644
--- a/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
+++ b/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.index.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
@@ -23,12 +24,11 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Table;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRequirement;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.TestTimeUtil;
import java.sql.Timestamp;
import java.util.Collections;
@@ -38,7 +38,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class ChangeFieldTest extends GerritBaseTests {
+public class ChangeFieldTest {
@Before
public void setUp() {
TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
@@ -53,9 +53,9 @@ public class ChangeFieldTest extends GerritBaseTests {
public void reviewerFieldValues() {
Table<ReviewerStateInternal, Account.Id, Timestamp> t = HashBasedTable.create();
Timestamp t1 = TimeUtil.nowTs();
- t.put(ReviewerStateInternal.REVIEWER, new Account.Id(1), t1);
+ t.put(ReviewerStateInternal.REVIEWER, Account.id(1), t1);
Timestamp t2 = TimeUtil.nowTs();
- t.put(ReviewerStateInternal.CC, new Account.Id(2), t2);
+ t.put(ReviewerStateInternal.CC, Account.id(2), t2);
ReviewerSet reviewers = ReviewerSet.fromTable(t);
List<String> values = ChangeField.getReviewerFieldValues(reviewers);
@@ -63,7 +63,7 @@ public class ChangeFieldTest extends GerritBaseTests {
.containsExactly(
"REVIEWER,1", "REVIEWER,1," + t1.getTime(), "CC,2", "CC,2," + t2.getTime());
- assertThat(ChangeField.parseReviewerFieldValues(new Change.Id(1), values)).isEqualTo(reviewers);
+ assertThat(ChangeField.parseReviewerFieldValues(Change.id(1), values)).isEqualTo(reviewers);
}
@Test
@@ -75,7 +75,7 @@ public class ChangeFieldTest extends GerritBaseTests {
SubmitRecord.Status.OK,
label(SubmitRecord.Label.Status.MAY, "Label-1", null),
label(SubmitRecord.Label.Status.OK, "Label-2", 1))),
- new Account.Id(1)))
+ Account.id(1)))
.containsExactly("OK", "MAY,label-1", "OK,label-2", "OK,label-2,0", "OK,label-2,1");
}
@@ -142,7 +142,7 @@ public class ChangeFieldTest extends GerritBaseTests {
l.status = status;
l.label = label;
if (appliedBy != null) {
- l.appliedBy = new Account.Id(appliedBy);
+ l.appliedBy = Account.id(appliedBy);
}
return l;
}
@@ -153,8 +153,8 @@ public class ChangeFieldTest extends GerritBaseTests {
ChangeField.storedSubmitRecords(recordList).stream()
.map(s -> new String(s, UTF_8))
.collect(toList());
- assertThat(ChangeField.parseSubmitRecords(stored))
- .named("JSON %s" + stored)
+ assertWithMessage("JSON %s" + stored)
+ .that(ChangeField.parseSubmitRecords(stored))
.isEqualTo(recordList);
}
}
diff --git a/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java b/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
index fd23da3679..f5d3bf73a0 100644
--- a/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
+++ b/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
@@ -16,32 +16,32 @@ package com.google.gerrit.server.index.change;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.common.data.GlobalCapability.DEFAULT_MAX_QUERY_LIMIT;
+import static com.google.gerrit.entities.Change.Status.MERGED;
+import static com.google.gerrit.entities.Change.Status.NEW;
import static com.google.gerrit.index.query.Predicate.and;
import static com.google.gerrit.index.query.Predicate.or;
-import static com.google.gerrit.reviewdb.client.Change.Status.MERGED;
-import static com.google.gerrit.reviewdb.client.Change.Status.NEW;
import static com.google.gerrit.server.index.change.IndexedChangeQuery.convertOptions;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.junit.Assert.assertEquals;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.change.AndChangeSource;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeStatusPredicate;
import com.google.gerrit.server.query.change.OrSource;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
-public class ChangeIndexRewriterTest extends GerritBaseTests {
+public class ChangeIndexRewriterTest {
private static final IndexConfig CONFIG = IndexConfig.createDefault();
private FakeChangeIndex index;
@@ -196,9 +196,8 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
indexes.setSearchIndex(new FakeChangeIndex(FakeChangeIndex.V1));
- exception.expect(QueryParseException.class);
- exception.expectMessage("Unsupported index predicate: file:a");
- rewrite(in);
+ QueryParseException thrown = assertThrows(QueryParseException.class, () -> rewrite(in));
+ assertThat(thrown).hasMessageThat().contains("Unsupported index predicate: file:a");
}
@Test
@@ -207,9 +206,9 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
Predicate<ChangeData> in = parse(q);
assertEquals(query(in), rewrite(in));
- exception.expect(QueryParseException.class);
- exception.expectMessage("too many terms in query");
- rewrite(parse(q + " OR file:d"));
+ QueryParseException thrown =
+ assertThrows(QueryParseException.class, () -> rewrite(parse(q + " OR file:d")));
+ assertThat(thrown).hasMessageThat().contains("too many terms in query");
}
@Test
diff --git a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
index 34c5717f10..a23ccaba9a 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
@@ -15,23 +15,24 @@
package com.google.gerrit.server.index.change;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.ResultSet;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
import org.junit.Ignore;
@Ignore
public class FakeChangeIndex implements ChangeIndex {
- static final Schema<ChangeData> V1 = new Schema<>(1, ImmutableList.of(ChangeField.STATUS));
+ static final Schema<ChangeData> V1 = new Schema<>(1, false, ImmutableList.of(ChangeField.STATUS));
static final Schema<ChangeData> V2 =
- new Schema<>(2, ImmutableList.of(ChangeField.STATUS, ChangeField.PATH, ChangeField.UPDATED));
+ new Schema<>(
+ 2, false, ImmutableList.of(ChangeField.STATUS, ChangeField.PATH, ChangeField.UPDATED));
private static class Source implements ChangeDataSource {
private final Predicate<ChangeData> p;
diff --git a/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java b/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
index a38eabe036..c887875d6a 100644
--- a/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
+++ b/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
@@ -15,20 +15,19 @@
package com.google.gerrit.server.index.change;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.server.index.change.StalenessChecker.refsAreStale;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ListMultimap;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.index.RefState;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import java.util.stream.Stream;
import org.eclipse.jgit.junit.TestRepository;
@@ -37,14 +36,14 @@ import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
-public class StalenessCheckerTest extends GerritBaseTests {
+public class StalenessCheckerTest {
private static final String SHA1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
private static final String SHA2 = "badc0feebadc0feebadc0feebadc0feebadc0fee";
- private static final Project.NameKey P1 = new Project.NameKey("project1");
- private static final Project.NameKey P2 = new Project.NameKey("project2");
+ private static final Project.NameKey P1 = Project.nameKey("project1");
+ private static final Project.NameKey P2 = Project.nameKey("project2");
- private static final Change.Id C = new Change.Id(1234);
+ private static final Change.Id C = Change.id(1234);
private GitRepositoryManager repoManager;
private Repository r1;
@@ -83,12 +82,7 @@ public class StalenessCheckerTest extends GerritBaseTests {
}
private static void assertInvalidState(String state) {
- try {
- RefState.parseStates(byteArrays(state));
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(IllegalArgumentException.class, () -> RefState.parseStates(byteArrays(state)));
}
@Test
@@ -155,12 +149,8 @@ public class StalenessCheckerTest extends GerritBaseTests {
}
private static void assertInvalidPattern(String state) {
- try {
- StalenessChecker.parsePatterns(byteArrays(state));
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(
+ IllegalArgumentException.class, () -> StalenessChecker.parsePatterns(byteArrays(state)));
}
@Test
diff --git a/javatests/com/google/gerrit/server/ioutil/BUILD b/javatests/com/google/gerrit/server/ioutil/BUILD
index ef0224397e..ac9530f6fa 100644
--- a/javatests/com/google/gerrit/server/ioutil/BUILD
+++ b/javatests/com/google/gerrit/server/ioutil/BUILD
@@ -10,7 +10,6 @@ junit_tests(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/server/ioutil",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
"//lib/truth",
"//lib/truth:truth-java8-extension",
diff --git a/javatests/com/google/gerrit/server/ioutil/BasicSerializationTest.java b/javatests/com/google/gerrit/server/ioutil/BasicSerializationTest.java
index c4f32c73b3..fae8559c31 100644
--- a/javatests/com/google/gerrit/server/ioutil/BasicSerializationTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/BasicSerializationTest.java
@@ -23,14 +23,13 @@ import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
-public class BasicSerializationTest extends GerritBaseTests {
+public class BasicSerializationTest {
@Test
public void testReadVarInt32() throws IOException {
assertEquals(0x00000000, readVarInt32(r(b(0))));
diff --git a/javatests/com/google/gerrit/server/ioutil/ColumnFormatterTest.java b/javatests/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
index 9f5e60a3bf..fe642ba945 100644
--- a/javatests/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
@@ -14,13 +14,12 @@
package com.google.gerrit.server.ioutil;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.junit.Assert;
import org.junit.Test;
-public class ColumnFormatterTest extends GerritBaseTests {
+public class ColumnFormatterTest {
/**
* Holds an in-memory {@link java.io.PrintWriter} object and allows comparisons of its contents to
* a supplied string via an assert statement.
diff --git a/javatests/com/google/gerrit/server/ioutil/HexFormatTest.java b/javatests/com/google/gerrit/server/ioutil/HexFormatTest.java
index 40fd71f616..9bb6951501 100644
--- a/javatests/com/google/gerrit/server/ioutil/HexFormatTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/HexFormatTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.server.ioutil;
import static org.junit.Assert.assertEquals;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class HexFormatTest extends GerritBaseTests {
+public class HexFormatTest {
@Test
public void fromInt() {
diff --git a/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java b/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java
index 33b1c4f27e..048d59d00a 100644
--- a/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java
@@ -18,11 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.List;
import org.junit.Test;
-public class RegexListSearcherTest extends GerritBaseTests {
+public class RegexListSearcherTest {
private static final ImmutableList<String> EMPTY = ImmutableList.of();
@Test
@@ -58,7 +57,7 @@ public class RegexListSearcherTest extends GerritBaseTests {
}
private void assertSearchReturns(List<?> expected, String re, List<String> inputs) {
- assertThat(inputs).isOrdered();
+ assertThat(inputs).isInOrder();
assertThat(RegexListSearcher.ofStrings(re).search(inputs))
.containsExactlyElementsIn(expected)
.inOrder();
diff --git a/javatests/com/google/gerrit/server/ioutil/StringUtilTest.java b/javatests/com/google/gerrit/server/ioutil/StringUtilTest.java
index 817b317b5e..04f806d804 100644
--- a/javatests/com/google/gerrit/server/ioutil/StringUtilTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/StringUtilTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.server.ioutil;
import static org.junit.Assert.assertEquals;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class StringUtilTest extends GerritBaseTests {
+public class StringUtilTest {
/** Test the boundary condition that the first character of a string should be escaped. */
@Test
public void escapeFirstChar() {
diff --git a/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
index 463decf8cd..733d784775 100644
--- a/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
+++ b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
@@ -17,26 +17,71 @@ package com.google.gerrit.server.logging;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.truth.Expect;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.testing.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-public class LoggingContextAwareExecutorServiceTest extends GerritBaseTests {
+public class LoggingContextAwareExecutorServiceTest {
@Rule public final Expect expect = Expect.create();
+ @Inject @GerritServerConfig private Config config;
+ @Inject private DynamicSet<PerformanceLogger> performanceLoggers;
+
+ private PerformanceLogger testPerformanceLogger;
+ private RegistrationHandle performanceLoggerRegistrationHandle;
+
+ @Before
+ public void setup() {
+ Injector injector = Guice.createInjector(new InMemoryModule());
+ injector.injectMembers(this);
+
+ testPerformanceLogger =
+ new PerformanceLogger() {
+ @Override
+ public void log(String operation, long durationMs, Metadata metadata) {
+ // do nothing
+ }
+ };
+ performanceLoggerRegistrationHandle = performanceLoggers.add("gerrit", testPerformanceLogger);
+ }
+
+ @After
+ public void cleanup() {
+ performanceLoggerRegistrationHandle.remove();
+ }
+
@Test
public void loggingContextPropagationToBackgroundThread() throws Exception {
assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
assertForceLogging(false);
- try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar");
+ PerformanceLogContext performanceLogContext =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ // Create a performance log record.
+ TraceContext.newTimer("test").close();
+
SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
assertThat(tagMap.keySet()).containsExactly("foo");
assertThat(tagMap.get("foo")).containsExactly("bar");
assertForceLogging(true);
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(1);
ExecutorService executor =
new LoggingContextAwareExecutorService(Executors.newFixedThreadPool(1));
@@ -52,17 +97,32 @@ public class LoggingContextAwareExecutorServiceTest extends GerritBaseTests {
expect
.that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
.isTrue();
+ expect.that(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+ expect.that(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(1);
+
+ // Create another performance log record. We expect this to be visible in the outer
+ // thread.
+ TraceContext.newTimer("test2").close();
+ expect.that(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(2);
})
.get();
- // Verify that tags and force logging flag in the outer thread are still set.
+ // Verify that logging context values in the outer thread are still set.
tagMap = LoggingContext.getInstance().getTags().asMap();
assertThat(tagMap.keySet()).containsExactly("foo");
assertThat(tagMap.get("foo")).containsExactly("bar");
assertForceLogging(true);
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+
+ // The performance log record that was added in the inner thread is available in addition to
+ // the performance log record that was created in the outer thread.
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(2);
}
+
assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
assertForceLogging(false);
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
}
private void assertForceLogging(boolean expected) {
diff --git a/javatests/com/google/gerrit/server/logging/MetadataTest.java b/javatests/com/google/gerrit/server/logging/MetadataTest.java
new file mode 100644
index 0000000000..f9ae2c122b
--- /dev/null
+++ b/javatests/com/google/gerrit/server/logging/MetadataTest.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.logging;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class MetadataTest {
+
+ @Test
+ public void stringForLoggingOmitsEmptyOptionalValuesAndReformatsOptionalValuesThatArePresent() {
+ Metadata metadata = Metadata.builder().accountId(1000001).branchName("refs/heads/foo").build();
+ assertThat(metadata.toStringForLoggingLazy().evaluate())
+ .isEqualTo("Metadata{accountId=1000001, branchName=refs/heads/foo, pluginMetadata=[]}");
+ }
+
+ @Test
+ public void
+ stringForLoggingOmitsEmptyOptionalValuesAndReformatsOptionalValuesThatArePresentNoFieldsSet() {
+ Metadata metadata = Metadata.builder().build();
+ assertThat(metadata.toStringForLoggingLazy().evaluate())
+ .isEqualTo("Metadata{pluginMetadata=[]}");
+ }
+}
diff --git a/javatests/com/google/gerrit/server/logging/MutableTagsTest.java b/javatests/com/google/gerrit/server/logging/MutableTagsTest.java
index 113f26c3d6..f6f3b46044 100644
--- a/javatests/com/google/gerrit/server/logging/MutableTagsTest.java
+++ b/javatests/com/google/gerrit/server/logging/MutableTagsTest.java
@@ -15,19 +15,18 @@
package com.google.gerrit.server.logging;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import org.junit.Before;
import org.junit.Test;
-public class MutableTagsTest extends GerritBaseTests {
+public class MutableTagsTest {
private MutableTags tags;
@Before
@@ -167,11 +166,7 @@ public class MutableTagsTest extends GerritBaseTests {
}
private void assertNullPointerException(String expectedMessage, Runnable r) {
- try {
- r.run();
- assert_().fail("expected NullPointerException");
- } catch (NullPointerException e) {
- assertThat(e.getMessage()).isEqualTo(expectedMessage);
- }
+ NullPointerException thrown = assertThrows(NullPointerException.class, () -> r.run());
+ assertThat(thrown).hasMessageThat().isEqualTo(expectedMessage);
}
}
diff --git a/javatests/com/google/gerrit/server/logging/PerformanceLogContextTest.java b/javatests/com/google/gerrit/server/logging/PerformanceLogContextTest.java
new file mode 100644
index 0000000000..ed4325db06
--- /dev/null
+++ b/javatests/com/google/gerrit/server/logging/PerformanceLogContextTest.java
@@ -0,0 +1,382 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.logging;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Field;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer0;
+import com.google.gerrit.metrics.Timer1;
+import com.google.gerrit.metrics.Timer2;
+import com.google.gerrit.metrics.Timer3;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.testing.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PerformanceLogContextTest {
+ @Inject @GerritServerConfig private Config config;
+ @Inject private DynamicSet<PerformanceLogger> performanceLoggers;
+
+ // In this test setup this gets the DisabledMetricMaker injected. This means it doesn't record any
+ // metric, but performance log records are still created.
+ @Inject private MetricMaker metricMaker;
+
+ private TestPerformanceLogger testPerformanceLogger;
+ private RegistrationHandle performanceLoggerRegistrationHandle;
+
+ @Before
+ public void setup() {
+ Injector injector = Guice.createInjector(new InMemoryModule());
+ injector.injectMembers(this);
+
+ testPerformanceLogger = new TestPerformanceLogger();
+ performanceLoggerRegistrationHandle = performanceLoggers.add("gerrit", testPerformanceLogger);
+ }
+
+ @After
+ public void cleanup() {
+ performanceLoggerRegistrationHandle.remove();
+
+ LoggingContext.getInstance().clearPerformanceLogEntries();
+ LoggingContext.getInstance().performanceLogging(false);
+ }
+
+ @Test
+ public void traceTimersInsidePerformanceLogContextCreatePerformanceLog() {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ try (PerformanceLogContext traceContext =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+
+ TraceContext.newTimer("test1").close();
+ TraceContext.newTimer("test2", Metadata.builder().accountId(1000000).changeId(123).build())
+ .close();
+
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(2);
+ }
+
+ assertThat(testPerformanceLogger.logEntries())
+ .containsExactly(
+ PerformanceLogEntry.create("test1", Metadata.empty()),
+ PerformanceLogEntry.create(
+ "test2", Metadata.builder().accountId(1000000).changeId(123).build()))
+ .inOrder();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ }
+
+ @Test
+ public void traceTimersOutsidePerformanceLogContextDoNotCreatePerformanceLog() {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ TraceContext.newTimer("test1").close();
+ TraceContext.newTimer("test2", Metadata.builder().accountId(1000000).changeId(123).build())
+ .close();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ assertThat(testPerformanceLogger.logEntries()).isEmpty();
+ }
+
+ @Test
+ public void
+ traceTimersInsidePerformanceLogContextDoNotCreatePerformanceLogIfNoPerformanceLoggers() {
+ // Remove test performance logger so that there are no registered performance loggers.
+ performanceLoggerRegistrationHandle.remove();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ try (PerformanceLogContext traceContext =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+
+ TraceContext.newTimer("test1").close();
+ TraceContext.newTimer("test2", Metadata.builder().accountId(1000000).changeId(123).build())
+ .close();
+
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ }
+
+ assertThat(testPerformanceLogger.logEntries()).isEmpty();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ }
+
+ @Test
+ public void timerMetricsInsidePerformanceLogContextCreatePerformanceLog() {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ try (PerformanceLogContext traceContext =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+
+ Timer0 timer0 =
+ metricMaker.newTimer("test1/latency", new Description("Latency metric for testing"));
+ timer0.start().close();
+
+ Timer1<Integer> timer1 =
+ metricMaker.newTimer(
+ "test2/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("account", Metadata.Builder::accountId).build());
+ timer1.start(1000000).close();
+
+ Timer2<Integer, Integer> timer2 =
+ metricMaker.newTimer(
+ "test3/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("account", Metadata.Builder::accountId).build(),
+ Field.ofInteger("change", Metadata.Builder::changeId).build());
+ timer2.start(1000000, 123).close();
+
+ Timer3<Integer, Integer, String> timer3 =
+ metricMaker.newTimer(
+ "test4/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("account", Metadata.Builder::accountId).build(),
+ Field.ofInteger("change", Metadata.Builder::changeId).build(),
+ Field.ofString("project", Metadata.Builder::projectName).build());
+ timer3.start(1000000, 123, "foo/bar").close();
+
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(4);
+ }
+
+ assertThat(testPerformanceLogger.logEntries())
+ .containsExactly(
+ PerformanceLogEntry.create("test1/latency", Metadata.empty()),
+ PerformanceLogEntry.create(
+ "test2/latency", Metadata.builder().accountId(1000000).build()),
+ PerformanceLogEntry.create(
+ "test3/latency", Metadata.builder().accountId(1000000).changeId(123).build()),
+ PerformanceLogEntry.create(
+ "test4/latency",
+ Metadata.builder().accountId(1000000).changeId(123).projectName("foo/bar").build()))
+ .inOrder();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ }
+
+ @Test
+ public void timerMetricsInsidePerformanceLogContextCreatePerformanceLogNullValuesAllowed() {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ try (PerformanceLogContext traceContext =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+
+ Timer1<String> timer1 =
+ metricMaker.newTimer(
+ "test1/latency",
+ new Description("Latency metric for testing"),
+ Field.ofString("project", Metadata.Builder::projectName).build());
+ timer1.start(null).close();
+
+ Timer2<String, String> timer2 =
+ metricMaker.newTimer(
+ "test2/latency",
+ new Description("Latency metric for testing"),
+ Field.ofString("project", Metadata.Builder::projectName).build(),
+ Field.ofString("branch", Metadata.Builder::branchName).build());
+ timer2.start(null, null).close();
+
+ Timer3<String, String, String> timer3 =
+ metricMaker.newTimer(
+ "test3/latency",
+ new Description("Latency metric for testing"),
+ Field.ofString("project", Metadata.Builder::projectName).build(),
+ Field.ofString("branch", Metadata.Builder::branchName).build(),
+ Field.ofString("revision", Metadata.Builder::revision).build());
+ timer3.start(null, null, null).close();
+
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(3);
+ }
+
+ assertThat(testPerformanceLogger.logEntries())
+ .containsExactly(
+ PerformanceLogEntry.create("test1/latency", Metadata.empty()),
+ PerformanceLogEntry.create("test2/latency", Metadata.empty()),
+ PerformanceLogEntry.create("test3/latency", Metadata.empty()))
+ .inOrder();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ }
+
+ @Test
+ public void timerMetricsOutsidePerformanceLogContextDoNotCreatePerformanceLog() {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ Timer0 timer0 =
+ metricMaker.newTimer("test1/latency", new Description("Latency metric for testing"));
+ timer0.start().close();
+
+ Timer1<Integer> timer1 =
+ metricMaker.newTimer(
+ "test2/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("account", Metadata.Builder::accountId).build());
+ timer1.start(1000000).close();
+
+ Timer2<Integer, Integer> timer2 =
+ metricMaker.newTimer(
+ "test3/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("account", Metadata.Builder::accountId).build(),
+ Field.ofInteger("change", Metadata.Builder::changeId).build());
+ timer2.start(1000000, 123).close();
+
+ Timer3<Integer, Integer, String> timer3 =
+ metricMaker.newTimer(
+ "test4/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("account", Metadata.Builder::accountId).build(),
+ Field.ofInteger("change", Metadata.Builder::changeId).build(),
+ Field.ofString("project", Metadata.Builder::projectName).build());
+ timer3.start(1000000, 123, "value3").close();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ assertThat(testPerformanceLogger.logEntries()).isEmpty();
+ }
+
+ @Test
+ public void
+ timerMetricssInsidePerformanceLogContextDoNotCreatePerformanceLogIfNoPerformanceLoggers() {
+ // Remove test performance logger so that there are no registered performance loggers.
+ performanceLoggerRegistrationHandle.remove();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ try (PerformanceLogContext traceContext =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+
+ Timer0 timer0 =
+ metricMaker.newTimer("test1/latency", new Description("Latency metric for testing"));
+ timer0.start().close();
+
+ Timer1<Integer> timer1 =
+ metricMaker.newTimer(
+ "test2/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("accoutn", Metadata.Builder::accountId).build());
+ timer1.start(1000000).close();
+
+ Timer2<Integer, Integer> timer2 =
+ metricMaker.newTimer(
+ "test3/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("account", Metadata.Builder::accountId).build(),
+ Field.ofInteger("change", Metadata.Builder::changeId).build());
+ timer2.start(1000000, 123).close();
+
+ Timer3<Integer, Integer, String> timer3 =
+ metricMaker.newTimer(
+ "test4/latency",
+ new Description("Latency metric for testing"),
+ Field.ofInteger("account", Metadata.Builder::accountId).build(),
+ Field.ofInteger("change", Metadata.Builder::changeId).build(),
+ Field.ofString("project", Metadata.Builder::projectName).build());
+ timer3.start(1000000, 123, "foo/bar").close();
+
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ }
+
+ assertThat(testPerformanceLogger.logEntries()).isEmpty();
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ }
+
+ @Test
+ public void nestingPerformanceLogContextsIsPossible() {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+
+ try (PerformanceLogContext traceContext1 =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+
+ TraceContext.newTimer("test1").close();
+
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(1);
+
+ try (PerformanceLogContext traceContext2 =
+ new PerformanceLogContext(config, performanceLoggers)) {
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+
+ TraceContext.newTimer("test2").close();
+ TraceContext.newTimer("test3").close();
+
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(2);
+ }
+
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isTrue();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).hasSize(1);
+ }
+ assertThat(LoggingContext.getInstance().isPerformanceLogging()).isFalse();
+ assertThat(LoggingContext.getInstance().getPerformanceLogRecords()).isEmpty();
+ }
+
+ private static class TestPerformanceLogger implements PerformanceLogger {
+ private List<PerformanceLogEntry> logEntries = new ArrayList<>();
+
+ @Override
+ public void log(String operation, long durationMs, Metadata metadata) {
+ logEntries.add(PerformanceLogEntry.create(operation, metadata));
+ }
+
+ ImmutableList<PerformanceLogEntry> logEntries() {
+ return ImmutableList.copyOf(logEntries);
+ }
+ }
+
+ @AutoValue
+ abstract static class PerformanceLogEntry {
+ static PerformanceLogEntry create(String operation, Metadata metadata) {
+ return new AutoValue_PerformanceLogContextTest_PerformanceLogEntry(operation, metadata);
+ }
+
+ abstract String operation();
+
+ abstract Metadata metadata();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/logging/TraceContextTest.java b/javatests/com/google/gerrit/server/logging/TraceContextTest.java
index 19b2eeb322..13f2035aef 100644
--- a/javatests/com/google/gerrit/server/logging/TraceContextTest.java
+++ b/javatests/com/google/gerrit/server/logging/TraceContextTest.java
@@ -15,18 +15,18 @@
package com.google.gerrit.server.logging;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.server.logging.TraceContext.TraceIdConsumer;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import org.junit.After;
import org.junit.Test;
-public class TraceContextTest extends GerritBaseTests {
+public class TraceContextTest {
@After
public void cleanup() {
LoggingContext.getInstance().clearTags();
@@ -237,6 +237,22 @@ public class TraceContextTest extends GerritBaseTests {
}
}
+ @Test
+ public void operationForTraceTimerCannotBeNull() throws Exception {
+ assertThrows(NullPointerException.class, () -> TraceContext.newTimer(null));
+ assertThrows(NullPointerException.class, () -> TraceContext.newTimer(null, Metadata.empty()));
+ assertThrows(
+ NullPointerException.class,
+ () ->
+ TraceContext.newTimer(
+ null, Metadata.builder().accountId(1000000).changeId(123).build()));
+ }
+
+ @Test
+ public void metadataForTraceTimerCannotBeNull() throws Exception {
+ assertThrows(NullPointerException.class, () -> TraceContext.newTimer("test", null));
+ }
+
private void assertTags(ImmutableMap<String, ImmutableSet<String>> expectedTagMap) {
SortedMap<String, SortedSet<Object>> actualTagMap =
LoggingContext.getInstance().getTags().asMap();
diff --git a/javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java b/javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java
index f8a613aa37..9dcb08c4b6 100644
--- a/javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java
+++ b/javatests/com/google/gerrit/server/mail/AutoReplyMailFilterTest.java
@@ -18,11 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.mail.Address;
import com.google.gerrit.mail.MailMessage;
-import com.google.gerrit.testing.GerritBaseTests;
import java.time.Instant;
import org.junit.Test;
-public class AutoReplyMailFilterTest extends GerritBaseTests {
+public class AutoReplyMailFilterTest {
private AutoReplyMailFilter autoReplyMailFilter = new AutoReplyMailFilter();
diff --git a/javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java b/javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java
index 78116edf29..f4fbc7846e 100644
--- a/javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java
@@ -20,11 +20,10 @@ import static com.google.gerrit.server.mail.send.CommentFormatter.BlockType.PARA
import static com.google.gerrit.server.mail.send.CommentFormatter.BlockType.PRE_FORMATTED;
import static com.google.gerrit.server.mail.send.CommentFormatter.BlockType.QUOTE;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.List;
import org.junit.Test;
-public class CommentFormatterTest extends GerritBaseTests {
+public class CommentFormatterTest {
private void assertBlock(
List<CommentFormatter.Block> list, int index, CommentFormatter.BlockType type, String text) {
CommentFormatter.Block block = list.get(index);
diff --git a/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java b/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java
index 0682bb3fd5..78cefdf70f 100644
--- a/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java
@@ -16,11 +16,10 @@ package com.google.gerrit.server.mail.send;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Collections;
import org.junit.Test;
-public class CommentSenderTest extends GerritBaseTests {
+public class CommentSenderTest {
private static class TestSender extends CommentSender {
TestSender() {
super(null, null, null, null, null);
diff --git a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
index 537ebff86b..74f44a1258 100644
--- a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
@@ -15,20 +15,17 @@
package com.google.gerrit.server.mail.send;
import static com.google.common.truth.Truth.assertThat;
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@@ -37,7 +34,7 @@ import org.eclipse.jgit.lib.PersonIdent;
import org.junit.Before;
import org.junit.Test;
-public class FromAddressGeneratorProviderTest extends GerritBaseTests {
+public class FromAddressGeneratorProviderTest {
private Config config;
private PersonIdent ident;
private AccountCache accountCache;
@@ -46,7 +43,7 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
public void setUp() throws Exception {
config = new Config();
ident = new PersonIdent("NAME", "e@email", 0, 0);
- accountCache = createStrictMock(AccountCache.class);
+ accountCache = mock(AccountCache.class);
}
private FromAddressGenerator create() {
@@ -86,12 +83,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.example.com";
final Account.Id user = user(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name);
assertThat(r.getEmail()).isEqualTo(email);
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -101,12 +97,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.example.com";
final Account.Id user = user(null, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isNull();
assertThat(r.getEmail()).isEqualTo(email);
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -116,23 +111,21 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String name = "A U. Thor";
final Account.Id user = user(name, null);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name + " (Code Review)");
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
public void USER_NullUser() {
setFrom("USER");
- replay(accountCache);
final Address r = create().from(null);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(ident.getName());
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyZeroInteractions(accountCache);
}
@Test
@@ -143,12 +136,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.example.com";
final Account.Id user = user(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name);
assertThat(r.getEmail()).isEqualTo(email);
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -159,12 +151,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.com";
final Account.Id user = user(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name + " (Code Review)");
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -176,12 +167,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.com";
final Account.Id user = user(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name);
assertThat(r.getEmail()).isEqualTo(email);
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -193,12 +183,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.com";
final Account.Id user = user(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name + " (Code Review)");
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -209,12 +198,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.com";
final Account.Id user = user(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name);
assertThat(r.getEmail()).isEqualTo(email);
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -237,23 +225,21 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.example.com";
final Account.Id user = userNoLookup(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(ident.getName());
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyZeroInteractions(accountCache);
}
@Test
public void SERVER_NullUser() {
setFrom("SERVER");
- replay(accountCache);
final Address r = create().from(null);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(ident.getName());
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyZeroInteractions(accountCache);
}
@Test
@@ -276,12 +262,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.example.com";
final Account.Id user = user(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name + " (Code Review)");
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -291,12 +276,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.example.com";
final Account.Id user = user(null, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo("Anonymous Coward (Code Review)");
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -306,23 +290,21 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String name = "A U. Thor";
final Account.Id user = user(name, null);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(name + " (Code Review)");
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
public void MIXED_NullUser() {
setFrom("MIXED");
- replay(accountCache);
final Address r = create().from(null);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(ident.getName());
assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress());
- verify(accountCache);
+ verifyZeroInteractions(accountCache);
}
@Test
@@ -333,12 +315,11 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.example.com";
final Account.Id user = user(name, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo("A " + name + " B");
assertThat(r.getEmail()).isEqualTo("my.server@email.address");
- verify(accountCache);
+ verifyAccountCacheGet(user);
}
@Test
@@ -348,42 +329,42 @@ public class FromAddressGeneratorProviderTest extends GerritBaseTests {
final String email = "a.u.thor@test.example.com";
final Account.Id user = user(null, email);
- replay(accountCache);
final Address r = create().from(user);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo("A Anonymous Coward B");
assertThat(r.getEmail()).isEqualTo("my.server@email.address");
- verify(accountCache);
}
@Test
public void CUSTOM_NullUser() {
setFrom("A ${user} B <my.server@email.address>");
- replay(accountCache);
final Address r = create().from(null);
assertThat(r).isNotNull();
assertThat(r.getName()).isEqualTo(ident.getName());
assertThat(r.getEmail()).isEqualTo("my.server@email.address");
- verify(accountCache);
}
private Account.Id user(String name, String email) {
final AccountState s = makeUser(name, email);
- expect(accountCache.get(eq(s.getAccount().getId()))).andReturn(Optional.of(s));
- return s.getAccount().getId();
+ when(accountCache.get(eq(s.account().id()))).thenReturn(Optional.of(s));
+ return s.account().id();
+ }
+
+ private void verifyAccountCacheGet(Account.Id id) {
+ verify(accountCache).get(eq(id));
}
private Account.Id userNoLookup(String name, String email) {
final AccountState s = makeUser(name, email);
- return s.getAccount().getId();
+ return s.account().id();
}
private AccountState makeUser(String name, String email) {
- final Account.Id userId = new Account.Id(42);
- final Account account = new Account(userId, TimeUtil.nowTs());
+ final Account.Id userId = Account.id(42);
+ final Account.Builder account = Account.builder(userId, TimeUtil.nowTs());
account.setFullName(name);
account.setPreferredEmail(email);
- return AccountState.forAccount(new AllUsersName(AllUsersNameProvider.DEFAULT), account);
+ return AccountState.forAccount(account.build());
}
}
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index a1f318f8a5..7192c55f23 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -19,16 +19,17 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.CommentRange;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.CommentRange;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
@@ -51,9 +52,9 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.gerrit.testing.AssertableExecutorService;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.FakeAccountCache;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.TestChanges;
import com.google.gerrit.testing.TestTimeUtil;
@@ -63,9 +64,11 @@ import com.google.inject.Injector;
import com.google.inject.util.Providers;
import java.sql.Timestamp;
import java.util.TimeZone;
+import java.util.concurrent.ExecutorService;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.After;
@@ -75,7 +78,7 @@ import org.junit.runner.RunWith;
@Ignore
@RunWith(ConfigSuite.class)
-public abstract class AbstractChangeNotesTest extends GerritBaseTests {
+public abstract class AbstractChangeNotesTest {
private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
@ConfigSuite.Parameter public Config testConfig;
@@ -91,6 +94,7 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
protected Project.NameKey project;
protected RevWalk rw;
protected TestRepository<InMemoryRepository> tr;
+ protected AssertableExecutorService assertableFanOutExecutor;
@Inject protected IdentifiedUser.GenericFactory userFactory;
@@ -110,20 +114,21 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
setTimeForTesting();
serverIdent = new PersonIdent("Gerrit Server", "noreply@gerrit.com", TimeUtil.nowTs(), TZ);
- project = new Project.NameKey("test-project");
+ project = Project.nameKey("test-project");
repoManager = new InMemoryRepositoryManager();
repo = repoManager.createRepository(project);
tr = new TestRepository<>(repo);
rw = tr.getRevWalk();
accountCache = new FakeAccountCache();
- Account co = new Account(new Account.Id(1), TimeUtil.nowTs());
+ Account.Builder co = Account.builder(Account.id(1), TimeUtil.nowTs());
co.setFullName("Change Owner");
co.setPreferredEmail("change@owner.com");
- accountCache.put(co);
- Account ou = new Account(new Account.Id(2), TimeUtil.nowTs());
+ accountCache.put(co.build());
+ Account.Builder ou = Account.builder(Account.id(2), TimeUtil.nowTs());
ou.setFullName("Other Account");
ou.setPreferredEmail("other@account.com");
- accountCache.put(ou);
+ accountCache.put(ou.build());
+ assertableFanOutExecutor = new AssertableExecutorService();
injector =
Guice.createInjector(
@@ -156,13 +161,16 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
.toInstance(serverIdent);
bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
bind(MetricMaker.class).to(DisabledMetricMaker.class);
+ bind(ExecutorService.class)
+ .annotatedWith(FanOutExecutor.class)
+ .toInstance(assertableFanOutExecutor);
}
});
injector.injectMembers(this);
repoManager.createRepository(allUsers);
- changeOwner = userFactory.create(co.getId());
- otherUser = userFactory.create(ou.getId());
+ changeOwner = userFactory.create(co.id());
+ otherUser = userFactory.create(ou.id());
otherUserId = otherUser.getAccountId();
internalUser = new InternalUser();
}
@@ -182,7 +190,7 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
Change c = TestChanges.newChange(project, changeOwner.getAccountId());
ChangeUpdate u = newUpdateForNewChange(c, changeOwner);
u.setChangeId(c.getKey().get());
- u.setBranch(c.getDest().get());
+ u.setBranch(c.getDest().branch());
u.setWorkInProgress(workInProgress);
u.commit();
return c;
@@ -247,7 +255,7 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
Timestamp t,
String message,
short side,
- String commitSHA1,
+ ObjectId commitId,
boolean unresolved) {
Comment c =
new Comment(
@@ -260,7 +268,7 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
unresolved);
c.lineNbr = line;
c.parentUuid = parentUUID;
- c.revId = commitSHA1;
+ c.setCommitId(commitId);
c.setRange(range);
return c;
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java
index b4d9738a51..9112756cef 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java
@@ -20,8 +20,8 @@ import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatS
import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesKeyProto;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
@@ -31,8 +31,8 @@ public final class ChangeNotesCacheTest {
public void keySerializer() throws Exception {
ChangeNotesCache.Key key =
ChangeNotesCache.Key.create(
- new Project.NameKey("project"),
- new Change.Id(1234),
+ Project.nameKey("project"),
+ Change.id(1234),
ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
byte[] serialized = ChangeNotesCache.Key.Serializer.INSTANCE.serialize(key);
assertThat(ChangeNotesKeyProto.parseFrom(serialized))
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
index 79dcd5bed3..013939a400 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesParserTest.java
@@ -15,9 +15,9 @@
package com.google.gerrit.server.notedb;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
import com.google.gerrit.server.util.time.TimeUtil;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -214,6 +214,26 @@ public class ChangeNotesParserTest extends AbstractChangeNotesTest {
}
@Test
+ public void parseAssignee() throws Exception {
+ assertParseSucceeds(
+ "Update change\n"
+ + "\n"
+ + "Branch: refs/heads/master\n"
+ + "Change-id: I577fb248e474018276351785930358ec0450e9f7\n"
+ + "Patch-set: 1\n"
+ + "Assignee: Change Owner <1@gerrit>\n"
+ + "Subject: This is a test change\n");
+ assertParseSucceeds(
+ "Update change\n"
+ + "\n"
+ + "Branch: refs/heads/master\n"
+ + "Change-id: I577fb248e474018276351785930358ec0450e9f7\n"
+ + "Patch-set: 2\n"
+ + "Assignee:\n"
+ + "Subject: This is a test change\n");
+ }
+
+ @Test
public void parseTopic() throws Exception {
assertParseSucceeds(
"Update change\n"
@@ -545,19 +565,12 @@ public class ChangeNotesParserTest extends AbstractChangeNotesTest {
}
private void assertParseFails(RevCommit commit) throws Exception {
- try {
- newParser(commit).parseAll();
- fail("Expected parse to fail:\n" + commit.getFullMessage());
- } catch (ConfigInvalidException e) {
- // Expected
- }
+ assertThrows(ConfigInvalidException.class, () -> newParser(commit).parseAll());
}
private ChangeNotesParser newParser(ObjectId tip) throws Exception {
walk.reset();
ChangeNoteJson changeNoteJson = injector.getInstance(ChangeNoteJson.class);
- LegacyChangeNoteRead reader = injector.getInstance(LegacyChangeNoteRead.class);
- return new ChangeNotesParser(
- newChange().getId(), tip, walk, changeNoteJson, reader, args.metrics);
+ return new ChangeNotesParser(newChange().getId(), tip, walk, changeNoteJson, args.metrics);
}
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index 2931b1706c..6ece894645 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -27,22 +27,23 @@ import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
+import com.google.gerrit.entities.converter.ChangeMessageProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
+import com.google.gerrit.entities.converter.PatchSetProtoConverter;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.converter.ChangeMessageProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetApprovalProtoConverter;
-import com.google.gerrit.reviewdb.converter.PatchSetProtoConverter;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
+import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.AssigneeStatusUpdateProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ChangeColumnsProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
@@ -50,23 +51,25 @@ import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.Reviewer
import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import com.google.gerrit.server.notedb.ChangeNotesState.ChangeColumns;
import com.google.gerrit.server.notedb.ChangeNotesState.Serializer;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.TypeLiteral;
import com.google.protobuf.ByteString;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Before;
import org.junit.Test;
-public class ChangeNotesStateTest extends GerritBaseTests {
- private static final Change.Id ID = new Change.Id(123);
+public class ChangeNotesStateTest {
+ private static final Change.Id ID = Change.id(123);
private static final ObjectId SHA =
ObjectId.fromString("1234567812345678123456781234567812345678");
private static final ByteString SHA_BYTES = ObjectIdConverter.create().toByteString(SHA);
private static final String CHANGE_KEY = "Iabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
+ private static final String DEFAULT_SERVER_ID = UUID.randomUUID().toString();
private ChangeColumns cols;
private ChangeColumnsProto colsProto;
@@ -75,10 +78,10 @@ public class ChangeNotesStateTest extends GerritBaseTests {
public void setUp() throws Exception {
cols =
ChangeColumns.builder()
- .changeKey(new Change.Key(CHANGE_KEY))
+ .changeKey(Change.key(CHANGE_KEY))
.createdOn(new Timestamp(123456L))
.lastUpdatedOn(new Timestamp(234567L))
- .owner(new Account.Id(1000))
+ .owner(Account.id(1000))
.branch("refs/heads/master")
.subject("Test change")
.isPrivate(false)
@@ -98,7 +101,7 @@ public class ChangeNotesStateTest extends GerritBaseTests {
newBuilder()
.columns(
cols.toBuilder()
- .changeKey(new Change.Key("Ieeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"))
+ .changeKey(Change.key("Ieeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"))
.build())
.build(),
ChangeNotesStateProto.newBuilder()
@@ -134,7 +137,7 @@ public class ChangeNotesStateTest extends GerritBaseTests {
@Test
public void serializeOwner() throws Exception {
assertRoundTrip(
- newBuilder().columns(cols.toBuilder().owner(new Account.Id(7777)).build()).build(),
+ newBuilder().columns(cols.toBuilder().owner(Account.id(7777)).build()).build(),
ChangeNotesStateProto.newBuilder()
.setMetaId(SHA_BYTES)
.setChangeId(ID.get())
@@ -168,7 +171,7 @@ public class ChangeNotesStateTest extends GerritBaseTests {
public void serializeCurrentPatchSetId() throws Exception {
assertRoundTrip(
newBuilder()
- .columns(cols.toBuilder().currentPatchSetId(new PatchSet.Id(ID, 2)).build())
+ .columns(cols.toBuilder().currentPatchSetId(PatchSet.id(ID, 2)).build())
.build(),
ChangeNotesStateProto.newBuilder()
.setMetaId(SHA_BYTES)
@@ -241,17 +244,6 @@ public class ChangeNotesStateTest extends GerritBaseTests {
}
@Test
- public void serializeAssignee() throws Exception {
- assertRoundTrip(
- newBuilder().columns(cols.toBuilder().assignee(new Account.Id(2000)).build()).build(),
- ChangeNotesStateProto.newBuilder()
- .setMetaId(SHA_BYTES)
- .setChangeId(ID.get())
- .setColumns(colsProto.toBuilder().setAssignee(2000).setHasAssignee(true))
- .build());
- }
-
- @Test
public void serializeStatus() throws Exception {
assertRoundTrip(
newBuilder().columns(cols.toBuilder().status(Change.Status.MERGED).build()).build(),
@@ -298,7 +290,7 @@ public class ChangeNotesStateTest extends GerritBaseTests {
@Test
public void serializeRevertOf() throws Exception {
assertRoundTrip(
- newBuilder().columns(cols.toBuilder().revertOf(new Change.Id(999)).build()).build(),
+ newBuilder().columns(cols.toBuilder().revertOf(Change.id(999)).build()).build(),
ChangeNotesStateProto.newBuilder()
.setMetaId(SHA_BYTES)
.setChangeId(ID.get())
@@ -307,21 +299,6 @@ public class ChangeNotesStateTest extends GerritBaseTests {
}
@Test
- public void serializePastAssignees() throws Exception {
- assertRoundTrip(
- newBuilder()
- .pastAssignees(ImmutableSet.of(new Account.Id(2002), new Account.Id(2001)))
- .build(),
- ChangeNotesStateProto.newBuilder()
- .setMetaId(SHA_BYTES)
- .setChangeId(ID.get())
- .setColumns(colsProto)
- .addPastAssignee(2002)
- .addPastAssignee(2001)
- .build());
- }
-
- @Test
public void serializeHashtags() throws Exception {
assertRoundTrip(
newBuilder().hashtags(ImmutableSet.of("tag2", "tag1")).build(),
@@ -336,25 +313,29 @@ public class ChangeNotesStateTest extends GerritBaseTests {
@Test
public void serializePatchSets() throws Exception {
- PatchSet ps1 = new PatchSet(new PatchSet.Id(ID, 1));
- ps1.setUploader(new Account.Id(2000));
- ps1.setRevision(new RevId("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
- ps1.setCreatedOn(cols.createdOn());
+ PatchSet ps1 =
+ PatchSet.builder()
+ .id(PatchSet.id(ID, 1))
+ .commitId(ObjectId.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
+ .uploader(Account.id(2000))
+ .createdOn(cols.createdOn())
+ .build();
ByteString ps1Bytes = toByteString(ps1, PatchSetProtoConverter.INSTANCE);
assertThat(ps1Bytes.size()).isEqualTo(66);
- PatchSet ps2 = new PatchSet(new PatchSet.Id(ID, 2));
- ps2.setUploader(new Account.Id(3000));
- ps2.setRevision(new RevId("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
- ps2.setCreatedOn(cols.lastUpdatedOn());
+ PatchSet ps2 =
+ PatchSet.builder()
+ .id(PatchSet.id(ID, 2))
+ .commitId(ObjectId.fromString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"))
+ .uploader(Account.id(3000))
+ .createdOn(cols.lastUpdatedOn())
+ .build();
ByteString ps2Bytes = toByteString(ps2, PatchSetProtoConverter.INSTANCE);
assertThat(ps2Bytes.size()).isEqualTo(66);
assertThat(ps2Bytes).isNotEqualTo(ps1Bytes);
assertRoundTrip(
- newBuilder()
- .patchSets(ImmutableMap.of(ps2.getId(), ps2, ps1.getId(), ps1).entrySet())
- .build(),
+ newBuilder().patchSets(ImmutableMap.of(ps2.id(), ps2, ps1.id(), ps1).entrySet()).build(),
ChangeNotesStateProto.newBuilder()
.setMetaId(SHA_BYTES)
.setChangeId(ID.get())
@@ -367,28 +348,31 @@ public class ChangeNotesStateTest extends GerritBaseTests {
@Test
public void serializeApprovals() throws Exception {
PatchSetApproval a1 =
- new PatchSetApproval(
- new PatchSetApproval.Key(
- new PatchSet.Id(ID, 1), new Account.Id(2001), new LabelId("Code-Review")),
- (short) 1,
- new Timestamp(1212L));
+ PatchSetApproval.builder()
+ .key(
+ PatchSetApproval.key(
+ PatchSet.id(ID, 1), Account.id(2001), LabelId.create("Code-Review")))
+ .value(1)
+ .granted(new Timestamp(1212L))
+ .build();
ByteString a1Bytes = toByteString(a1, PatchSetApprovalProtoConverter.INSTANCE);
assertThat(a1Bytes.size()).isEqualTo(43);
PatchSetApproval a2 =
- new PatchSetApproval(
- new PatchSetApproval.Key(
- new PatchSet.Id(ID, 1), new Account.Id(2002), new LabelId("Verified")),
- (short) -1,
- new Timestamp(3434L));
+ PatchSetApproval.builder()
+ .key(
+ PatchSetApproval.key(
+ PatchSet.id(ID, 1), Account.id(2002), LabelId.create("Verified")))
+ .value(-1)
+ .granted(new Timestamp(3434L))
+ .build();
ByteString a2Bytes = toByteString(a2, PatchSetApprovalProtoConverter.INSTANCE);
assertThat(a2Bytes.size()).isEqualTo(49);
assertThat(a2Bytes).isNotEqualTo(a1Bytes);
assertRoundTrip(
newBuilder()
- .approvals(
- ImmutableListMultimap.of(a2.getPatchSetId(), a2, a1.getPatchSetId(), a1).entries())
+ .approvals(ImmutableListMultimap.of(a2.patchSetId(), a2, a1.patchSetId(), a1).entries())
.build(),
ChangeNotesStateProto.newBuilder()
.setMetaId(SHA_BYTES)
@@ -406,11 +390,8 @@ public class ChangeNotesStateTest extends GerritBaseTests {
.reviewers(
ReviewerSet.fromTable(
ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>builder()
- .put(ReviewerStateInternal.CC, new Account.Id(2001), new Timestamp(1212L))
- .put(
- ReviewerStateInternal.REVIEWER,
- new Account.Id(2002),
- new Timestamp(3434L))
+ .put(ReviewerStateInternal.CC, Account.id(2001), new Timestamp(1212L))
+ .put(ReviewerStateInternal.REVIEWER, Account.id(2002), new Timestamp(3434L))
.build()))
.build(),
ChangeNotesStateProto.newBuilder()
@@ -503,11 +484,8 @@ public class ChangeNotesStateTest extends GerritBaseTests {
.pendingReviewers(
ReviewerSet.fromTable(
ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>builder()
- .put(ReviewerStateInternal.CC, new Account.Id(2001), new Timestamp(1212L))
- .put(
- ReviewerStateInternal.REVIEWER,
- new Account.Id(2002),
- new Timestamp(3434L))
+ .put(ReviewerStateInternal.CC, Account.id(2001), new Timestamp(1212L))
+ .put(ReviewerStateInternal.REVIEWER, Account.id(2002), new Timestamp(3434L))
.build()))
.build(),
ChangeNotesStateProto.newBuilder()
@@ -564,9 +542,7 @@ public class ChangeNotesStateTest extends GerritBaseTests {
@Test
public void serializeAllPastReviewers() throws Exception {
assertRoundTrip(
- newBuilder()
- .allPastReviewers(ImmutableList.of(new Account.Id(2002), new Account.Id(2001)))
- .build(),
+ newBuilder().allPastReviewers(ImmutableList.of(Account.id(2002), Account.id(2001))).build(),
ChangeNotesStateProto.newBuilder()
.setMetaId(SHA_BYTES)
.setChangeId(ID.get())
@@ -584,13 +560,13 @@ public class ChangeNotesStateTest extends GerritBaseTests {
ImmutableList.of(
ReviewerStatusUpdate.create(
new Timestamp(1212L),
- new Account.Id(1000),
- new Account.Id(2002),
+ Account.id(1000),
+ Account.id(2002),
ReviewerStateInternal.CC),
ReviewerStatusUpdate.create(
new Timestamp(3434L),
- new Account.Id(1000),
- new Account.Id(2001),
+ Account.id(1000),
+ Account.id(2001),
ReviewerStateInternal.REVIEWER)))
.build(),
ChangeNotesStateProto.newBuilder()
@@ -613,6 +589,35 @@ public class ChangeNotesStateTest extends GerritBaseTests {
}
@Test
+ public void serializeAssigneeUpdates() throws Exception {
+ assertRoundTrip(
+ newBuilder()
+ .assigneeUpdates(
+ ImmutableList.of(
+ AssigneeStatusUpdate.create(
+ new Timestamp(1212L), Account.id(1000), Optional.of(Account.id(2001))),
+ AssigneeStatusUpdate.create(
+ new Timestamp(3434L), Account.id(1000), Optional.empty())))
+ .build(),
+ ChangeNotesStateProto.newBuilder()
+ .setMetaId(SHA_BYTES)
+ .setChangeId(ID.get())
+ .setColumns(colsProto)
+ .addAssigneeUpdate(
+ AssigneeStatusUpdateProto.newBuilder()
+ .setDate(1212L)
+ .setUpdatedBy(1000)
+ .setCurrentAssignee(2001)
+ .setHasCurrentAssignee(true))
+ .addAssigneeUpdate(
+ AssigneeStatusUpdateProto.newBuilder()
+ .setDate(3434L)
+ .setUpdatedBy(1000)
+ .setHasCurrentAssignee(false))
+ .build());
+ }
+
+ @Test
public void serializeSubmitRecords() throws Exception {
SubmitRecord sr1 = new SubmitRecord();
sr1.status = SubmitRecord.Status.OK;
@@ -635,19 +640,19 @@ public class ChangeNotesStateTest extends GerritBaseTests {
public void serializeChangeMessages() throws Exception {
ChangeMessage m1 =
new ChangeMessage(
- new ChangeMessage.Key(ID, "uuid1"),
- new Account.Id(1000),
+ ChangeMessage.key(ID, "uuid1"),
+ Account.id(1000),
new Timestamp(1212L),
- new PatchSet.Id(ID, 1));
+ PatchSet.id(ID, 1));
ByteString m1Bytes = toByteString(m1, ChangeMessageProtoConverter.INSTANCE);
assertThat(m1Bytes.size()).isEqualTo(35);
ChangeMessage m2 =
new ChangeMessage(
- new ChangeMessage.Key(ID, "uuid2"),
- new Account.Id(2000),
+ ChangeMessage.key(ID, "uuid2"),
+ Account.id(2000),
new Timestamp(3434L),
- new PatchSet.Id(ID, 2));
+ PatchSet.id(ID, 2));
ByteString m2Bytes = toByteString(m2, ChangeMessageProtoConverter.INSTANCE);
assertThat(m2Bytes.size()).isEqualTo(35);
assertThat(m2Bytes).isNotEqualTo(m1Bytes);
@@ -668,31 +673,30 @@ public class ChangeNotesStateTest extends GerritBaseTests {
Comment c1 =
new Comment(
new Comment.Key("uuid1", "file1", 1),
- new Account.Id(1001),
+ Account.id(1001),
new Timestamp(1212L),
(short) 1,
"message 1",
"serverId",
false);
- c1.setRevId(new RevId("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
+ c1.setCommitId(ObjectId.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
String c1Json = Serializer.GSON.toJson(c1);
Comment c2 =
new Comment(
new Comment.Key("uuid2", "file2", 2),
- new Account.Id(1002),
+ Account.id(1002),
new Timestamp(3434L),
(short) 2,
"message 2",
"serverId",
true);
- c2.setRevId(new RevId("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
+ c2.setCommitId(ObjectId.fromString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
String c2Json = Serializer.GSON.toJson(c2);
assertRoundTrip(
newBuilder()
- .publishedComments(
- ImmutableListMultimap.of(new RevId(c2.revId), c2, new RevId(c1.revId), c1))
+ .publishedComments(ImmutableListMultimap.of(c2.getCommitId(), c2, c1.getCommitId(), c1))
.build(),
ChangeNotesStateProto.newBuilder()
.setMetaId(SHA_BYTES)
@@ -704,14 +708,26 @@ public class ChangeNotesStateTest extends GerritBaseTests {
}
@Test
+ public void serializeUpdateCount() throws Exception {
+ assertRoundTrip(
+ newBuilder().updateCount(234).build(),
+ ChangeNotesStateProto.newBuilder()
+ .setMetaId(SHA_BYTES)
+ .setChangeId(ID.get())
+ .setColumns(colsProto)
+ .setUpdateCount(234)
+ .build());
+ }
+
+ @Test
public void changeNotesStateMethods() throws Exception {
assertThatSerializedClass(ChangeNotesState.class)
.hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("metaId", ObjectId.class)
.put("changeId", Change.Id.class)
+ .put("serverId", String.class)
.put("columns", ChangeColumns.class)
- .put("pastAssignees", new TypeLiteral<ImmutableSet<Account.Id>>() {}.getType())
.put("hashtags", new TypeLiteral<ImmutableSet<String>>() {}.getType())
.put(
"patchSets",
@@ -728,11 +744,15 @@ public class ChangeNotesStateTest extends GerritBaseTests {
.put(
"reviewerUpdates",
new TypeLiteral<ImmutableList<ReviewerStatusUpdate>>() {}.getType())
+ .put(
+ "assigneeUpdates",
+ new TypeLiteral<ImmutableList<AssigneeStatusUpdate>>() {}.getType())
.put("submitRecords", new TypeLiteral<ImmutableList<SubmitRecord>>() {}.getType())
.put("changeMessages", new TypeLiteral<ImmutableList<ChangeMessage>>() {}.getType())
.put(
"publishedComments",
- new TypeLiteral<ImmutableListMultimap<RevId, Comment>>() {}.getType())
+ new TypeLiteral<ImmutableListMultimap<ObjectId, Comment>>() {}.getType())
+ .put("updateCount", int.class)
.build());
}
@@ -751,7 +771,6 @@ public class ChangeNotesStateTest extends GerritBaseTests {
.put("topic", String.class)
.put("originalSubject", String.class)
.put("submissionId", String.class)
- .put("assignee", Account.Id.class)
.put("status", Change.Status.class)
.put("isPrivate", boolean.class)
.put("workInProgress", boolean.class)
@@ -764,36 +783,37 @@ public class ChangeNotesStateTest extends GerritBaseTests {
@Test
public void patchSetFields() throws Exception {
assertThatSerializedClass(PatchSet.class)
- .hasFields(
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("id", PatchSet.Id.class)
- .put("revision", RevId.class)
+ .put("commitId", ObjectId.class)
.put("uploader", Account.Id.class)
.put("createdOn", Timestamp.class)
- .put("groups", String.class)
- .put("pushCertificate", String.class)
- .put("description", String.class)
+ .put("groups", new TypeLiteral<ImmutableList<String>>() {}.getType())
+ .put("pushCertificate", new TypeLiteral<Optional<String>>() {}.getType())
+ .put("description", new TypeLiteral<Optional<String>>() {}.getType())
.build());
}
@Test
public void patchSetApprovalFields() throws Exception {
assertThatSerializedClass(PatchSetApproval.Key.class)
- .hasFields(
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("patchSetId", PatchSet.Id.class)
.put("accountId", Account.Id.class)
- .put("categoryId", LabelId.class)
+ .put("labelId", LabelId.class)
.build());
assertThatSerializedClass(PatchSetApproval.class)
- .hasFields(
+ .hasAutoValueMethods(
ImmutableMap.<String, Type>builder()
.put("key", PatchSetApproval.Key.class)
.put("value", short.class)
.put("granted", Timestamp.class)
- .put("tag", String.class)
+ .put("tag", new TypeLiteral<Optional<String>>() {}.getType())
.put("realAccountId", Account.Id.class)
.put("postSubmit", boolean.class)
+ .put("toBuilder", PatchSetApproval.Builder.class)
.build());
}
@@ -832,6 +852,19 @@ public class ChangeNotesStateTest extends GerritBaseTests {
}
@Test
+ public void assigneeStatusUpdateMethods() throws Exception {
+ assertThatSerializedClass(AssigneeStatusUpdate.class)
+ .hasAutoValueMethods(
+ ImmutableMap.of(
+ "date",
+ Timestamp.class,
+ "updatedBy",
+ Account.Id.class,
+ "currentAssignee",
+ new TypeLiteral<Optional<Account.Id>>() {}.getType()));
+ }
+
+ @Test
public void submitRecordFields() throws Exception {
assertThatSerializedClass(SubmitRecord.class)
.hasFields(
@@ -861,7 +894,7 @@ public class ChangeNotesStateTest extends GerritBaseTests {
@Test
public void changeMessageFields() throws Exception {
assertThatSerializedClass(ChangeMessage.Key.class)
- .hasFields(ImmutableMap.of("changeId", Change.Id.class, "uuid", String.class));
+ .hasAutoValueMethods(ImmutableMap.of("changeId", Change.Id.class, "uuid", String.class));
assertThatSerializedClass(ChangeMessage.class)
.hasFields(
ImmutableMap.<String, Type>builder()
@@ -905,10 +938,22 @@ public class ChangeNotesStateTest extends GerritBaseTests {
.put("revId", String.class)
.put("serverId", String.class)
.put("unresolved", boolean.class)
- .put("legacyFormat", boolean.class)
.build());
}
+ @Test
+ public void serializeServerId() throws Exception {
+ assertRoundTrip(
+ newBuilder().serverId(DEFAULT_SERVER_ID).build(),
+ ChangeNotesStateProto.newBuilder()
+ .setMetaId(SHA_BYTES)
+ .setChangeId(ID.get())
+ .setServerId(DEFAULT_SERVER_ID)
+ .setHasServerId(true)
+ .setColumns(colsProto.toBuilder())
+ .build());
+ }
+
private static ChangeNotesStateProto toProto(ChangeNotesState state) throws Exception {
return ChangeNotesStateProto.parseFrom(Serializer.INSTANCE.serialize(state));
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index a6c0224465..145e914e7e 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -16,15 +16,17 @@ package com.google.gerrit.server.notedb;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
-import static com.google.gerrit.reviewdb.client.RefNames.refsDraftComments;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
+import static com.google.gerrit.entities.RefNames.refsDraftComments;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REMOVED;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Comparator.comparing;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-import static org.junit.Assert.fail;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
@@ -35,18 +37,17 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.ChangeMessage;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.CommentRange;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.CommentRange;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.server.AssigneeStatusUpdate;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ReviewerSet;
@@ -73,7 +74,6 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
@Inject private DraftCommentNotes.Factory draftNotesFactory;
@Inject private ChangeNoteJson changeNoteJson;
- @Inject private LegacyChangeNoteRead legacyChangeNoteRead;
@Inject private @GerritServerId String serverId;
@@ -101,7 +101,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.commit();
ChangeNotes notes = newNotes(c);
- assertThat(notes.getCurrentPatchSet().getDescription()).isEqualTo(description);
+ assertThat(notes.getCurrentPatchSet().description()).hasValue(description);
description = "new, now more descriptive!";
update = newUpdate(c, changeOwner);
@@ -109,7 +109,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.commit();
notes = newNotes(c);
- assertThat(notes.getCurrentPatchSet().getDescription()).isEqualTo(description);
+ assertThat(notes.getCurrentPatchSet().description()).hasValue(description);
}
@Test
@@ -119,7 +119,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
RevCommit commit = tr.commit().message("PS2").create();
ChangeUpdate update = newUpdate(c, changeOwner);
update.putComment(
- Status.PUBLISHED,
+ Comment.Status.PUBLISHED,
newComment(
c.currentPatchSetId(),
"a.txt",
@@ -131,14 +131,14 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
TimeUtil.nowTs(),
"Comment",
(short) 1,
- commit.name(),
+ commit,
false));
update.setTag(tag);
update.commit();
ChangeNotes notes = newNotes(c);
- ImmutableListMultimap<RevId, Comment> comments = notes.getComments();
+ ImmutableListMultimap<ObjectId, Comment> comments = notes.getComments();
assertThat(comments).hasSize(1);
assertThat(comments.entries().asList().get(0).getValue().tag).isEqualTo(tag);
}
@@ -162,7 +162,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals = notes.getApprovals();
assertThat(approvals).hasSize(1);
- assertThat(approvals.entries().asList().get(0).getValue().getTag()).isEqualTo(tag2);
+ assertThat(approvals.entries().asList().get(0).getValue().tag()).hasValue(tag2);
}
@Test
@@ -181,7 +181,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
RevCommit commit = tr.commit().message("PS2").create();
update = newUpdate(c, changeOwner);
update.putComment(
- Status.PUBLISHED,
+ Comment.Status.PUBLISHED,
newComment(
c.currentPatchSetId(),
"a.txt",
@@ -193,7 +193,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
TimeUtil.nowTs(),
"Comment",
(short) 1,
- commit.name(),
+ commit,
false));
update.setChangeMessage("coverage verification");
update.setTag(coverageTag);
@@ -209,10 +209,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals = notes.getApprovals();
assertThat(approvals).hasSize(1);
PatchSetApproval approval = approvals.entries().asList().get(0).getValue();
- assertThat(approval.getTag()).isEqualTo(integrationTag);
- assertThat(approval.getValue()).isEqualTo(-1);
+ assertThat(approval.tag()).hasValue(integrationTag);
+ assertThat(approval.value()).isEqualTo(-1);
- ImmutableListMultimap<RevId, Comment> comments = notes.getComments();
+ ImmutableListMultimap<ObjectId, Comment> comments = notes.getComments();
assertThat(comments).hasSize(1);
assertThat(comments.entries().asList().get(0).getValue().tag).isEqualTo(coverageTag);
@@ -236,17 +236,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
List<PatchSetApproval> psas = notes.getApprovals().get(c.currentPatchSetId());
assertThat(psas).hasSize(2);
- assertThat(psas.get(0).getPatchSetId()).isEqualTo(c.currentPatchSetId());
- assertThat(psas.get(0).getAccountId().get()).isEqualTo(1);
- assertThat(psas.get(0).getLabel()).isEqualTo("Code-Review");
- assertThat(psas.get(0).getValue()).isEqualTo((short) -1);
- assertThat(psas.get(0).getGranted()).isEqualTo(truncate(after(c, 2000)));
+ assertThat(psas.get(0).patchSetId()).isEqualTo(c.currentPatchSetId());
+ assertThat(psas.get(0).accountId().get()).isEqualTo(1);
+ assertThat(psas.get(0).label()).isEqualTo("Code-Review");
+ assertThat(psas.get(0).value()).isEqualTo((short) -1);
+ assertThat(psas.get(0).granted()).isEqualTo(truncate(after(c, 2000)));
- assertThat(psas.get(1).getPatchSetId()).isEqualTo(c.currentPatchSetId());
- assertThat(psas.get(1).getAccountId().get()).isEqualTo(1);
- assertThat(psas.get(1).getLabel()).isEqualTo("Verified");
- assertThat(psas.get(1).getValue()).isEqualTo((short) 1);
- assertThat(psas.get(1).getGranted()).isEqualTo(psas.get(0).getGranted());
+ assertThat(psas.get(1).patchSetId()).isEqualTo(c.currentPatchSetId());
+ assertThat(psas.get(1).accountId().get()).isEqualTo(1);
+ assertThat(psas.get(1).label()).isEqualTo("Verified");
+ assertThat(psas.get(1).value()).isEqualTo((short) 1);
+ assertThat(psas.get(1).granted()).isEqualTo(psas.get(0).granted());
}
@Test
@@ -268,18 +268,18 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(psas).hasSize(2);
PatchSetApproval psa1 = Iterables.getOnlyElement(psas.get(ps1));
- assertThat(psa1.getPatchSetId()).isEqualTo(ps1);
- assertThat(psa1.getAccountId().get()).isEqualTo(1);
- assertThat(psa1.getLabel()).isEqualTo("Code-Review");
- assertThat(psa1.getValue()).isEqualTo((short) -1);
- assertThat(psa1.getGranted()).isEqualTo(truncate(after(c, 2000)));
+ assertThat(psa1.patchSetId()).isEqualTo(ps1);
+ assertThat(psa1.accountId().get()).isEqualTo(1);
+ assertThat(psa1.label()).isEqualTo("Code-Review");
+ assertThat(psa1.value()).isEqualTo((short) -1);
+ assertThat(psa1.granted()).isEqualTo(truncate(after(c, 2000)));
PatchSetApproval psa2 = Iterables.getOnlyElement(psas.get(ps2));
- assertThat(psa2.getPatchSetId()).isEqualTo(ps2);
- assertThat(psa2.getAccountId().get()).isEqualTo(1);
- assertThat(psa2.getLabel()).isEqualTo("Code-Review");
- assertThat(psa2.getValue()).isEqualTo((short) +1);
- assertThat(psa2.getGranted()).isEqualTo(truncate(after(c, 4000)));
+ assertThat(psa2.patchSetId()).isEqualTo(ps2);
+ assertThat(psa2.accountId().get()).isEqualTo(1);
+ assertThat(psa2.label()).isEqualTo("Code-Review");
+ assertThat(psa2.value()).isEqualTo((short) +1);
+ assertThat(psa2.granted()).isEqualTo(truncate(after(c, 4000)));
}
@Test
@@ -292,8 +292,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeNotes notes = newNotes(c);
PatchSetApproval psa =
Iterables.getOnlyElement(notes.getApprovals().get(c.currentPatchSetId()));
- assertThat(psa.getLabel()).isEqualTo("Code-Review");
- assertThat(psa.getValue()).isEqualTo((short) -1);
+ assertThat(psa.label()).isEqualTo("Code-Review");
+ assertThat(psa.value()).isEqualTo((short) -1);
update = newUpdate(c, changeOwner);
update.putApproval("Code-Review", (short) 1);
@@ -301,8 +301,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
notes = newNotes(c);
psa = Iterables.getOnlyElement(notes.getApprovals().get(c.currentPatchSetId()));
- assertThat(psa.getLabel()).isEqualTo("Code-Review");
- assertThat(psa.getValue()).isEqualTo((short) 1);
+ assertThat(psa.label()).isEqualTo("Code-Review");
+ assertThat(psa.value()).isEqualTo((short) 1);
}
@Test
@@ -321,17 +321,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
List<PatchSetApproval> psas = notes.getApprovals().get(c.currentPatchSetId());
assertThat(psas).hasSize(2);
- assertThat(psas.get(0).getPatchSetId()).isEqualTo(c.currentPatchSetId());
- assertThat(psas.get(0).getAccountId().get()).isEqualTo(1);
- assertThat(psas.get(0).getLabel()).isEqualTo("Code-Review");
- assertThat(psas.get(0).getValue()).isEqualTo((short) -1);
- assertThat(psas.get(0).getGranted()).isEqualTo(truncate(after(c, 2000)));
+ assertThat(psas.get(0).patchSetId()).isEqualTo(c.currentPatchSetId());
+ assertThat(psas.get(0).accountId().get()).isEqualTo(1);
+ assertThat(psas.get(0).label()).isEqualTo("Code-Review");
+ assertThat(psas.get(0).value()).isEqualTo((short) -1);
+ assertThat(psas.get(0).granted()).isEqualTo(truncate(after(c, 2000)));
- assertThat(psas.get(1).getPatchSetId()).isEqualTo(c.currentPatchSetId());
- assertThat(psas.get(1).getAccountId().get()).isEqualTo(2);
- assertThat(psas.get(1).getLabel()).isEqualTo("Code-Review");
- assertThat(psas.get(1).getValue()).isEqualTo((short) 1);
- assertThat(psas.get(1).getGranted()).isEqualTo(truncate(after(c, 3000)));
+ assertThat(psas.get(1).patchSetId()).isEqualTo(c.currentPatchSetId());
+ assertThat(psas.get(1).accountId().get()).isEqualTo(2);
+ assertThat(psas.get(1).label()).isEqualTo("Code-Review");
+ assertThat(psas.get(1).value()).isEqualTo((short) 1);
+ assertThat(psas.get(1).granted()).isEqualTo(truncate(after(c, 3000)));
}
@Test
@@ -344,9 +344,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeNotes notes = newNotes(c);
PatchSetApproval psa =
Iterables.getOnlyElement(notes.getApprovals().get(c.currentPatchSetId()));
- assertThat(psa.getAccountId().get()).isEqualTo(1);
- assertThat(psa.getLabel()).isEqualTo("Not-For-Long");
- assertThat(psa.getValue()).isEqualTo((short) 1);
+ assertThat(psa.accountId().get()).isEqualTo(1);
+ assertThat(psa.label()).isEqualTo("Not-For-Long");
+ assertThat(psa.value()).isEqualTo((short) 1);
update = newUpdate(c, changeOwner);
update.removeApproval("Not-For-Long");
@@ -356,8 +356,12 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(notes.getApprovals())
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- psa.getPatchSetId(),
- new PatchSetApproval(psa.getKey(), (short) 0, update.getWhen())));
+ psa.patchSetId(),
+ PatchSetApproval.builder()
+ .key(psa.key())
+ .value(0)
+ .granted(update.getWhen())
+ .build()));
}
@Test
@@ -370,9 +374,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeNotes notes = newNotes(c);
PatchSetApproval psa =
Iterables.getOnlyElement(notes.getApprovals().get(c.currentPatchSetId()));
- assertThat(psa.getAccountId()).isEqualTo(otherUserId);
- assertThat(psa.getLabel()).isEqualTo("Not-For-Long");
- assertThat(psa.getValue()).isEqualTo((short) 1);
+ assertThat(psa.accountId()).isEqualTo(otherUserId);
+ assertThat(psa.label()).isEqualTo("Not-For-Long");
+ assertThat(psa.value()).isEqualTo((short) 1);
update = newUpdate(c, changeOwner);
update.removeApprovalFor(otherUserId, "Not-For-Long");
@@ -382,8 +386,12 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(notes.getApprovals())
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- psa.getPatchSetId(),
- new PatchSetApproval(psa.getKey(), (short) 0, update.getWhen())));
+ psa.patchSetId(),
+ PatchSetApproval.builder()
+ .key(psa.key())
+ .value(0)
+ .granted(update.getWhen())
+ .build()));
// Add back approval on same label.
update = newUpdate(c, otherUser);
@@ -392,9 +400,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
notes = newNotes(c);
psa = Iterables.getOnlyElement(notes.getApprovals().get(c.currentPatchSetId()));
- assertThat(psa.getAccountId()).isEqualTo(otherUserId);
- assertThat(psa.getLabel()).isEqualTo("Not-For-Long");
- assertThat(psa.getValue()).isEqualTo((short) 2);
+ assertThat(psa.accountId()).isEqualTo(otherUserId);
+ assertThat(psa.label()).isEqualTo("Not-For-Long");
+ assertThat(psa.value()).isEqualTo((short) 2);
}
@Test
@@ -408,17 +416,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeNotes notes = newNotes(c);
ImmutableList<PatchSetApproval> approvals =
notes.getApprovals().get(c.currentPatchSetId()).stream()
- .sorted(comparing(a -> a.getAccountId().get()))
+ .sorted(comparing(a -> a.accountId().get()))
.collect(toImmutableList());
assertThat(approvals).hasSize(2);
- assertThat(approvals.get(0).getAccountId()).isEqualTo(changeOwner.getAccountId());
- assertThat(approvals.get(0).getLabel()).isEqualTo("Code-Review");
- assertThat(approvals.get(0).getValue()).isEqualTo((short) 1);
+ assertThat(approvals.get(0).accountId()).isEqualTo(changeOwner.getAccountId());
+ assertThat(approvals.get(0).label()).isEqualTo("Code-Review");
+ assertThat(approvals.get(0).value()).isEqualTo((short) 1);
- assertThat(approvals.get(1).getAccountId()).isEqualTo(otherUser.getAccountId());
- assertThat(approvals.get(1).getLabel()).isEqualTo("Code-Review");
- assertThat(approvals.get(1).getValue()).isEqualTo((short) -1);
+ assertThat(approvals.get(1).accountId()).isEqualTo(otherUser.getAccountId());
+ assertThat(approvals.get(1).label()).isEqualTo("Code-Review");
+ assertThat(approvals.get(1).value()).isEqualTo((short) -1);
}
@Test
@@ -448,12 +456,12 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeNotes notes = newNotes(c);
List<PatchSetApproval> approvals = Lists.newArrayList(notes.getApprovals().values());
assertThat(approvals).hasSize(2);
- assertThat(approvals.get(0).getLabel()).isEqualTo("Verified");
- assertThat(approvals.get(0).getValue()).isEqualTo((short) 1);
- assertThat(approvals.get(0).isPostSubmit()).isFalse();
- assertThat(approvals.get(1).getLabel()).isEqualTo("Code-Review");
- assertThat(approvals.get(1).getValue()).isEqualTo((short) 2);
- assertThat(approvals.get(1).isPostSubmit()).isTrue();
+ assertThat(approvals.get(0).label()).isEqualTo("Verified");
+ assertThat(approvals.get(0).value()).isEqualTo((short) 1);
+ assertThat(approvals.get(0).postSubmit()).isFalse();
+ assertThat(approvals.get(1).label()).isEqualTo("Code-Review");
+ assertThat(approvals.get(1).value()).isEqualTo((short) 2);
+ assertThat(approvals.get(1).postSubmit()).isTrue();
}
@Test
@@ -488,26 +496,26 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
List<PatchSetApproval> approvals = Lists.newArrayList(notes.getApprovals().values());
assertThat(approvals).hasSize(3);
- assertThat(approvals.get(0).getAccountId()).isEqualTo(ownerId);
- assertThat(approvals.get(0).getLabel()).isEqualTo("Verified");
- assertThat(approvals.get(0).getValue()).isEqualTo(1);
- assertThat(approvals.get(0).isPostSubmit()).isFalse();
- assertThat(approvals.get(1).getAccountId()).isEqualTo(ownerId);
- assertThat(approvals.get(1).getLabel()).isEqualTo("Code-Review");
- assertThat(approvals.get(1).getValue()).isEqualTo(2);
- assertThat(approvals.get(1).isPostSubmit()).isFalse(); // During submit.
- assertThat(approvals.get(2).getAccountId()).isEqualTo(otherId);
- assertThat(approvals.get(2).getLabel()).isEqualTo("Other-Label");
- assertThat(approvals.get(2).getValue()).isEqualTo(2);
- assertThat(approvals.get(2).isPostSubmit()).isTrue();
+ assertThat(approvals.get(0).accountId()).isEqualTo(ownerId);
+ assertThat(approvals.get(0).label()).isEqualTo("Verified");
+ assertThat(approvals.get(0).value()).isEqualTo(1);
+ assertThat(approvals.get(0).postSubmit()).isFalse();
+ assertThat(approvals.get(1).accountId()).isEqualTo(ownerId);
+ assertThat(approvals.get(1).label()).isEqualTo("Code-Review");
+ assertThat(approvals.get(1).value()).isEqualTo(2);
+ assertThat(approvals.get(1).postSubmit()).isFalse(); // During submit.
+ assertThat(approvals.get(2).accountId()).isEqualTo(otherId);
+ assertThat(approvals.get(2).label()).isEqualTo("Other-Label");
+ assertThat(approvals.get(2).value()).isEqualTo(2);
+ assertThat(approvals.get(2).postSubmit()).isTrue();
}
@Test
public void multipleReviewers() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
- update.putReviewer(otherUser.getAccount().getId(), REVIEWER);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
+ update.putReviewer(otherUser.getAccount().id(), REVIEWER);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -516,8 +524,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
.isEqualTo(
ReviewerSet.fromTable(
ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>builder()
- .put(REVIEWER, new Account.Id(1), ts)
- .put(REVIEWER, new Account.Id(2), ts)
+ .put(REVIEWER, Account.id(1), ts)
+ .put(REVIEWER, Account.id(2), ts)
.build()));
}
@@ -525,8 +533,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void reviewerTypes() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
- update.putReviewer(otherUser.getAccount().getId(), CC);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
+ update.putReviewer(otherUser.getAccount().id(), CC);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -535,8 +543,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
.isEqualTo(
ReviewerSet.fromTable(
ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>builder()
- .put(REVIEWER, new Account.Id(1), ts)
- .put(CC, new Account.Id(2), ts)
+ .put(REVIEWER, Account.id(1), ts)
+ .put(CC, Account.id(2), ts)
.build()));
}
@@ -544,29 +552,29 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void oneReviewerMultipleTypes() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(otherUser.getAccount().getId(), REVIEWER);
+ update.putReviewer(otherUser.getAccount().id(), REVIEWER);
update.commit();
ChangeNotes notes = newNotes(c);
Timestamp ts = new Timestamp(update.getWhen().getTime());
assertThat(notes.getReviewers())
- .isEqualTo(ReviewerSet.fromTable(ImmutableTable.of(REVIEWER, new Account.Id(2), ts)));
+ .isEqualTo(ReviewerSet.fromTable(ImmutableTable.of(REVIEWER, Account.id(2), ts)));
update = newUpdate(c, otherUser);
- update.putReviewer(otherUser.getAccount().getId(), CC);
+ update.putReviewer(otherUser.getAccount().id(), CC);
update.commit();
notes = newNotes(c);
ts = new Timestamp(update.getWhen().getTime());
assertThat(notes.getReviewers())
- .isEqualTo(ReviewerSet.fromTable(ImmutableTable.of(CC, new Account.Id(2), ts)));
+ .isEqualTo(ReviewerSet.fromTable(ImmutableTable.of(CC, Account.id(2), ts)));
}
@Test
public void removeReviewer() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(otherUser.getAccount().getId(), REVIEWER);
+ update.putReviewer(otherUser.getAccount().id(), REVIEWER);
update.commit();
update = newUpdate(c, changeOwner);
@@ -580,17 +588,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeNotes notes = newNotes(c);
List<PatchSetApproval> psas = notes.getApprovals().get(c.currentPatchSetId());
assertThat(psas).hasSize(2);
- assertThat(psas.get(0).getAccountId()).isEqualTo(changeOwner.getAccount().getId());
- assertThat(psas.get(1).getAccountId()).isEqualTo(otherUser.getAccount().getId());
+ assertThat(psas.get(0).accountId()).isEqualTo(changeOwner.getAccount().id());
+ assertThat(psas.get(1).accountId()).isEqualTo(otherUser.getAccount().id());
update = newUpdate(c, changeOwner);
- update.removeReviewer(otherUser.getAccount().getId());
+ update.removeReviewer(otherUser.getAccount().id());
update.commit();
notes = newNotes(c);
psas = notes.getApprovals().get(c.currentPatchSetId());
assertThat(psas).hasSize(1);
- assertThat(psas.get(0).getAccountId()).isEqualTo(changeOwner.getAccount().getId());
+ assertThat(psas.get(0).accountId()).isEqualTo(changeOwner.getAccount().id());
}
@Test
@@ -736,6 +744,35 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
}
@Test
+ public void assigneeStatusUpdateChangeNotes() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, otherUser);
+ update.setAssignee(otherUserId);
+ update.commit();
+
+ update = newUpdate(c, changeOwner);
+ update.removeAssignee();
+ update.commit();
+
+ update = newUpdate(c, changeOwner);
+ update.setAssignee(changeOwner.getAccountId());
+ update.commit();
+
+ update = newUpdate(c, changeOwner);
+ update.setAssignee(otherUserId);
+ update.commit();
+
+ ChangeNotes notes = newNotes(c);
+ ImmutableList<AssigneeStatusUpdate> statusUpdates = notes.getAssigneeUpdates();
+ assertThat(statusUpdates).hasSize(4);
+ assertThat(statusUpdates.get(3).updatedBy()).isEqualTo(otherUserId);
+ assertThat(statusUpdates.get(3).currentAssignee()).hasValue(otherUserId);
+ assertThat(statusUpdates.get(2).currentAssignee()).isEmpty();
+ assertThat(statusUpdates.get(1).currentAssignee()).hasValue(changeOwner.getAccountId());
+ assertThat(statusUpdates.get(0).currentAssignee()).hasValue(otherUserId);
+ }
+
+ @Test
public void hashtagCommit() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
@@ -819,14 +856,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
// Trying to set another Change-Id fails
String otherChangeId = "I577fb248e474018276351785930358ec0450e9f7";
- update = newUpdate(c, changeOwner);
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage(
- "The Change-Id was already set to "
- + c.getKey()
- + ", so we cannot set this Change-Id: "
- + otherChangeId);
- update.setChangeId(otherChangeId);
+ ChangeUpdate failingUpdate = newUpdate(c, changeOwner);
+ IllegalArgumentException thrown =
+ assertThrows(
+ IllegalArgumentException.class, () -> failingUpdate.setChangeId(otherChangeId));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains(
+ "The Change-Id was already set to "
+ + c.getKey()
+ + ", so we cannot set this Change-Id: "
+ + otherChangeId);
}
@Test
@@ -834,7 +874,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeNotes notes = newNotes(c);
- Branch.NameKey expectedBranch = new Branch.NameKey(project, "refs/heads/master");
+ BranchNameKey expectedBranch = BranchNameKey.create(project, "refs/heads/master");
assertThat(notes.getChange().getDest()).isEqualTo(expectedBranch);
// An update doesn't affect the branch
@@ -849,7 +889,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.setBranch(otherBranch);
update.commit();
assertThat(newNotes(c).getChange().getDest())
- .isEqualTo(new Branch.NameKey(project, otherBranch));
+ .isEqualTo(BranchNameKey.create(project, otherBranch));
}
@Test
@@ -956,7 +996,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
c.setCurrentPatchSet(c.currentPatchSetId(), " " + trimmedSubj, c.getOriginalSubject());
ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
- update.setBranch(c.getDest().get());
+ update.setBranch(c.getDest().branch());
update.commit();
ChangeNotes notes = newNotes(c);
@@ -968,7 +1008,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
c.setCurrentPatchSet(c.currentPatchSetId(), tabSubj, c.getOriginalSubject());
update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
- update.setBranch(c.getDest().get());
+ update.setBranch(c.getDest().branch());
update.commit();
notes = newNotes(c);
@@ -977,7 +1017,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
@Test
public void commitChangeNotesUnique() throws Exception {
- // PatchSetId -> RevId must be a one to one mapping
+ // PatchSetId -> ObjectId must be a one to one mapping
Change c = newChange();
ChangeNotes notes = newNotes(c);
@@ -990,19 +1030,15 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.setCommit(rw, commit);
update.commit();
- try {
- newNotes(c);
- fail("Expected IOException");
- } catch (StorageException e) {
- assertCause(
- e,
- ConfigInvalidException.class,
- "Multiple revisions parsed for patch set 1:"
- + " RevId{"
- + commit.name()
- + "} and "
- + ps.getRevision().get());
- }
+ StorageException e = assertThrows(StorageException.class, () -> newNotes(c));
+ assertCause(
+ e,
+ ConfigInvalidException.class,
+ "Multiple revisions parsed for patch set 1:"
+ + " "
+ + commit.name()
+ + " and "
+ + ps.commitId().name());
}
@Test
@@ -1012,32 +1048,32 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
// ps1 created by newChange()
ChangeNotes notes = newNotes(c);
PatchSet ps1 = notes.getCurrentPatchSet();
- assertThat(notes.getChange().currentPatchSetId()).isEqualTo(ps1.getId());
+ assertThat(notes.getChange().currentPatchSetId()).isEqualTo(ps1.id());
assertThat(notes.getChange().getSubject()).isEqualTo("Change subject");
assertThat(notes.getChange().getOriginalSubject()).isEqualTo("Change subject");
- assertThat(ps1.getId()).isEqualTo(new PatchSet.Id(c.getId(), 1));
- assertThat(ps1.getUploader()).isEqualTo(changeOwner.getAccountId());
+ assertThat(ps1.id()).isEqualTo(PatchSet.id(c.getId(), 1));
+ assertThat(ps1.uploader()).isEqualTo(changeOwner.getAccountId());
// ps2 by other user
RevCommit commit = incrementPatchSet(c, otherUser);
notes = newNotes(c);
PatchSet ps2 = notes.getCurrentPatchSet();
- assertThat(ps2.getId()).isEqualTo(new PatchSet.Id(c.getId(), 2));
+ assertThat(ps2.id()).isEqualTo(PatchSet.id(c.getId(), 2));
assertThat(notes.getChange().getSubject()).isEqualTo("PS2");
assertThat(notes.getChange().getOriginalSubject()).isEqualTo("Change subject");
- assertThat(notes.getChange().currentPatchSetId()).isEqualTo(ps2.getId());
- assertThat(ps2.getRevision().get()).isNotEqualTo(ps1.getRevision());
- assertThat(ps2.getRevision().get()).isEqualTo(commit.name());
- assertThat(ps2.getUploader()).isEqualTo(otherUser.getAccountId());
- assertThat(ps2.getCreatedOn()).isEqualTo(notes.getChange().getLastUpdatedOn());
+ assertThat(notes.getChange().currentPatchSetId()).isEqualTo(ps2.id());
+ assertThat(ps2.commitId()).isNotEqualTo(ps1.commitId());
+ assertThat(ps2.commitId()).isEqualTo(commit);
+ assertThat(ps2.uploader()).isEqualTo(otherUser.getAccountId());
+ assertThat(ps2.createdOn()).isEqualTo(notes.getChange().getLastUpdatedOn());
// comment on ps1, current patch set is still ps2
ChangeUpdate update = newUpdate(c, changeOwner);
- update.setPatchSetId(ps1.getId());
+ update.setPatchSetId(ps1.id());
update.setChangeMessage("Comment on old patch set.");
update.commit();
notes = newNotes(c);
- assertThat(notes.getChange().currentPatchSetId()).isEqualTo(ps2.getId());
+ assertThat(notes.getChange().currentPatchSetId()).isEqualTo(ps2.id());
}
@Test
@@ -1053,7 +1089,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.putApproval("Code-Review", (short) 1);
update.setChangeMessage("This is a message");
update.putComment(
- Status.PUBLISHED,
+ Comment.Status.PUBLISHED,
newComment(
c.currentPatchSetId(),
"a.txt",
@@ -1065,7 +1101,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
TimeUtil.nowTs(),
"Comment",
(short) 1,
- commit.name(),
+ commit,
false));
update.commit();
@@ -1098,14 +1134,14 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
PatchSet.Id psId1 = c.currentPatchSetId();
ChangeNotes notes = newNotes(c);
- assertThat(notes.getPatchSets().get(psId1).getGroups()).isEmpty();
+ assertThat(notes.getPatchSets().get(psId1).groups()).isEmpty();
// ps1
ChangeUpdate update = newUpdate(c, changeOwner);
update.setGroups(ImmutableList.of("a", "b"));
update.commit();
notes = newNotes(c);
- assertThat(notes.getPatchSets().get(psId1).getGroups()).containsExactly("a", "b").inOrder();
+ assertThat(notes.getPatchSets().get(psId1).groups()).containsExactly("a", "b").inOrder();
incrementCurrentPatchSetFieldOnly(c);
PatchSet.Id psId2 = c.currentPatchSetId();
@@ -1114,8 +1150,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.setGroups(ImmutableList.of("d"));
update.commit();
notes = newNotes(c);
- assertThat(notes.getPatchSets().get(psId2).getGroups()).containsExactly("d");
- assertThat(notes.getPatchSets().get(psId1).getGroups()).containsExactly("a", "b").inOrder();
+ assertThat(notes.getPatchSets().get(psId2).groups()).containsExactly("d");
+ assertThat(notes.getPatchSets().get(psId1).groups()).containsExactly("a", "b").inOrder();
}
@Test
@@ -1144,8 +1180,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
readNote(notes, commit);
Map<PatchSet.Id, PatchSet> patchSets = notes.getPatchSets();
- assertThat(patchSets.get(psId1).getPushCertificate()).isNull();
- assertThat(patchSets.get(psId2).getPushCertificate()).isEqualTo(pushCert);
+ assertThat(patchSets.get(psId1).pushCertificate()).isEmpty();
+ assertThat(patchSets.get(psId2).pushCertificate()).hasValue(pushCert);
assertThat(notes.getComments()).isEmpty();
// comment on ps2
@@ -1153,7 +1189,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.setPatchSetId(psId2);
Timestamp ts = TimeUtil.nowTs();
update.putComment(
- Status.PUBLISHED,
+ Comment.Status.PUBLISHED,
newComment(
psId2,
"a.txt",
@@ -1165,15 +1201,15 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ts,
"Comment",
(short) 1,
- commit.name(),
+ commit,
false));
update.commit();
notes = newNotes(c);
patchSets = notes.getPatchSets();
- assertThat(patchSets.get(psId1).getPushCertificate()).isNull();
- assertThat(patchSets.get(psId2).getPushCertificate()).isEqualTo(pushCert);
+ assertThat(patchSets.get(psId1).pushCertificate()).isEmpty();
+ assertThat(patchSets.get(psId2).pushCertificate()).hasValue(pushCert);
assertThat(notes.getComments()).isNotEmpty();
}
@@ -1203,13 +1239,13 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
List<PatchSetApproval> psas = notes.getApprovals().get(c.currentPatchSetId());
assertThat(psas).hasSize(2);
- assertThat(psas.get(0).getAccountId()).isEqualTo(changeOwner.getAccount().getId());
- assertThat(psas.get(0).getLabel()).isEqualTo("Verified");
- assertThat(psas.get(0).getValue()).isEqualTo((short) 1);
+ assertThat(psas.get(0).accountId()).isEqualTo(changeOwner.getAccount().id());
+ assertThat(psas.get(0).label()).isEqualTo("Verified");
+ assertThat(psas.get(0).value()).isEqualTo((short) 1);
- assertThat(psas.get(1).getAccountId()).isEqualTo(otherUser.getAccount().getId());
- assertThat(psas.get(1).getLabel()).isEqualTo("Code-Review");
- assertThat(psas.get(1).getValue()).isEqualTo((short) 2);
+ assertThat(psas.get(1).accountId()).isEqualTo(otherUser.getAccount().id());
+ assertThat(psas.get(1).label()).isEqualTo("Code-Review");
+ assertThat(psas.get(1).value()).isEqualTo((short) 2);
}
@Test
@@ -1235,10 +1271,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
time1,
message1,
(short) 0,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
+ ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"),
false);
update1.setPatchSetId(psId);
- update1.putComment(Status.PUBLISHED, comment1);
+ update1.putComment(Comment.Status.PUBLISHED, comment1);
updateManager.add(update1);
ChangeUpdate update2 = newUpdate(c, otherUser);
@@ -1260,12 +1296,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
try (ChangeNotesRevWalk rw = ChangeNotesCommit.newRevWalk(repo)) {
ChangeNotesParser notesWithComments =
new ChangeNotesParser(
- c.getId(),
- commitWithComments.copy(),
- rw,
- changeNoteJson,
- legacyChangeNoteRead,
- args.metrics);
+ c.getId(), commitWithComments.copy(), rw, changeNoteJson, args.metrics);
ChangeNotesState state = notesWithComments.parseAll();
assertThat(state.approvals()).isEmpty();
assertThat(state.publishedComments()).hasSize(1);
@@ -1274,12 +1305,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
try (ChangeNotesRevWalk rw = ChangeNotesCommit.newRevWalk(repo)) {
ChangeNotesParser notesWithApprovals =
new ChangeNotesParser(
- c.getId(),
- commitWithApprovals.copy(),
- rw,
- changeNoteJson,
- legacyChangeNoteRead,
- args.metrics);
+ c.getId(), commitWithApprovals.copy(), rw, changeNoteJson, args.metrics);
ChangeNotesState state = notesWithApprovals.parseAll();
assertThat(state.approvals()).hasSize(1);
@@ -1317,25 +1343,25 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
PatchSetApproval approval1 =
newNotes(c1).getApprovals().get(c1.currentPatchSetId()).iterator().next();
- assertThat(approval1.getLabel()).isEqualTo("Verified");
+ assertThat(approval1.label()).isEqualTo("Verified");
PatchSetApproval approval2 =
newNotes(c2).getApprovals().get(c2.currentPatchSetId()).iterator().next();
- assertThat(approval2.getLabel()).isEqualTo("Code-Review");
+ assertThat(approval2.label()).isEqualTo("Code-Review");
}
@Test
public void changeMessageOnePatchSet() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
update.setChangeMessage("Just a little code change.\n");
update.commit();
ChangeNotes notes = newNotes(c);
ChangeMessage cm = Iterables.getOnlyElement(notes.getChangeMessages());
assertThat(cm.getMessage()).isEqualTo("Just a little code change.\n");
- assertThat(cm.getAuthor()).isEqualTo(changeOwner.getAccount().getId());
+ assertThat(cm.getAuthor()).isEqualTo(changeOwner.getAccount().id());
assertThat(cm.getPatchSetId()).isEqualTo(c.currentPatchSetId());
}
@@ -1343,7 +1369,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void noChangeMessage() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -1360,7 +1386,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeNotes notes = newNotes(c);
ChangeMessage cm1 = Iterables.getOnlyElement(notes.getChangeMessages());
assertThat(cm1.getMessage()).isEqualTo("Testing trailing double newline\n\n");
- assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().getId());
+ assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().id());
}
@Test
@@ -1379,14 +1405,14 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
+ "Testing paragraph 2\n"
+ "\n"
+ "Testing paragraph 3");
- assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().getId());
+ assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().id());
}
@Test
public void changeMessagesMultiplePatchSets() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
update.setChangeMessage("This is the change message for the first PS.");
update.commit();
PatchSet.Id ps1 = c.currentPatchSetId();
@@ -1404,12 +1430,12 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeMessage cm1 = notes.getChangeMessages().get(0);
assertThat(cm1.getPatchSetId()).isEqualTo(ps1);
assertThat(cm1.getMessage()).isEqualTo("This is the change message for the first PS.");
- assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().getId());
+ assertThat(cm1.getAuthor()).isEqualTo(changeOwner.getAccount().id());
ChangeMessage cm2 = notes.getChangeMessages().get(1);
assertThat(cm2.getPatchSetId()).isEqualTo(ps2);
assertThat(cm2.getMessage()).isEqualTo("This is the change message for the second PS.");
- assertThat(cm2.getAuthor()).isEqualTo(changeOwner.getAccount().getId());
+ assertThat(cm2.getAuthor()).isEqualTo(changeOwner.getAccount().id());
assertThat(cm2.getPatchSetId()).isEqualTo(ps2);
}
@@ -1417,14 +1443,14 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void changeMessageMultipleInOnePatchSet() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
update.setChangeMessage("First change message.\n");
update.commit();
PatchSet.Id ps1 = c.currentPatchSetId();
update = newUpdate(c, changeOwner);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
update.setChangeMessage("Second change message.\n");
update.commit();
@@ -1433,10 +1459,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
List<ChangeMessage> cm = notes.getChangeMessages();
assertThat(cm).hasSize(2);
assertThat(cm.get(0).getMessage()).isEqualTo("First change message.\n");
- assertThat(cm.get(0).getAuthor()).isEqualTo(changeOwner.getAccount().getId());
+ assertThat(cm.get(0).getAuthor()).isEqualTo(changeOwner.getAccount().id());
assertThat(cm.get(0).getPatchSetId()).isEqualTo(ps1);
assertThat(cm.get(1).getMessage()).isEqualTo("Second change message.\n");
- assertThat(cm.get(1).getAuthor()).isEqualTo(changeOwner.getAccount().getId());
+ assertThat(cm.get(1).getAuthor()).isEqualTo(changeOwner.getAccount().id());
assertThat(cm.get(1).getPatchSetId()).isEqualTo(ps1);
}
@@ -1445,7 +1471,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
PatchSet.Id psId = c.currentPatchSetId();
- RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
Comment comment =
newComment(
@@ -1459,14 +1485,14 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
TimeUtil.nowTs(),
"message",
(short) 1,
- revId.get(),
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
- assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(revId, comment));
+ assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(commitId, comment));
}
@Test
@@ -1474,7 +1500,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
PatchSet.Id psId = c.currentPatchSetId();
- RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
CommentRange range = new CommentRange(1, 0, 2, 0);
Comment comment =
@@ -1489,14 +1515,14 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
TimeUtil.nowTs(),
"message",
(short) 1,
- revId.get(),
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
- assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(revId, comment));
+ assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(commitId, comment));
}
@Test
@@ -1504,7 +1530,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
PatchSet.Id psId = c.currentPatchSetId();
- RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
CommentRange range = new CommentRange(0, 0, 0, 0);
Comment comment =
@@ -1519,14 +1545,14 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
TimeUtil.nowTs(),
"message",
(short) 1,
- revId.get(),
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
- assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(revId, comment));
+ assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(commitId, comment));
}
@Test
@@ -1534,7 +1560,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
PatchSet.Id psId = c.currentPatchSetId();
- RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
CommentRange range = new CommentRange(1, 2, 3, 4);
Comment comment =
@@ -1549,18 +1575,18 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
TimeUtil.nowTs(),
"message",
(short) 1,
- revId.get(),
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
- assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(revId, comment));
+ assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(commitId, comment));
}
@Test
- public void patchLineCommentNotesFormatMultiplePatchSetsSameRevId() throws Exception {
+ public void patchLineCommentNotesFormatMultiplePatchSetsSameCommitId() throws Exception {
Change c = newChange();
PatchSet.Id psId1 = c.currentPatchSetId();
incrementPatchSet(c);
@@ -1574,7 +1600,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
CommentRange range1 = new CommentRange(1, 1, 2, 1);
CommentRange range2 = new CommentRange(2, 1, 3, 1);
Timestamp time = TimeUtil.nowTs();
- RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
Comment comment1 =
newComment(
@@ -1588,7 +1614,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
time,
message1,
(short) 0,
- revId.get(),
+ commitId,
false);
Comment comment2 =
newComment(
@@ -1602,7 +1628,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
time,
message2,
(short) 0,
- revId.get(),
+ commitId,
false);
Comment comment3 =
newComment(
@@ -1616,23 +1642,23 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
time,
message3,
(short) 0,
- revId.get(),
+ commitId,
false);
ChangeUpdate update = newUpdate(c, otherUser);
update.setPatchSetId(psId2);
- update.putComment(Status.PUBLISHED, comment3);
- update.putComment(Status.PUBLISHED, comment2);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment3);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getComments())
.isEqualTo(
ImmutableListMultimap.of(
- revId, comment1,
- revId, comment2,
- revId, comment3));
+ commitId, comment1,
+ commitId, comment2,
+ commitId, comment3));
}
@Test
@@ -1645,7 +1671,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
CommentRange range = new CommentRange(1, 1, 2, 1);
Timestamp time = TimeUtil.nowTs();
PatchSet.Id psId = c.currentPatchSetId();
- RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
Comment comment =
newComment(
@@ -1659,25 +1685,25 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
time,
message,
(short) 1,
- revId.get(),
+ commitId,
false);
comment.setRealAuthor(changeOwner.getAccountId());
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
- assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(revId, comment));
+ assertThat(notes.getComments()).isEqualTo(ImmutableListMultimap.of(commitId, comment));
}
@Test
public void patchLineCommentNotesFormatWeirdUser() throws Exception {
- Account account = new Account(new Account.Id(3), TimeUtil.nowTs());
+ Account.Builder account = Account.builder(Account.id(3), TimeUtil.nowTs());
account.setFullName("Weird\n\u0002<User>\n");
account.setPreferredEmail(" we\r\nird@ex>ample<.com");
- accountCache.put(account);
- IdentifiedUser user = userFactory.create(account.getId());
+ accountCache.put(account.build());
+ IdentifiedUser user = userFactory.create(Account.id(3));
Change c = newChange();
ChangeUpdate update = newUpdate(c, user);
@@ -1698,16 +1724,16 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
time,
"comment",
(short) 1,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
+ ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"),
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getComments())
- .isEqualTo(ImmutableListMultimap.of(new RevId(comment.revId), comment));
+ .isEqualTo(ImmutableListMultimap.of(comment.getCommitId(), comment));
}
@Test
@@ -1716,8 +1742,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdate(c, otherUser);
String uuid1 = "uuid1";
String uuid2 = "uuid2";
- String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
- String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
+ ObjectId commitId1 = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId2 = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
String messageForBase = "comment for base";
String messageForPS = "comment for ps";
CommentRange range = new CommentRange(1, 1, 2, 1);
@@ -1736,10 +1762,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
messageForBase,
(short) 0,
- rev1,
+ commitId1,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, commentForBase);
+ update.putComment(Comment.Status.PUBLISHED, commentForBase);
update.commit();
update = newUpdate(c, otherUser);
@@ -1755,17 +1781,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
messageForPS,
(short) 1,
- rev2,
+ commitId2,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, commentForPS);
+ update.putComment(Comment.Status.PUBLISHED, commentForPS);
update.commit();
assertThat(newNotes(c).getComments())
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- new RevId(rev1), commentForBase,
- new RevId(rev2), commentForPS));
+ commitId1, commentForBase,
+ commitId2, commentForPS));
}
@Test
@@ -1773,7 +1799,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
String uuid1 = "uuid1";
String uuid2 = "uuid2";
- String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id psId = c.currentPatchSetId();
String filename = "filename";
@@ -1794,10 +1820,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
timeForComment1,
"comment 1",
side,
- rev,
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
update = newUpdate(c, otherUser);
@@ -1813,17 +1839,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
timeForComment2,
"comment 2",
side,
- rev,
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
assertThat(newNotes(c).getComments())
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- new RevId(rev), comment1,
- new RevId(rev), comment2))
+ commitId, comment1,
+ commitId, comment2))
.inOrder();
}
@@ -1831,7 +1857,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void patchLineCommentMultipleOnePatchsetMultipleFiles() throws Exception {
Change c = newChange();
String uuid = "uuid";
- String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id psId = c.currentPatchSetId();
String filename1 = "filename1";
@@ -1852,10 +1878,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment 1",
side,
- rev,
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
update = newUpdate(c, otherUser);
@@ -1871,17 +1897,17 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment 2",
side,
- rev,
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
assertThat(newNotes(c).getComments())
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- new RevId(rev), comment1,
- new RevId(rev), comment2))
+ commitId, comment1,
+ commitId, comment2))
.inOrder();
}
@@ -1889,8 +1915,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void patchLineCommentMultiplePatchsets() throws Exception {
Change c = newChange();
String uuid = "uuid";
- String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
- String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
+ ObjectId commitId1 = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId2 = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
@@ -1910,10 +1936,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev1,
+ commitId1,
false);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
incrementPatchSet(c);
@@ -1933,24 +1959,24 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps2",
side,
- rev2,
+ commitId2,
false);
update.setPatchSetId(ps2);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
assertThat(newNotes(c).getComments())
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- new RevId(rev1), comment1,
- new RevId(rev2), comment2));
+ commitId1, comment1,
+ commitId2, comment2));
}
@Test
public void patchLineCommentSingleDraftToPublished() throws Exception {
Change c = newChange();
String uuid = "uuid";
- String rev = "abcd4567abcd4567abcd4567abcd4567abcd4567";
+ ObjectId commitId = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
@@ -1970,26 +1996,26 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev,
+ commitId,
false);
update.setPatchSetId(ps1);
- update.putComment(Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment1);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId))
- .containsExactlyEntriesIn(ImmutableListMultimap.of(new RevId(rev), comment1));
+ .containsExactlyEntriesIn(ImmutableListMultimap.of(commitId, comment1));
assertThat(notes.getComments()).isEmpty();
update = newUpdate(c, otherUser);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId)).isEmpty();
assertThat(notes.getComments())
- .containsExactlyEntriesIn(ImmutableListMultimap.of(new RevId(rev), comment1));
+ .containsExactlyEntriesIn(ImmutableListMultimap.of(commitId, comment1));
}
@Test
@@ -1997,7 +2023,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
String uuid1 = "uuid1";
String uuid2 = "uuid2";
- String rev = "abcd4567abcd4567abcd4567abcd4567abcd4567";
+ ObjectId commitId = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
CommentRange range1 = new CommentRange(1, 1, 2, 2);
CommentRange range2 = new CommentRange(2, 2, 3, 3);
String filename = "filename1";
@@ -2020,7 +2046,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev,
+ commitId,
false);
Comment comment2 =
newComment(
@@ -2034,32 +2060,32 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"other on ps1",
side,
- rev,
+ commitId,
false);
- update.putComment(Status.DRAFT, comment1);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId))
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- new RevId(rev), comment1,
- new RevId(rev), comment2))
+ commitId, comment1,
+ commitId, comment2))
.inOrder();
assertThat(notes.getComments()).isEmpty();
// Publish first draft.
update = newUpdate(c, otherUser);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId))
- .containsExactlyEntriesIn(ImmutableListMultimap.of(new RevId(rev), comment2));
+ .containsExactlyEntriesIn(ImmutableListMultimap.of(commitId, comment2));
assertThat(notes.getComments())
- .containsExactlyEntriesIn(ImmutableListMultimap.of(new RevId(rev), comment1));
+ .containsExactlyEntriesIn(ImmutableListMultimap.of(commitId, comment1));
}
@Test
@@ -2067,8 +2093,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
String uuid1 = "uuid1";
String uuid2 = "uuid2";
- String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
- String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
+ ObjectId commitId1 = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId2 = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
CommentRange range1 = new CommentRange(1, 1, 2, 2);
CommentRange range2 = new CommentRange(2, 2, 3, 3);
String filename = "filename1";
@@ -2090,7 +2116,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on base",
(short) 0,
- rev1,
+ commitId1,
false);
Comment psComment =
newComment(
@@ -2104,27 +2130,27 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps",
(short) 1,
- rev2,
+ commitId2,
false);
- update.putComment(Status.DRAFT, baseComment);
- update.putComment(Status.DRAFT, psComment);
+ update.putComment(Comment.Status.DRAFT, baseComment);
+ update.putComment(Comment.Status.DRAFT, psComment);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId))
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- new RevId(rev1), baseComment,
- new RevId(rev2), psComment));
+ commitId1, baseComment,
+ commitId2, psComment));
assertThat(notes.getComments()).isEmpty();
// Publish both comments.
update = newUpdate(c, otherUser);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, baseComment);
- update.putComment(Status.PUBLISHED, psComment);
+ update.putComment(Comment.Status.PUBLISHED, baseComment);
+ update.putComment(Comment.Status.PUBLISHED, psComment);
update.commit();
notes = newNotes(c);
@@ -2132,16 +2158,15 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(notes.getComments())
.containsExactlyEntriesIn(
ImmutableListMultimap.of(
- new RevId(rev1), baseComment,
- new RevId(rev2), psComment));
+ commitId1, baseComment,
+ commitId2, psComment));
}
@Test
public void patchLineCommentsDeleteAllDrafts() throws Exception {
Change c = newChange();
String uuid = "uuid";
- String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
- ObjectId objId = ObjectId.fromString(rev);
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id psId = c.currentPatchSetId();
String filename = "filename";
@@ -2161,15 +2186,15 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev,
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.DRAFT, comment);
+ update.putComment(Comment.Status.DRAFT, comment);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId)).hasSize(1);
- assertThat(notes.getDraftCommentNotes().getNoteMap().contains(objId)).isTrue();
+ assertThat(notes.getDraftCommentNotes().getNoteMap().contains(commitId)).isTrue();
update = newUpdate(c, otherUser);
update.setPatchSetId(psId);
@@ -2185,10 +2210,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void patchLineCommentsDeleteAllDraftsForOneRevision() throws Exception {
Change c = newChange();
String uuid = "uuid";
- String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
- String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
- ObjectId objId1 = ObjectId.fromString(rev1);
- ObjectId objId2 = ObjectId.fromString(rev2);
+ ObjectId commitId1 = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId2 = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
@@ -2208,10 +2231,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev1,
+ commitId1,
false);
update.setPatchSetId(ps1);
- update.putComment(Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment1);
update.commit();
incrementPatchSet(c);
@@ -2231,10 +2254,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps2",
side,
- rev2,
+ commitId2,
false);
update.setPatchSetId(ps2);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -2248,15 +2271,15 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
notes = newNotes(c);
assertThat(notes.getDraftComments(otherUserId)).hasSize(1);
NoteMap noteMap = notes.getDraftCommentNotes().getNoteMap();
- assertThat(noteMap.contains(objId1)).isTrue();
- assertThat(noteMap.contains(objId2)).isFalse();
+ assertThat(noteMap.contains(commitId1)).isTrue();
+ assertThat(noteMap.contains(commitId2)).isFalse();
}
@Test
public void addingPublishedCommentDoesNotCreateNoOpCommitOnEmptyDraftRef() throws Exception {
Change c = newChange();
String uuid = "uuid";
- String rev = "abcd4567abcd4567abcd4567abcd4567abcd4567";
+ ObjectId commitId = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
@@ -2276,9 +2299,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev,
+ commitId,
false);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
assertThat(repo.exactRef(changeMetaRef(c.getId()))).isNotNull();
@@ -2289,7 +2312,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
@Test
public void addingPublishedCommentDoesNotCreateNoOpCommitOnNonEmptyDraftRef() throws Exception {
Change c = newChange();
- String rev = "abcd4567abcd4567abcd4567abcd4567abcd4567";
+ ObjectId commitId = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
@@ -2309,9 +2332,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"draft comment on ps1",
side,
- rev,
+ commitId,
false);
- update.putComment(Status.DRAFT, draft);
+ update.putComment(Comment.Status.DRAFT, draft);
update.commit();
String draftRef = refsDraftComments(c.getId(), otherUser.getAccountId());
@@ -2331,9 +2354,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev,
+ commitId,
false);
- update.putComment(Status.PUBLISHED, pub);
+ update.putComment(Comment.Status.PUBLISHED, pub);
update.commit();
assertThat(exactRefAllUsers(draftRef)).isEqualTo(old);
@@ -2344,7 +2367,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
String uuid = "uuid";
- String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
String messageForBase = "comment for base";
Timestamp now = TimeUtil.nowTs();
PatchSet.Id psId = c.currentPatchSetId();
@@ -2361,14 +2384,14 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
messageForBase,
(short) 0,
- rev,
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
assertThat(newNotes(c).getComments())
- .containsExactlyEntriesIn(ImmutableListMultimap.of(new RevId(rev), comment));
+ .containsExactlyEntriesIn(ImmutableListMultimap.of(commitId, comment));
}
@Test
@@ -2376,7 +2399,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
String uuid = "uuid";
- String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
String messageForBase = "comment for base";
Timestamp now = TimeUtil.nowTs();
PatchSet.Id psId = c.currentPatchSetId();
@@ -2393,22 +2416,22 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
messageForBase,
(short) 0,
- rev,
+ commitId,
false);
update.setPatchSetId(psId);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
assertThat(newNotes(c).getComments())
- .containsExactlyEntriesIn(ImmutableListMultimap.of(new RevId(rev), comment));
+ .containsExactlyEntriesIn(ImmutableListMultimap.of(commitId, comment));
}
@Test
public void putCommentsForMultipleRevisions() throws Exception {
Change c = newChange();
String uuid = "uuid";
- String rev1 = "abcd1234abcd1234abcd1234abcd1234abcd1234";
- String rev2 = "abcd4567abcd4567abcd4567abcd4567abcd4567";
+ ObjectId commitId1 = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId2 = ObjectId.fromString("abcd4567abcd4567abcd4567abcd4567abcd4567");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
String filename = "filename1";
@@ -2432,7 +2455,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev1,
+ commitId1,
false);
Comment comment2 =
newComment(
@@ -2446,10 +2469,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps2",
side,
- rev2,
+ commitId2,
false);
- update.putComment(Status.DRAFT, comment1);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
ChangeNotes notes = newNotes(c);
@@ -2458,8 +2481,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update = newUpdate(c, otherUser);
update.setPatchSetId(ps2);
- update.putComment(Status.PUBLISHED, comment1);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
notes = newNotes(c);
@@ -2470,7 +2493,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
@Test
public void publishSubsetOfCommentsOnRevision() throws Exception {
Change c = newChange();
- RevId rev1 = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId1 = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
short side = (short) 1;
@@ -2490,7 +2513,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment1",
side,
- rev1.get(),
+ commitId1,
false);
Comment comment2 =
newComment(
@@ -2504,24 +2527,25 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment2",
side,
- rev1.get(),
+ commitId1,
false);
- update.putComment(Status.DRAFT, comment1);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
ChangeNotes notes = newNotes(c);
- assertThat(notes.getDraftComments(otherUserId).get(rev1)).containsExactly(comment1, comment2);
+ assertThat(notes.getDraftComments(otherUserId).get(commitId1))
+ .containsExactly(comment1, comment2);
assertThat(notes.getComments()).isEmpty();
update = newUpdate(c, otherUser);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
notes = newNotes(c);
- assertThat(notes.getDraftComments(otherUserId).get(rev1)).containsExactly(comment1);
- assertThat(notes.getComments().get(rev1)).containsExactly(comment2);
+ assertThat(notes.getDraftComments(otherUserId).get(commitId1)).containsExactly(comment1);
+ assertThat(notes.getComments().get(commitId1)).containsExactly(comment2);
}
@Test
@@ -2535,15 +2559,15 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(msg.getMessage()).isEqualTo("A message.");
assertThat(msg.getAuthor()).isNull();
- update = newUpdate(c, internalUser);
- exception.expect(IllegalStateException.class);
- update.putApproval("Code-Review", (short) 1);
+ ChangeUpdate failingUpdate = newUpdate(c, internalUser);
+ assertThrows(
+ IllegalStateException.class, () -> failingUpdate.putApproval("Code-Review", (short) 1));
}
@Test
public void filterOutAndFixUpZombieDraftComments() throws Exception {
Change c = newChange();
- RevId rev1 = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
+ ObjectId commitId1 = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
CommentRange range = new CommentRange(1, 1, 2, 1);
PatchSet.Id ps1 = c.currentPatchSetId();
short side = (short) 1;
@@ -2562,7 +2586,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"comment on ps1",
side,
- rev1.get(),
+ commitId1,
false);
Comment comment2 =
newComment(
@@ -2576,10 +2600,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
now,
"another comment",
side,
- rev1.get(),
+ commitId1,
false);
- update.putComment(Status.DRAFT, comment1);
- update.putComment(Status.DRAFT, comment2);
+ update.putComment(Comment.Status.DRAFT, comment1);
+ update.putComment(Comment.Status.DRAFT, comment2);
update.commit();
String refName = refsDraftComments(c.getId(), otherUserId);
@@ -2587,7 +2611,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update = newUpdate(c, otherUser);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment2);
+ update.putComment(Comment.Status.PUBLISHED, comment2);
update.commit();
assertThat(exactRefAllUsers(refName)).isNotNull();
assertThat(exactRefAllUsers(refName)).isNotEqualTo(oldDraftId);
@@ -2604,16 +2628,16 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
// Looking at drafts directly shows the zombie comment.
DraftCommentNotes draftNotes = draftNotesFactory.create(c.getId(), otherUserId);
- assertThat(draftNotes.load().getComments().get(rev1)).containsExactly(comment1, comment2);
+ assertThat(draftNotes.load().getComments().get(commitId1)).containsExactly(comment1, comment2);
// Zombie comment is filtered out of drafts via ChangeNotes.
ChangeNotes notes = newNotes(c);
- assertThat(notes.getDraftComments(otherUserId).get(rev1)).containsExactly(comment1);
- assertThat(notes.getComments().get(rev1)).containsExactly(comment2);
+ assertThat(notes.getDraftComments(otherUserId).get(commitId1)).containsExactly(comment1);
+ assertThat(notes.getComments().get(commitId1)).containsExactly(comment2);
update = newUpdate(c, otherUser);
update.setPatchSetId(ps1);
- update.putComment(Status.PUBLISHED, comment1);
+ update.putComment(Comment.Status.PUBLISHED, comment1);
update.commit();
// Updating an unrelated comment causes the zombie comment to get fixed up.
@@ -2624,7 +2648,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void updateCommentsInSequentialUpdates() throws Exception {
Change c = newChange();
CommentRange range = new CommentRange(1, 1, 2, 1);
- String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
+ ObjectId commitId = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
ChangeUpdate update1 = newUpdate(c, otherUser);
Comment comment1 =
@@ -2639,9 +2663,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
new Timestamp(update1.getWhen().getTime()),
"comment 1",
(short) 1,
- rev,
+ commitId,
false);
- update1.putComment(Status.PUBLISHED, comment1);
+ update1.putComment(Comment.Status.PUBLISHED, comment1);
ChangeUpdate update2 = newUpdate(c, otherUser);
Comment comment2 =
@@ -2656,9 +2680,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
new Timestamp(update2.getWhen().getTime()),
"comment 2",
(short) 1,
- rev,
+ commitId,
false);
- update2.putComment(Status.PUBLISHED, comment2);
+ update2.putComment(Comment.Status.PUBLISHED, comment2);
try (NoteDbUpdateManager manager = updateManagerFactory.create(project)) {
manager.add(update1);
@@ -2667,7 +2691,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
}
ChangeNotes notes = newNotes(c);
- List<Comment> comments = notes.getComments().get(new RevId(rev));
+ List<Comment> comments = notes.getComments().get(commitId);
assertThat(comments).hasSize(2);
assertThat(comments.get(0).message).isEqualTo("comment 1");
assertThat(comments.get(1).message).isEqualTo("comment 2");
@@ -2697,7 +2721,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
int numComments = notes.getComments().size();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.setPatchSetId(new PatchSet.Id(c.getId(), c.currentPatchSetId().get() + 1));
+ update.setPatchSetId(PatchSet.id(c.getId(), c.currentPatchSetId().get() + 1));
update.setChangeMessage("Should be ignored");
update.putApproval("Code-Review", (short) 2);
CommentRange range = new CommentRange(1, 1, 2, 1);
@@ -2713,9 +2737,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
new Timestamp(update.getWhen().getTime()),
"comment",
(short) 1,
- "abcd1234abcd1234abcd1234abcd1234abcd1234",
+ ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"),
false);
- update.putComment(Status.PUBLISHED, comment);
+ update.putComment(Comment.Status.PUBLISHED, comment);
update.commit();
notes = newNotes(c);
@@ -2734,7 +2758,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(2);
ChangeUpdate update = newUpdate(c, changeOwner);
- update.setPatchSetId(new PatchSet.Id(c.getId(), 1));
+ update.setPatchSetId(PatchSet.id(c.getId(), 1));
update.setCurrentPatchSet();
update.commit();
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(1);
@@ -2751,7 +2775,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
// Delete PS1, PS2 becomes current.
update = newUpdate(c, changeOwner);
- update.setPatchSetId(new PatchSet.Id(c.getId(), 1));
+ update.setPatchSetId(PatchSet.id(c.getId(), 1));
update.setPatchSetState(PatchSetState.DELETED);
update.commit();
assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(2);
@@ -2930,8 +2954,8 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void pendingReviewers() throws Exception {
Address adr1 = new Address("Foo Bar1", "foo.bar1@gerritcodereview.com");
Address adr2 = new Address("Foo Bar2", "foo.bar2@gerritcodereview.com");
- Account.Id ownerId = changeOwner.getAccount().getId();
- Account.Id otherUserId = otherUser.getAccount().getId();
+ Account.Id ownerId = changeOwner.getAccount().id();
+ Account.Id otherUserId = otherUser.getAccount().id();
ChangeNotes notes = newNotes(newChange());
assertThat(notes.getPendingReviewers().asTable()).isEmpty();
@@ -3006,9 +3030,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void setRevertOfToCurrentChangeFails() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage("A change cannot revert itself");
- update.setRevertOf(c.getId().get());
+ IllegalArgumentException thrown =
+ assertThrows(IllegalArgumentException.class, () -> update.setRevertOf(c.getId().get()));
+ assertThat(thrown).hasMessageThat().contains("A change cannot revert itself");
}
@Test
@@ -3016,9 +3040,58 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
update.setRevertOf(newChange().getId().get());
- exception.expect(StorageException.class);
- exception.expectMessage("Given ChangeUpdate is only allowed on initial commit");
+ StorageException thrown = assertThrows(StorageException.class, () -> update.commit());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Given ChangeUpdate is only allowed on initial commit");
+ }
+
+ @Test
+ public void updateCount() throws Exception {
+ Change c = newChange();
+ assertThat(newNotes(c).getUpdateCount()).isEqualTo(1);
+
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ update.putApproval("Code-Review", (short) -1);
+ update.commit();
+ assertThat(newNotes(c).getUpdateCount()).isEqualTo(2);
+
+ update = newUpdate(c, changeOwner);
+ update.putApproval("Code-Review", (short) 1);
+ update.commit();
+ assertThat(newNotes(c).getUpdateCount()).isEqualTo(3);
+ }
+
+ @Test
+ public void createPatchSetAfterPatchSetDeletion() throws Exception {
+ Change c = newChange();
+ assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(1);
+
+ // Create PS2.
+ incrementCurrentPatchSetFieldOnly(c);
+ RevCommit commit = tr.commit().message("PS" + c.currentPatchSetId().get()).create();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ update.setCommit(rw, commit);
+ update.setGroups(ImmutableList.of(commit.name()));
+ update.commit();
+ assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(2);
+
+ // Delete PS2.
+ update = newUpdate(c, changeOwner);
+ update.setPatchSetState(PatchSetState.DELETED);
update.commit();
+ c = newNotes(c).getChange();
+ assertThat(c.currentPatchSetId().get()).isEqualTo(1);
+
+ // Create another PS2
+ incrementCurrentPatchSetFieldOnly(c);
+ commit = tr.commit().message("PS" + c.currentPatchSetId().get()).create();
+ update = newUpdate(c, changeOwner);
+ update.setPatchSetState(PatchSetState.PUBLISHED);
+ update.setCommit(rw, commit);
+ update.setGroups(ImmutableList.of(commit.name()));
+ update.commit();
+ assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(2);
}
private String readNote(ChangeNotes notes, ObjectId noteId) throws Exception {
@@ -3042,11 +3115,11 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
break;
}
}
- assertThat(cause)
- .named(
+ assertWithMessage(
expectedClass.getSimpleName()
+ " in causal chain of:\n"
+ Throwables.getStackTraceAsString(e))
+ .that(cause)
.isNotNull();
assertThat(cause.getMessage()).isEqualTo(expectedMsg);
}
diff --git a/javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java b/javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java
index 4dd2005170..c2620dc5cf 100644
--- a/javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java
@@ -16,20 +16,20 @@ package com.google.gerrit.server.notedb;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Comment;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.sql.Timestamp;
import java.time.ZonedDateTime;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.lib.ObjectId;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class CommentTimestampAdapterTest extends GerritBaseTests {
+public class CommentTimestampAdapterTest {
/** Arbitrary time outside of a DST transition, as an ISO instant. */
private static final String NON_DST_STR = "2017-02-07T10:20:30.123Z";
@@ -153,14 +153,14 @@ public class CommentTimestampAdapterTest extends GerritBaseTests {
Comment c =
new Comment(
new Comment.Key("uuid", "filename", 1),
- new Account.Id(100),
+ Account.id(100),
NON_DST_TS,
(short) 0,
"message",
"serverId",
false);
c.lineNbr = 1;
- c.revId = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ c.setCommitId(ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
String json = gson.toJson(c);
assertThat(json).contains("\"writtenOn\": \"" + NON_DST_STR_TRUNC + "\",");
diff --git a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
index 5552572a37..97781a4a34 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
@@ -19,9 +19,9 @@ import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
import com.google.gerrit.mail.Address;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -44,8 +44,8 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.putApproval("Verified", (short) 1);
update.putApproval("Code-Review", (short) -1);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
- update.putReviewer(otherUser.getAccount().getId(), CC);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
+ update.putReviewer(otherUser.getAccount().id(), CC);
update.commit();
assertThat(update.getRefName()).isEqualTo("refs/changes/01/1/meta");
@@ -199,10 +199,13 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
@Test
public void anonymousUser() throws Exception {
- Account anon = new Account(new Account.Id(3), TimeUtil.nowTs());
+ Account anon =
+ Account.builder(Account.id(3), TimeUtil.nowTs())
+ .setMetaId("1234567812345678123456781234567812345678")
+ .build();
accountCache.put(anon);
Change c = newChange();
- ChangeUpdate update = newUpdate(c, userFactory.create(anon.getId()));
+ ChangeUpdate update = newUpdate(c, userFactory.create(anon.id()));
update.setChangeMessage("Comment on the change.");
update.commit();
@@ -241,7 +244,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
public void noChangeMessage() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
- update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
+ update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
update.commit();
assertBodyEquals(
@@ -311,7 +314,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
c.setCurrentPatchSet(c.currentPatchSetId(), " " + c.getSubject(), c.getOriginalSubject());
ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
- update.setBranch(c.getDest().get());
+ update.setBranch(c.getDest().branch());
update.commit();
assertBodyEquals(
@@ -332,7 +335,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
c.setCurrentPatchSet(c.currentPatchSetId(), "\t\t" + c.getSubject(), c.getOriginalSubject());
update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
- update.setBranch(c.getDest().get());
+ update.setBranch(c.getDest().branch());
update.commit();
assertBodyEquals(
diff --git a/javatests/com/google/gerrit/server/notedb/DraftCommentNotesTest.java b/javatests/com/google/gerrit/server/notedb/DraftCommentNotesTest.java
new file mode 100644
index 0000000000..bf498841c4
--- /dev/null
+++ b/javatests/com/google/gerrit/server/notedb/DraftCommentNotesTest.java
@@ -0,0 +1,98 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.notedb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Comment;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.server.util.time.TimeUtil;
+import org.eclipse.jgit.lib.ObjectId;
+import org.junit.Test;
+
+public class DraftCommentNotesTest extends AbstractChangeNotesTest {
+
+ @Test
+ public void createAndPublishCommentInOneAction_runsDraftOperationAsynchronously()
+ throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, otherUser);
+ update.setPatchSetId(c.currentPatchSetId());
+ update.putComment(Comment.Status.PUBLISHED, comment(c.currentPatchSetId()));
+ update.commit();
+
+ assertThat(newNotes(c).getDraftComments(otherUserId)).isEmpty();
+ assertableFanOutExecutor.assertInteractions(1);
+ }
+
+ @Test
+ public void createAndPublishComment_runsPublishDraftOperationAsynchronously() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, otherUser);
+
+ update.setPatchSetId(c.currentPatchSetId());
+ update.putComment(Comment.Status.DRAFT, comment(c.currentPatchSetId()));
+ update.commit();
+ assertThat(newNotes(c).getDraftComments(otherUserId)).hasSize(1);
+ assertableFanOutExecutor.assertInteractions(0);
+
+ update = newUpdate(c, otherUser);
+ update.putComment(Comment.Status.PUBLISHED, comment(c.currentPatchSetId()));
+ update.commit();
+
+ assertThat(newNotes(c).getDraftComments(otherUserId)).isEmpty();
+ assertableFanOutExecutor.assertInteractions(1);
+ }
+
+ @Test
+ public void createAndDeleteDraftComment_runsDraftOperationSynchronously() throws Exception {
+ Change c = newChange();
+
+ ChangeUpdate update = newUpdate(c, otherUser);
+ update.setPatchSetId(c.currentPatchSetId());
+ update.putComment(Comment.Status.DRAFT, comment(c.currentPatchSetId()));
+ update.commit();
+
+ ChangeNotes notes = newNotes(c);
+ assertThat(notes.getDraftComments(otherUserId)).hasSize(1);
+ assertableFanOutExecutor.assertInteractions(0);
+
+ update = newUpdate(c, otherUser);
+ update.setPatchSetId(c.currentPatchSetId());
+ update.deleteComment(comment(c.currentPatchSetId()));
+ update.commit();
+
+ notes = newNotes(c);
+ assertThat(notes.getDraftComments(otherUserId)).isEmpty();
+ assertableFanOutExecutor.assertInteractions(0);
+ }
+
+ private Comment comment(PatchSet.Id psId) {
+ return newComment(
+ psId,
+ "filename",
+ "uuid",
+ null,
+ 0,
+ otherUser,
+ null,
+ TimeUtil.nowTs(),
+ "comment",
+ (short) 0,
+ ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"),
+ false);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/notedb/IntBlobTest.java b/javatests/com/google/gerrit/server/notedb/IntBlobTest.java
index 1cbe61de96..333c229147 100644
--- a/javatests/com/google/gerrit/server/notedb/IntBlobTest.java
+++ b/javatests/com/google/gerrit/server/notedb/IntBlobTest.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.notedb;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static com.google.gerrit.truth.OptionalSubject.assertThat;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.git.LockFailureException;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import java.io.IOException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -44,7 +44,7 @@ public class IntBlobTest {
@Before
public void setUp() throws Exception {
- projectName = new Project.NameKey("repo");
+ projectName = Project.nameKey("repo");
repo = new InMemoryRepository(new DfsRepositoryDescription(projectName.get()));
tr = new TestRepository<>(repo);
rw = tr.getRevWalk();
@@ -59,12 +59,7 @@ public class IntBlobTest {
public void parseNonBlob() throws Exception {
String refName = "refs/foo/master";
tr.branch(refName).commit().create();
- try {
- IntBlob.parse(repo, refName);
- assert_().fail("Expected IncorrectObjectTypeException");
- } catch (IncorrectObjectTypeException e) {
- // Expected.
- }
+ assertThrows(IncorrectObjectTypeException.class, () -> IntBlob.parse(repo, refName));
}
@Test
@@ -85,12 +80,9 @@ public class IntBlobTest {
public void parseInvalid() throws Exception {
String refName = "refs/foo";
ObjectId id = tr.update(refName, tr.blob("1 2 3"));
- try {
- IntBlob.parse(repo, refName);
- assert_().fail("Expected StorageException");
- } catch (StorageException e) {
- assertThat(e).hasMessageThat().isEqualTo("invalid value in refs/foo blob at " + id.name());
- }
+ StorageException thrown =
+ assertThrows(StorageException.class, () -> IntBlob.parse(repo, refName));
+ assertThat(thrown).hasMessageThat().isEqualTo("invalid value in refs/foo blob at " + id.name());
}
@Test
@@ -180,19 +172,19 @@ public class IntBlobTest {
@Test
public void storeWrongOldId() throws Exception {
String refName = "refs/foo";
- try {
- IntBlob.store(
- repo,
- rw,
- projectName,
- refName,
- ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
- 123,
- GitReferenceUpdated.DISABLED);
- assert_().fail("expected LockFailureException");
- } catch (LockFailureException e) {
- assertThat(e.getFailedRefs()).containsExactly("refs/foo");
- }
+ LockFailureException thrown =
+ assertThrows(
+ LockFailureException.class,
+ () ->
+ IntBlob.store(
+ repo,
+ rw,
+ projectName,
+ refName,
+ ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+ 123,
+ GitReferenceUpdated.DISABLED));
+ assertThat(thrown.getFailedRefs()).containsExactly("refs/foo");
assertThat(IntBlob.parse(repo, refName)).isEmpty();
}
diff --git a/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java b/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
index 74cc507e27..a768eaf02d 100644
--- a/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
+++ b/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
@@ -15,22 +15,27 @@
package com.google.gerrit.server.notedb;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-import static org.junit.Assert.fail;
+import com.github.rholder.retry.BlockStrategy;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
+import com.google.common.collect.ImmutableList;
+import com.google.common.truth.Expect;
import com.google.common.util.concurrent.Runnables;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import java.io.IOException;
-import java.util.concurrent.ExecutionException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -41,11 +46,14 @@ import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
-public class RepoSequenceTest extends GerritBaseTests {
+public class RepoSequenceTest {
+ @Rule public final Expect expect = Expect.create();
+
// Don't sleep in tests.
- private static final Retryer<RefUpdate> RETRYER =
+ private static final Retryer<ImmutableList<Integer>> RETRYER =
RepoSequence.retryerBuilder().withBlockStrategy(t -> {}).build();
private InMemoryRepositoryManager repoManager;
@@ -54,7 +62,7 @@ public class RepoSequenceTest extends GerritBaseTests {
@Before
public void setUp() throws Exception {
repoManager = new InMemoryRepositoryManager();
- project = new Project.NameKey("project");
+ project = Project.nameKey("project");
repoManager.createRepository(project);
}
@@ -66,13 +74,13 @@ public class RepoSequenceTest extends GerritBaseTests {
RepoSequence s = newSequence(name, 1, batchSize);
for (int i = 1; i <= max; i++) {
try {
- assertThat(s.next()).named("i=" + i + " for " + name).isEqualTo(i);
+ assertWithMessage("i=" + i + " for " + name).that(s.next()).isEqualTo(i);
} catch (StorageException e) {
throw new AssertionError("failed batchSize=" + batchSize + ", i=" + i, e);
}
}
- assertThat(s.acquireCount)
- .named("acquireCount for " + name)
+ assertWithMessage("acquireCount for " + name)
+ .that(s.acquireCount)
.isEqualTo(divCeil(max, batchSize));
}
}
@@ -160,7 +168,7 @@ public class RepoSequenceTest extends GerritBaseTests {
RepoSequence s = newSequence("id", 1, 10, bgUpdate, RETRYER);
assertThat(doneBgUpdate.get()).isFalse();
assertThat(s.next()).isEqualTo(1234);
- // Single acquire call that results in 2 ref reads.
+ // Two acquire calls, but only one successful.
assertThat(s.acquireCount).isEqualTo(1);
assertThat(doneBgUpdate.get()).isTrue();
}
@@ -168,9 +176,11 @@ public class RepoSequenceTest extends GerritBaseTests {
@Test
public void failOnInvalidValue() throws Exception {
ObjectId id = writeBlob("id", "not a number");
- exception.expect(StorageException.class);
- exception.expectMessage("invalid value in refs/sequences/id blob at " + id.name());
- newSequence("id", 1, 3).next();
+ StorageException thrown =
+ assertThrows(StorageException.class, () -> newSequence("id", 1, 3).next());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("invalid value in refs/sequences/id blob at " + id.name());
}
@Test
@@ -178,13 +188,9 @@ public class RepoSequenceTest extends GerritBaseTests {
try (Repository repo = repoManager.openRepository(project);
TestRepository<Repository> tr = new TestRepository<>(repo)) {
tr.branch(RefNames.REFS_SEQUENCES + "id").commit().create();
- try {
- newSequence("id", 1, 3).next();
- fail();
- } catch (StorageException e) {
- assertThat(e.getCause()).isInstanceOf(ExecutionException.class);
- assertThat(e.getCause().getCause()).isInstanceOf(IncorrectObjectTypeException.class);
- }
+ StorageException e =
+ assertThrows(StorageException.class, () -> newSequence("id", 1, 3).next());
+ assertThat(e.getCause()).isInstanceOf(IncorrectObjectTypeException.class);
}
}
@@ -197,12 +203,84 @@ public class RepoSequenceTest extends GerritBaseTests {
1,
10,
() -> writeBlob("id", Integer.toString(bgCounter.getAndAdd(1000))),
- RetryerBuilder.<RefUpdate>newBuilder()
+ RetryerBuilder.<ImmutableList<Integer>>newBuilder()
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build());
- exception.expect(StorageException.class);
- exception.expectMessage("Failed to update refs/sequences/id: LOCK_FAILURE");
- s.next();
+ StorageException thrown = assertThrows(StorageException.class, () -> s.next());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Failed to update refs/sequences/id: LOCK_FAILURE");
+ }
+
+ @Test
+ public void idCanBeRetrievedFromOtherThreadWhileWaitingToRetry() throws Exception {
+ // Seed existing ref value.
+ writeBlob("id", "1");
+
+ // Let the first update of the sequence fail with LOCK_FAILURE, so that the update is retried.
+ CountDownLatch lockFailure = new CountDownLatch(1);
+ CountDownLatch parallelSuccessfulSequenceGeneration = new CountDownLatch(1);
+ AtomicBoolean doneBgUpdate = new AtomicBoolean(false);
+ Runnable bgUpdate =
+ () -> {
+ if (!doneBgUpdate.getAndSet(true)) {
+ writeBlob("id", "1234");
+ }
+ };
+
+ BlockStrategy blockStrategy =
+ t -> {
+ // Keep blocking until we verified that another thread can retrieve a sequence number
+ // while we are blocking here.
+ lockFailure.countDown();
+ parallelSuccessfulSequenceGeneration.await();
+ };
+
+ // Use batch size = 1 to make each call go to NoteDb.
+ RepoSequence s =
+ newSequence(
+ "id",
+ 1,
+ 1,
+ bgUpdate,
+ RepoSequence.retryerBuilder().withBlockStrategy(blockStrategy).build());
+
+ assertThat(doneBgUpdate.get()).isFalse();
+
+ // Start a thread to get a sequence number. This thread needs to update the sequence in NoteDb,
+ // but due to the background update (see bgUpdate) the first attempt to update NoteDb fails
+ // with LOCK_FAILURE. RepoSequence uses a retryer to retry the NoteDb update on LOCK_FAILURE,
+ // but our block strategy ensures that this retry only happens after isBlocking was set to
+ // false.
+ Future<?> future =
+ Executors.newFixedThreadPool(1)
+ .submit(
+ () -> {
+ // The background update sets the next available sequence number to 1234. Then the
+ // test thread retrieves one sequence number, so that the next available sequence
+ // number for this thread is 1235.
+ expect.that(s.next()).isEqualTo(1235);
+ });
+
+ // Wait until the LOCK_FAILURE has happened and the block strategy was entered.
+ lockFailure.await();
+
+ // Verify that the background update was done now.
+ assertThat(doneBgUpdate.get()).isTrue();
+
+ // Verify that we can retrieve a sequence number while the other thread is blocked. If the
+ // s.next() call hangs it means that the RepoSequence.counterLock was not released before the
+ // background thread started to block for retry. In this case the test would time out.
+ assertThat(s.next()).isEqualTo(1234);
+
+ // Stop blocking the retry of the background thread (and verify that it was still blocked).
+ parallelSuccessfulSequenceGeneration.countDown();
+
+ // Wait until the background thread is done.
+ future.get();
+
+ // Two successful acquire calls (because batch size == 1).
+ assertThat(s.acquireCount).isEqualTo(2);
}
@Test
@@ -260,7 +338,7 @@ public class RepoSequenceTest extends GerritBaseTests {
final int start,
int batchSize,
Runnable afterReadRef,
- Retryer<RefUpdate> retryer) {
+ Retryer<ImmutableList<Integer>> retryer) {
return new RepoSequence(
repoManager,
GitReferenceUpdated.DISABLED,
diff --git a/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java b/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
index 6c63c5f6a0..52a81ade12 100644
--- a/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
+++ b/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
@@ -15,18 +15,18 @@
package com.google.gerrit.server.patch;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.jgit.diff.ReplaceEdit;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.List;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.diff.EditList;
import org.junit.Test;
-public class IntraLineLoaderTest extends GerritBaseTests {
+public class IntraLineLoaderTest {
@Test
public void rewriteAtStartOfLineIsRecognized() throws Exception {
@@ -88,22 +88,30 @@ public class IntraLineLoaderTest extends GerritBaseTests {
// TODO: expected failure
// the current code does not work on the first line
// and the insert marker is in the wrong location
- @Test(expected = AssertionError.class)
+ @Test
public void preferInsertAtLineBreak2() throws Exception {
- String a = " abc\n def\n";
- String b = " abc\n def\n";
- assertThat(intraline(a, b))
- .isEqualTo(ref().insert(" ").common(" abc\n").insert(" ").common(" def\n").edits);
+ assertThrows(
+ AssertionError.class,
+ () -> {
+ String a = " abc\n def\n";
+ String b = " abc\n def\n";
+ assertThat(intraline(a, b))
+ .isEqualTo(ref().insert(" ").common(" abc\n").insert(" ").common(" def\n").edits);
+ });
}
// TODO: expected failure
// the current code does not work on the first line
- @Test(expected = AssertionError.class)
+ @Test
public void preferDeleteAtLineBreak() throws Exception {
- String a = " abc\n def\n";
- String b = " abc\n def\n";
- assertThat(intraline(a, b))
- .isEqualTo(ref().remove(" ").common(" abc\n").remove(" ").common(" def\n").edits);
+ assertThrows(
+ AssertionError.class,
+ () -> {
+ String a = " abc\n def\n";
+ String b = " abc\n def\n";
+ assertThat(intraline(a, b))
+ .isEqualTo(ref().remove(" ").common(" abc\n").remove(" ").common(" def\n").edits);
+ });
}
@Test
diff --git a/javatests/com/google/gerrit/server/patch/PatchListEntryTest.java b/javatests/com/google/gerrit/server/patch/PatchListEntryTest.java
index 4ed5f4b299..7ac1c3162b 100644
--- a/javatests/com/google/gerrit/server/patch/PatchListEntryTest.java
+++ b/javatests/com/google/gerrit/server/patch/PatchListEntryTest.java
@@ -19,11 +19,10 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Patch;
import org.junit.Test;
-public class PatchListEntryTest extends GerritBaseTests {
+public class PatchListEntryTest {
@Test
public void empty1() {
final String name = "empty-file";
diff --git a/javatests/com/google/gerrit/server/patch/PatchListTest.java b/javatests/com/google/gerrit/server/patch/PatchListTest.java
index ccdd040195..e224191a5a 100644
--- a/javatests/com/google/gerrit/server/patch/PatchListTest.java
+++ b/javatests/com/google/gerrit/server/patch/PatchListTest.java
@@ -16,8 +16,7 @@ package com.google.gerrit.server.patch;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Patch;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
@@ -26,7 +25,7 @@ import java.io.ObjectOutputStream;
import java.util.Arrays;
import org.junit.Test;
-public class PatchListTest extends GerritBaseTests {
+public class PatchListTest {
@Test
public void fileOrder() {
String[] names = {
diff --git a/javatests/com/google/gerrit/server/permissions/DefaultPermissionsMappingTest.java b/javatests/com/google/gerrit/server/permissions/DefaultPermissionsMappingTest.java
index ff9ac411f7..305e81bf1c 100644
--- a/javatests/com/google/gerrit/server/permissions/DefaultPermissionsMappingTest.java
+++ b/javatests/com/google/gerrit/server/permissions/DefaultPermissionsMappingTest.java
@@ -18,10 +18,9 @@ import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.server.permissions.DefaultPermissionMappings.refPermission;
import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class DefaultPermissionsMappingTest extends GerritBaseTests {
+public class DefaultPermissionsMappingTest {
@Test
public void stringToRefPermission() {
assertThat(refPermission("doesnotexist")).isEmpty();
diff --git a/javatests/com/google/gerrit/server/permissions/PluginPermissionsUtilTest.java b/javatests/com/google/gerrit/server/permissions/PluginPermissionsUtilTest.java
index f40c3bc1f3..7aa73a74e2 100644
--- a/javatests/com/google/gerrit/server/permissions/PluginPermissionsUtilTest.java
+++ b/javatests/com/google/gerrit/server/permissions/PluginPermissionsUtilTest.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.permissions;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.server.permissions.PluginPermissionsUtil.isValidPluginPermission;
import com.google.common.collect.ImmutableList;
@@ -30,8 +30,8 @@ public final class PluginPermissionsUtilTest {
ImmutableList.of("plugin-foo-a", "plugin-foo-a-b");
for (String permission : validPluginPermissions) {
- assertThat(isValidPluginPermission(permission))
- .named("valid plugin permission: %s", permission)
+ assertWithMessage("valid plugin permission: %s", permission)
+ .that(isValidPluginPermission(permission))
.isTrue();
}
}
@@ -48,8 +48,8 @@ public final class PluginPermissionsUtilTest {
"plugin-foo-a1");
for (String permission : invalidPluginPermissions) {
- assertThat(isValidPluginPermission(permission))
- .named("invalid plugin permission: %s", permission)
+ assertWithMessage("invalid plugin permission: %s", permission)
+ .that(isValidPluginPermission(permission))
.isFalse();
}
}
diff --git a/javatests/com/google/gerrit/server/permissions/RefControlTest.java b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
index 58adc0c6c9..43a3f10111 100644
--- a/javatests/com/google/gerrit/server/permissions/RefControlTest.java
+++ b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
@@ -15,268 +15,179 @@
package com.google.gerrit.server.permissions;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.blockLabel;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.labelPermissionKey;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
import static com.google.gerrit.common.data.Permission.EDIT_TOPIC_NAME;
import static com.google.gerrit.common.data.Permission.LABEL;
import static com.google.gerrit.common.data.Permission.OWNER;
import static com.google.gerrit.common.data.Permission.PUSH;
import static com.google.gerrit.common.data.Permission.READ;
import static com.google.gerrit.common.data.Permission.SUBMIT;
+import static com.google.gerrit.entities.RefNames.REFS_CONFIG;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.CHANGE_OWNER;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.project.testing.Util.ADMIN;
-import static com.google.gerrit.server.project.testing.Util.DEVS;
-import static com.google.gerrit.server.project.testing.Util.allow;
-import static com.google.gerrit.server.project.testing.Util.allowExclusive;
-import static com.google.gerrit.server.project.testing.Util.block;
-import static com.google.gerrit.server.project.testing.Util.deny;
-import static com.google.gerrit.server.project.testing.Util.doNotInherit;
-import static com.google.gerrit.testing.InMemoryRepositoryManager.newRepository;
-
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.ImmutableSortedSet;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
import com.google.common.collect.Lists;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.PermissionRange;
-import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.InvalidNameException;
-import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.CapabilityCollection;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.account.ListGroupMembership;
import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllProjectsNameProvider;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AllUsersNameProvider;
-import com.google.gerrit.server.git.TransferConfig;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.index.SingleVersionModule.SingleVersionListener;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefPattern;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryModule;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import java.util.Optional;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Repository;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class RefControlTest extends GerritBaseTests {
- private void assertAdminsAreOwnersAndDevsAreNot() {
- ProjectControl uBlah = user(local, DEVS);
- ProjectControl uAdmin = user(local, DEVS, ADMIN);
+public class RefControlTest {
+ private static final AccountGroup.UUID ADMIN = AccountGroup.uuid("test.admin");
+ private static final AccountGroup.UUID DEVS = AccountGroup.uuid("test.devs");
+
+ private void assertAdminsAreOwnersAndDevsAreNot() throws Exception {
+ ProjectControl uBlah = user(localKey, DEVS);
+ ProjectControl uAdmin = user(localKey, DEVS, ADMIN);
- assertThat(uBlah.isOwner()).named("not owner").isFalse();
- assertThat(uAdmin.isOwner()).named("is owner").isTrue();
+ assertWithMessage("not owner").that(uBlah.isOwner()).isFalse();
+ assertWithMessage("is owner").that(uAdmin.isOwner()).isTrue();
}
private void assertOwner(String ref, ProjectControl u) {
- assertThat(u.controlForRef(ref).isOwner()).named("OWN " + ref).isTrue();
+ assertWithMessage("OWN " + ref).that(u.controlForRef(ref).isOwner()).isTrue();
}
private void assertNotOwner(ProjectControl u) {
- assertThat(u.isOwner()).named("not owner").isFalse();
+ assertWithMessage("not owner").that(u.isOwner()).isFalse();
}
private void assertNotOwner(String ref, ProjectControl u) {
- assertThat(u.controlForRef(ref).isOwner()).named("NOT OWN " + ref).isFalse();
+ assertWithMessage("NOT OWN " + ref).that(u.controlForRef(ref).isOwner()).isFalse();
}
private void assertCanAccess(ProjectControl u) {
boolean access = u.asForProject().testOrFalse(ProjectPermission.ACCESS);
- assertThat(access).named("can access").isTrue();
+ assertWithMessage("can access").that(access).isTrue();
}
private void assertAccessDenied(ProjectControl u) {
boolean access = u.asForProject().testOrFalse(ProjectPermission.ACCESS);
- assertThat(access).named("cannot access").isFalse();
+ assertWithMessage("cannot access").that(access).isFalse();
}
private void assertCanRead(String ref, ProjectControl u) {
- assertThat(u.controlForRef(ref).isVisible()).named("can read " + ref).isTrue();
+ assertWithMessage("can read " + ref).that(u.controlForRef(ref).isVisible()).isTrue();
}
private void assertCannotRead(String ref, ProjectControl u) {
- assertThat(u.controlForRef(ref).isVisible()).named("cannot read " + ref).isFalse();
+ assertWithMessage("cannot read " + ref).that(u.controlForRef(ref).isVisible()).isFalse();
}
private void assertCanSubmit(String ref, ProjectControl u) {
- assertThat(u.controlForRef(ref).canSubmit(false)).named("can submit " + ref).isTrue();
+ assertWithMessage("can submit " + ref).that(u.controlForRef(ref).canSubmit(false)).isTrue();
}
private void assertCannotSubmit(String ref, ProjectControl u) {
- assertThat(u.controlForRef(ref).canSubmit(false)).named("can submit " + ref).isFalse();
+ assertWithMessage("can submit " + ref).that(u.controlForRef(ref).canSubmit(false)).isFalse();
}
private void assertCanUpload(ProjectControl u) {
- assertThat(u.canPushToAtLeastOneRef()).named("can upload").isTrue();
+ assertWithMessage("can upload").that(u.canPushToAtLeastOneRef()).isTrue();
}
private void assertCreateChange(String ref, ProjectControl u) {
boolean create = u.asForProject().ref(ref).testOrFalse(RefPermission.CREATE_CHANGE);
- assertThat(create).named("can create change " + ref).isTrue();
+ assertWithMessage("can create change " + ref).that(create).isTrue();
}
private void assertCannotUpload(ProjectControl u) {
- assertThat(u.canPushToAtLeastOneRef()).named("cannot upload").isFalse();
+ assertWithMessage("cannot upload").that(u.canPushToAtLeastOneRef()).isFalse();
}
private void assertCannotCreateChange(String ref, ProjectControl u) {
boolean create = u.asForProject().ref(ref).testOrFalse(RefPermission.CREATE_CHANGE);
- assertThat(create).named("cannot create change " + ref).isFalse();
+ assertWithMessage("cannot create change " + ref).that(create).isFalse();
}
private void assertCanUpdate(String ref, ProjectControl u) {
boolean update = u.asForProject().ref(ref).testOrFalse(RefPermission.UPDATE);
- assertThat(update).named("can update " + ref).isTrue();
+ assertWithMessage("can update " + ref).that(update).isTrue();
}
private void assertCannotUpdate(String ref, ProjectControl u) {
boolean update = u.asForProject().ref(ref).testOrFalse(RefPermission.UPDATE);
- assertThat(update).named("cannot update " + ref).isFalse();
+ assertWithMessage("cannot update " + ref).that(update).isFalse();
}
private void assertCanForceUpdate(String ref, ProjectControl u) {
boolean update = u.asForProject().ref(ref).testOrFalse(RefPermission.FORCE_UPDATE);
- assertThat(update).named("can force push " + ref).isTrue();
+ assertWithMessage("can force push " + ref).that(update).isTrue();
}
private void assertCannotForceUpdate(String ref, ProjectControl u) {
boolean update = u.asForProject().ref(ref).testOrFalse(RefPermission.FORCE_UPDATE);
- assertThat(update).named("cannot force push " + ref).isFalse();
+ assertWithMessage("cannot force push " + ref).that(update).isFalse();
}
private void assertCanVote(int score, PermissionRange range) {
- assertThat(range.contains(score)).named("can vote " + score).isTrue();
+ assertWithMessage("can vote " + score).that(range.contains(score)).isTrue();
}
private void assertCannotVote(int score, PermissionRange range) {
- assertThat(range.contains(score)).named("cannot vote " + score).isFalse();
- }
-
- private final AllProjectsName allProjectsName =
- new AllProjectsName(AllProjectsNameProvider.DEFAULT);
- private final AllUsersName allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
- private final AccountGroup.UUID fixers = new AccountGroup.UUID("test.fixers");
- private final Map<Project.NameKey, ProjectState> all = new HashMap<>();
- private Project.NameKey localKey = new Project.NameKey("local");
- private ProjectConfig local;
- private Project.NameKey parentKey = new Project.NameKey("parent");
- private ProjectConfig parent;
- private InMemoryRepositoryManager repoManager;
- private ProjectCache projectCache;
- private PermissionCollection.Factory sectionSorter;
- private ChangeControl.Factory changeControlFactory;
-
- @Inject private PermissionBackend permissionBackend;
- @Inject private CapabilityCollection.Factory capabilityCollectionFactory;
+ assertWithMessage("cannot vote " + score).that(range.contains(score)).isFalse();
+ }
+
+ private final AccountGroup.UUID fixers = AccountGroup.uuid("test.fixers");
+ private final Project.NameKey localKey = Project.nameKey("local");
+ private final Project.NameKey parentKey = Project.nameKey("parent");
+
+ @Inject private AllProjectsName allProjectsName;
+ @Inject private InMemoryRepositoryManager repoManager;
+ @Inject private MetaDataUpdate.Server metaDataUpdateFactory;
+ @Inject private ProjectCache projectCache;
+ @Inject private ProjectControl.Factory projectControlFactory;
+ @Inject private ProjectOperations projectOperations;
@Inject private SchemaCreator schemaCreator;
@Inject private SingleVersionListener singleVersionListener;
@Inject private ThreadLocalRequestContext requestContext;
- @Inject private DefaultRefFilter.Factory refFilterFactory;
- @Inject private TransferConfig transferConfig;
- @Inject private MetricMaker metricMaker;
- @Inject private ProjectConfig.Factory projectConfigFactory;
@Before
public void setUp() throws Exception {
- repoManager = new InMemoryRepositoryManager();
- projectCache =
- new ProjectCache() {
- @Override
- public ProjectState getAllProjects() {
- return get(allProjectsName);
- }
-
- @Override
- public ProjectState getAllUsers() {
- return null;
- }
-
- @Override
- public ProjectState get(Project.NameKey projectName) {
- return all.get(projectName);
- }
-
- @Override
- public void evict(Project p) {}
-
- @Override
- public void remove(Project p) {}
-
- @Override
- public void remove(Project.NameKey name) {}
-
- @Override
- public ImmutableSortedSet<Project.NameKey> all() {
- return ImmutableSortedSet.of();
- }
-
- @Override
- public ImmutableSortedSet<Project.NameKey> byName(String prefix) {
- return ImmutableSortedSet.of();
- }
-
- @Override
- public void onCreateProject(Project.NameKey newProjectName) {}
-
- @Override
- public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
- return Collections.emptySet();
- }
-
- @Override
- public ProjectState checkedGet(Project.NameKey projectName) throws IOException {
- return all.get(projectName);
- }
-
- @Override
- public void evict(Project.NameKey p) {}
-
- @Override
- public ProjectState checkedGet(Project.NameKey projectName, boolean strict)
- throws Exception {
- return all.get(projectName);
- }
- };
-
Injector injector = Guice.createInjector(new InMemoryModule());
injector.injectMembers(this);
- try {
- Repository repo = repoManager.createRepository(allProjectsName);
- ProjectConfig allProjects =
- projectConfigFactory.create(new Project.NameKey(allProjectsName.get()));
- allProjects.load(repo);
- LabelType cr = Util.codeReview();
- allProjects.getLabelSections().put(cr.getName(), cr);
- add(allProjects);
- } catch (IOException | ConfigInvalidException e) {
- throw new RuntimeException(e);
- }
+ // Tests previously used ProjectConfig.Factory to create ProjectConfigs without going through
+ // the ProjectCache, which was wrong. Manually call getInstance so we don't store it in a
+ // field that is accessible to test methods.
+ ProjectConfig.Factory projectConfigFactory = injector.getInstance(ProjectConfig.Factory.class);
singleVersionListener.start();
try {
@@ -285,58 +196,80 @@ public class RefControlTest extends GerritBaseTests {
singleVersionListener.stop();
}
- Cache<SectionSortCache.EntryKey, SectionSortCache.EntryVal> c =
- CacheBuilder.newBuilder().build();
- sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c), metricMaker);
-
- parent = projectConfigFactory.create(parentKey);
- parent.load(newRepository(parentKey));
- add(parent);
+ // Clear out All-Projects and use the lowest-level API possible for project creation, so the
+ // only ACL entries are exactly what is initialized by this test, and we aren't subject to
+ // changing defaults in SchemaCreator or ProjectCreator.
+ try (Repository allProjectsRepo = repoManager.createRepository(allProjectsName);
+ TestRepository<Repository> tr = new TestRepository<>(allProjectsRepo)) {
+ tr.delete(REFS_CONFIG);
+ try (MetaDataUpdate md = metaDataUpdateFactory.create(allProjectsName)) {
+ ProjectConfig allProjectsConfig = projectConfigFactory.create(allProjectsName);
+ allProjectsConfig.load(md);
+ LabelType cr = TestLabels.codeReview();
+ allProjectsConfig.getLabelSections().put(cr.getName(), cr);
+ allProjectsConfig.commit(md);
+ }
+ }
- local = projectConfigFactory.create(localKey);
- local.load(newRepository(localKey));
- add(local);
- local.getProject().setParentName(parentKey);
+ repoManager.createRepository(parentKey).close();
+ repoManager.createRepository(localKey).close();
+ try (MetaDataUpdate md = metaDataUpdateFactory.create(localKey)) {
+ ProjectConfig newLocal = projectConfigFactory.create(localKey);
+ newLocal.load(md);
+ newLocal.getProject().setParentName(parentKey);
+ newLocal.commit(md);
+ }
requestContext.setContext(() -> null);
-
- changeControlFactory = injector.getInstance(ChangeControl.Factory.class);
}
@After
- public void tearDown() {
+ public void tearDown() throws Exception {
requestContext.setContext(null);
}
@Test
public void ownerProject() throws Exception {
- allow(local, OWNER, ADMIN, "refs/*");
-
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(OWNER).ref("refs/*").group(ADMIN))
+ .update();
assertAdminsAreOwnersAndDevsAreNot();
}
@Test
public void denyOwnerProject() throws Exception {
- allow(local, OWNER, ADMIN, "refs/*");
- deny(local, OWNER, DEVS, "refs/*");
-
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(OWNER).ref("refs/*").group(ADMIN))
+ .add(deny(OWNER).ref("refs/*").group(DEVS))
+ .update();
assertAdminsAreOwnersAndDevsAreNot();
}
@Test
public void blockOwnerProject() throws Exception {
- allow(local, OWNER, ADMIN, "refs/*");
- block(local, OWNER, DEVS, "refs/*");
-
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(OWNER).ref("refs/*").group(ADMIN))
+ .add(block(OWNER).ref("refs/*").group(DEVS))
+ .update();
assertAdminsAreOwnersAndDevsAreNot();
}
@Test
public void branchDelegation1() throws Exception {
- allow(local, OWNER, ADMIN, "refs/*");
- allow(local, OWNER, DEVS, "refs/heads/x/*");
-
- ProjectControl uDev = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(OWNER).ref("refs/*").group(ADMIN))
+ .add(allow(OWNER).ref("refs/heads/x/*").group(DEVS))
+ .update();
+
+ ProjectControl uDev = user(localKey, DEVS);
assertNotOwner(uDev);
assertOwner("refs/heads/x/*", uDev);
@@ -349,12 +282,16 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void branchDelegation2() throws Exception {
- allow(local, OWNER, ADMIN, "refs/*");
- allow(local, OWNER, DEVS, "refs/heads/x/*");
- allow(local, OWNER, fixers, "refs/heads/x/y/*");
- doNotInherit(local, OWNER, "refs/heads/x/y/*");
-
- ProjectControl uDev = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(OWNER).ref("refs/*").group(ADMIN))
+ .add(allow(OWNER).ref("refs/heads/x/*").group(DEVS))
+ .add(allow(OWNER).ref("refs/heads/x/y/*").group(fixers))
+ .setExclusiveGroup(permissionKey(OWNER).ref("refs/heads/x/y/*"), true)
+ .update();
+
+ ProjectControl uDev = user(localKey, DEVS);
assertNotOwner(uDev);
assertOwner("refs/heads/x/*", uDev);
@@ -363,7 +300,7 @@ public class RefControlTest extends GerritBaseTests {
assertNotOwner("refs/*", uDev);
assertNotOwner("refs/heads/master", uDev);
- ProjectControl uFix = user(local, fixers);
+ ProjectControl uFix = user(localKey, fixers);
assertNotOwner(uFix);
assertOwner("refs/heads/x/y/*", uFix);
@@ -376,53 +313,41 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void inheritRead_SingleBranchDeniesUpload() throws Exception {
- allow(parent, READ, REGISTERED_USERS, "refs/*");
- allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
- allow(local, READ, REGISTERED_USERS, "refs/heads/foobar");
- doNotInherit(local, READ, "refs/heads/foobar");
- doNotInherit(local, PUSH, "refs/for/refs/heads/foobar");
-
- ProjectControl u = user(local);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(PUSH).ref("refs/for/refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/heads/foobar").group(REGISTERED_USERS))
+ .setExclusiveGroup(permissionKey(READ).ref("refs/heads/foobar"), true)
+ .setExclusiveGroup(permissionKey(PUSH).ref("refs/for/refs/heads/foobar"), true)
+ .update();
+
+ ProjectControl u = user(localKey);
assertCanUpload(u);
assertCreateChange("refs/heads/master", u);
assertCannotCreateChange("refs/heads/foobar", u);
}
@Test
- public void blockPushDrafts() throws Exception {
- allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
- block(parent, PUSH, ANONYMOUS_USERS, "refs/drafts/*");
- allow(local, PUSH, REGISTERED_USERS, "refs/drafts/*");
-
- ProjectControl u = user(local);
- assertCreateChange("refs/heads/master", u);
- assertThat(u.controlForRef("refs/drafts/master").canPerform(PUSH)).isFalse();
- }
-
- @Test
- public void blockPushDraftsUnblockAdmin() throws Exception {
- block(parent, PUSH, ANONYMOUS_USERS, "refs/drafts/*");
- allow(parent, PUSH, ADMIN, "refs/drafts/*");
- allow(local, PUSH, REGISTERED_USERS, "refs/drafts/*");
-
- ProjectControl u = user(local);
- ProjectControl a = user(local, "a", ADMIN);
-
- assertThat(a.controlForRef("refs/drafts/master").canPerform(PUSH))
- .named("push is allowed")
- .isTrue();
- assertThat(u.controlForRef("refs/drafts/master").canPerform(PUSH))
- .named("push is not allowed")
- .isFalse();
- }
-
- @Test
public void inheritRead_SingleBranchDoesNotOverrideInherited() throws Exception {
- allow(parent, READ, REGISTERED_USERS, "refs/*");
- allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
- allow(local, READ, REGISTERED_USERS, "refs/heads/foobar");
-
- ProjectControl u = user(local);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(PUSH).ref("refs/for/refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/heads/foobar").group(REGISTERED_USERS))
+ .update();
+
+ ProjectControl u = user(localKey);
assertCanUpload(u);
assertCreateChange("refs/heads/master", u);
assertCreateChange("refs/heads/foobar", u);
@@ -430,31 +355,50 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void inheritDuplicateSections() throws Exception {
- allow(parent, READ, ADMIN, "refs/*");
- allow(local, READ, DEVS, "refs/heads/*");
- assertCanAccess(user(local, "a", ADMIN));
-
- local = projectConfigFactory.create(localKey);
- local.load(newRepository(localKey));
- local.getProject().setParentName(parentKey);
- allow(local, READ, DEVS, "refs/*");
- assertCanAccess(user(local, "d", DEVS));
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(ADMIN))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(DEVS))
+ .update();
+ assertCanAccess(user(localKey, "a", ADMIN));
+ assertCanAccess(user(localKey, "d", DEVS));
}
@Test
public void inheritRead_OverrideWithDeny() throws Exception {
- allow(parent, READ, REGISTERED_USERS, "refs/*");
- deny(local, READ, REGISTERED_USERS, "refs/*");
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(deny(READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
- assertAccessDenied(user(local));
+ assertAccessDenied(user(localKey));
}
@Test
public void inheritRead_AppendWithDenyOfRef() throws Exception {
- allow(parent, READ, REGISTERED_USERS, "refs/*");
- deny(local, READ, REGISTERED_USERS, "refs/heads/*");
-
- ProjectControl u = user(local);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(deny(READ).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
+
+ ProjectControl u = user(localKey);
assertCanAccess(u);
assertCanRead("refs/master", u);
assertCanRead("refs/tags/foobar", u);
@@ -463,11 +407,19 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void inheritRead_OverridesAndDeniesOfRef() throws Exception {
- allow(parent, READ, REGISTERED_USERS, "refs/*");
- deny(local, READ, REGISTERED_USERS, "refs/*");
- allow(local, READ, REGISTERED_USERS, "refs/heads/*");
-
- ProjectControl u = user(local);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(deny(READ).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(READ).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
+
+ ProjectControl u = user(localKey);
assertCanAccess(u);
assertCannotRead("refs/foobar", u);
assertCannotRead("refs/tags/foobar", u);
@@ -476,11 +428,19 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void inheritSubmit_OverridesAndDeniesOfRef() throws Exception {
- allow(parent, SUBMIT, REGISTERED_USERS, "refs/*");
- deny(local, SUBMIT, REGISTERED_USERS, "refs/*");
- allow(local, SUBMIT, REGISTERED_USERS, "refs/heads/*");
-
- ProjectControl u = user(local);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(deny(SUBMIT).ref("refs/*").group(REGISTERED_USERS))
+ .add(allow(SUBMIT).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
+
+ ProjectControl u = user(localKey);
assertCannotSubmit("refs/foobar", u);
assertCannotSubmit("refs/tags/foobar", u);
assertCanSubmit("refs/heads/foobar", u);
@@ -488,46 +448,73 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void cannotUploadToAnyRef() throws Exception {
- allow(parent, READ, REGISTERED_USERS, "refs/*");
- allow(local, READ, DEVS, "refs/heads/*");
- allow(local, PUSH, DEVS, "refs/for/refs/heads/*");
-
- ProjectControl u = user(local);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/heads/*").group(DEVS))
+ .add(allow(PUSH).ref("refs/for/refs/heads/*").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey);
assertCannotUpload(u);
assertCannotCreateChange("refs/heads/master", u);
}
@Test
public void usernamePatternCanUploadToAnyRef() throws Exception {
- allow(local, PUSH, REGISTERED_USERS, "refs/heads/users/${username}/*");
- ProjectControl u = user(local, "a-registered-user");
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(PUSH).ref("refs/heads/users/${username}/*").group(REGISTERED_USERS))
+ .update();
+ ProjectControl u = user(localKey, "a-registered-user");
assertCanUpload(u);
}
@Test
public void usernamePatternRegExpCanUploadToAnyRef() throws Exception {
- allow(local, PUSH, REGISTERED_USERS, "^refs/heads/users/${username}/(public|private)/.+");
- ProjectControl u = user(local, "a-registered-user");
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(
+ allow(PUSH)
+ .ref("^refs/heads/users/${username}/(public|private)/.+")
+ .group(REGISTERED_USERS))
+ .update();
+ ProjectControl u = user(localKey, "a-registered-user");
assertCanUpload(u);
assertCanUpdate("refs/heads/users/a-registered-user/private/a", u);
}
@Test
public void usernamePatternNonRegex() throws Exception {
- allow(local, READ, DEVS, "refs/sb/${username}/heads/*");
-
- ProjectControl u = user(local, "u", DEVS);
- ProjectControl d = user(local, "d", DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/sb/${username}/heads/*").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, "u", DEVS);
+ ProjectControl d = user(localKey, "d", DEVS);
assertCannotRead("refs/sb/d/heads/foobar", u);
assertCanRead("refs/sb/d/heads/foobar", d);
}
@Test
public void usernamePatternWithRegex() throws Exception {
- allow(local, READ, DEVS, "^refs/sb/${username}/heads/.*");
-
- ProjectControl u = user(local, "d.v", DEVS);
- ProjectControl d = user(local, "dev", DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("^refs/sb/${username}/heads/.*").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, "d.v", DEVS);
+ ProjectControl d = user(localKey, "dev", DEVS);
assertCanAccess(u);
assertCanAccess(d);
assertCannotRead("refs/sb/dev/heads/foobar", u);
@@ -536,48 +523,80 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void usernameEmailPatternWithRegex() throws Exception {
- allow(local, READ, DEVS, "^refs/sb/${username}/heads/.*");
-
- ProjectControl u = user(local, "d.v@ger-rit.org", DEVS);
- ProjectControl d = user(local, "dev@ger-rit.org", DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("^refs/sb/${username}/heads/.*").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, "d.v@ger-rit.org", DEVS);
+ ProjectControl d = user(localKey, "dev@ger-rit.org", DEVS);
assertCannotRead("refs/sb/dev@ger-rit.org/heads/foobar", u);
assertCanRead("refs/sb/dev@ger-rit.org/heads/foobar", d);
}
@Test
public void sortWithRegex() throws Exception {
- allow(local, READ, DEVS, "^refs/heads/.*");
- allow(parent, READ, ANONYMOUS_USERS, "^refs/heads/.*-QA-.*");
-
- ProjectControl u = user(local, DEVS);
- ProjectControl d = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("^refs/heads/.*").group(DEVS))
+ .update();
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(READ).ref("^refs/heads/.*-QA-.*").group(ANONYMOUS_USERS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
+ ProjectControl d = user(localKey, DEVS);
assertCanRead("refs/heads/foo-QA-bar", u);
assertCanRead("refs/heads/foo-QA-bar", d);
}
@Test
public void blockRule_ParentBlocksChild() throws Exception {
- allow(local, PUSH, DEVS, "refs/tags/*");
- block(parent, PUSH, ANONYMOUS_USERS, "refs/tags/*");
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(PUSH).ref("refs/tags/*").group(DEVS))
+ .update();
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/tags/*").group(ANONYMOUS_USERS))
+ .update();
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/tags/V10", u);
}
@Test
public void blockRule_ParentBlocksChildEvenIfAlreadyBlockedInChild() throws Exception {
- allow(local, PUSH, DEVS, "refs/tags/*");
- block(local, PUSH, ANONYMOUS_USERS, "refs/tags/*");
- block(parent, PUSH, ANONYMOUS_USERS, "refs/tags/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(PUSH).ref("refs/tags/*").group(DEVS))
+ .add(block(PUSH).ref("refs/tags/*").group(ANONYMOUS_USERS))
+ .update();
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/tags/*").group(ANONYMOUS_USERS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/tags/V10", u);
}
@Test
public void blockPartialRangeLocally() throws Exception {
- block(local, LABEL + "Code-Review", +1, +2, DEVS, "refs/heads/master");
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/master").group(DEVS).range(+1, +2))
+ .update();
- ProjectControl u = user(local, DEVS);
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCannotVote(2, range);
@@ -585,10 +604,18 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void blockLabelRange_ParentBlocksChild() throws Exception {
- allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
- block(parent, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .update();
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCanVote(-1, range);
@@ -599,11 +626,19 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void blockLabelRange_ParentBlocksChildEvenIfAlreadyBlockedInChild() throws Exception {
- allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
- block(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
- block(parent, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .update();
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCanVote(-1, range);
@@ -614,197 +649,317 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void inheritSubmit_AllowInChildDoesntAffectUnblockInParent() throws Exception {
- block(parent, SUBMIT, ANONYMOUS_USERS, "refs/heads/*");
- allow(parent, SUBMIT, REGISTERED_USERS, "refs/heads/*");
- allow(local, SUBMIT, REGISTERED_USERS, "refs/heads/*");
-
- ProjectControl u = user(local);
- assertThat(u.controlForRef("refs/heads/master").canPerform(SUBMIT))
- .named("submit is allowed")
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(SUBMIT).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .add(allow(SUBMIT).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(SUBMIT).ref("refs/heads/*").group(REGISTERED_USERS))
+ .update();
+
+ ProjectControl u = user(localKey);
+ assertWithMessage("submit is allowed")
+ .that(u.controlForRef("refs/heads/master").canPerform(SUBMIT))
.isTrue();
}
@Test
public void unblockNoForce() throws Exception {
- block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, PUSH, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .add(allow(PUSH).ref("refs/heads/*").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCanUpdate("refs/heads/master", u);
}
@Test
public void unblockForce() throws Exception {
- PermissionRule r = block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- r.setForce(true);
- allow(local, PUSH, DEVS, "refs/heads/*").setForce(true);
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS).force(true))
+ .add(allow(PUSH).ref("refs/heads/*").group(DEVS).force(true))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCanForceUpdate("refs/heads/master", u);
}
@Test
public void unblockRead_NotPossible() throws Exception {
- block(parent, READ, ANONYMOUS_USERS, "refs/*");
- allow(parent, READ, ADMIN, "refs/*");
- allow(local, READ, ANONYMOUS_USERS, "refs/*");
- allow(local, READ, ADMIN, "refs/*");
- ProjectControl u = user(local);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(READ).ref("refs/*").group(ANONYMOUS_USERS))
+ .add(allow(READ).ref("refs/*").group(ADMIN))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(ANONYMOUS_USERS))
+ .add(allow(READ).ref("refs/*").group(ADMIN))
+ .update();
+
+ ProjectControl u = user(localKey);
assertCannotRead("refs/heads/master", u);
}
@Test
public void unblockForceWithAllowNoForce_NotPossible() throws Exception {
- PermissionRule r = block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- r.setForce(true);
- allow(local, PUSH, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS).force(true))
+ .add(allow(PUSH).ref("refs/heads/*").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotForceUpdate("refs/heads/master", u);
}
@Test
public void unblockMoreSpecificRef_Fails() throws Exception {
- block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, PUSH, DEVS, "refs/heads/master");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .add(allow(PUSH).ref("refs/heads/master").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/heads/master", u);
}
@Test
public void unblockMoreSpecificRefInLocal_Fails() throws Exception {
- block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, PUSH, DEVS, "refs/heads/master");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(PUSH).ref("refs/heads/master").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/heads/master", u);
}
@Test
public void unblockMoreSpecificRefWithExclusiveFlag() throws Exception {
- block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, PUSH, DEVS, "refs/heads/master", true);
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .add(allow(PUSH).ref("refs/heads/master").group(DEVS))
+ .setExclusiveGroup(permissionKey(PUSH).ref("refs/heads/master"), true)
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCanUpdate("refs/heads/master", u);
}
@Test
public void unblockVoteMoreSpecificRefWithExclusiveFlag() throws Exception {
- String perm = LABEL + "Code-Review";
-
- block(local, perm, -1, 1, ANONYMOUS_USERS, "refs/heads/*");
- allowExclusive(local, perm, -2, 2, DEVS, "refs/heads/master");
-
- ProjectControl u = user(local, DEVS);
- PermissionRange range = u.controlForRef("refs/heads/master").getRange(perm);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, 1))
+ .add(allowLabel("Code-Review").ref("refs/heads/master").group(DEVS).range(-2, 2))
+ .setExclusiveGroup(labelPermissionKey("Code-Review").ref("refs/heads/master"), true)
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
+ PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCanVote(-2, range);
}
@Test
public void unblockFromParentDoesNotAffectChild() throws Exception {
- allow(parent, PUSH, DEVS, "refs/heads/master", true);
- block(local, PUSH, DEVS, "refs/heads/master");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(PUSH).ref("refs/heads/master").group(DEVS))
+ .setExclusiveGroup(permissionKey(PUSH).ref("refs/heads/master"), true)
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/master").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/heads/master", u);
}
@Test
public void unblockFromParentDoesNotAffectChildDifferentGroups() throws Exception {
- allow(parent, PUSH, DEVS, "refs/heads/master", true);
- block(local, PUSH, ANONYMOUS_USERS, "refs/heads/master");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allow(PUSH).ref("refs/heads/master").group(DEVS))
+ .setExclusiveGroup(permissionKey(PUSH).ref("refs/heads/master"), true)
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/master").group(ANONYMOUS_USERS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/heads/master", u);
}
@Test
public void unblockMoreSpecificRefInLocalWithExclusiveFlag_Fails() throws Exception {
- block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, PUSH, DEVS, "refs/heads/master", true);
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(PUSH).ref("refs/heads/master").group(DEVS))
+ .setExclusiveGroup(permissionKey(PUSH).ref("refs/heads/master"), true)
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/heads/master", u);
}
@Test
public void blockMoreSpecificRefWithinProject() throws Exception {
- block(local, PUSH, ANONYMOUS_USERS, "refs/heads/secret");
- allow(local, PUSH, DEVS, "refs/heads/*", true);
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/secret").group(ANONYMOUS_USERS))
+ .add(allow(PUSH).ref("refs/heads/*").group(DEVS))
+ .setExclusiveGroup(permissionKey(PUSH).ref("refs/heads/*"), true)
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/heads/secret", u);
assertCanUpdate("refs/heads/master", u);
}
@Test
public void unblockOtherPermissionWithMoreSpecificRefAndExclusiveFlag_Fails() throws Exception {
- block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, PUSH, DEVS, "refs/heads/master");
- allow(local, SUBMIT, DEVS, "refs/heads/master", true);
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .add(allow(PUSH).ref("refs/heads/master").group(DEVS))
+ .add(allow(SUBMIT).ref("refs/heads/master").group(DEVS))
+ .setExclusiveGroup(permissionKey(SUBMIT).ref("refs/heads/master"), true)
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/heads/master", u);
}
@Test
public void unblockLargerScope_Fails() throws Exception {
- block(local, PUSH, ANONYMOUS_USERS, "refs/heads/master");
- allow(local, PUSH, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/master").group(ANONYMOUS_USERS))
+ .add(allow(PUSH).ref("refs/heads/*").group(DEVS))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
assertCannotUpdate("refs/heads/master", u);
}
@Test
public void unblockInLocal_Fails() throws Exception {
- block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, PUSH, fixers, "refs/heads/*");
-
- ProjectControl f = user(local, fixers);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(PUSH).ref("refs/heads/*").group(fixers))
+ .update();
+
+ ProjectControl f = user(localKey, fixers);
assertCannotUpdate("refs/heads/master", f);
}
@Test
public void unblockInParentBlockInLocal() throws Exception {
- block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
- allow(parent, PUSH, DEVS, "refs/heads/*");
- block(local, PUSH, DEVS, "refs/heads/*");
-
- ProjectControl d = user(local, DEVS);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .add(allow(PUSH).ref("refs/heads/*").group(DEVS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(PUSH).ref("refs/heads/*").group(DEVS))
+ .update();
+
+ ProjectControl d = user(localKey, DEVS);
assertCannotUpdate("refs/heads/master", d);
}
@Test
public void unblockForceEditTopicName() throws Exception {
- block(local, EDIT_TOPIC_NAME, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, EDIT_TOPIC_NAME, DEVS, "refs/heads/*").setForce(true);
-
- ProjectControl u = user(local, DEVS);
- assertThat(u.controlForRef("refs/heads/master").canForceEditTopicName())
- .named("u can edit topic name")
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(block(EDIT_TOPIC_NAME).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .add(allow(EDIT_TOPIC_NAME).ref("refs/heads/*").group(DEVS).force(true))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
+ assertWithMessage("u can edit topic name")
+ .that(u.controlForRef("refs/heads/master").canForceEditTopicName())
.isTrue();
}
@Test
public void unblockInLocalForceEditTopicName_Fails() throws Exception {
- block(parent, EDIT_TOPIC_NAME, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, EDIT_TOPIC_NAME, DEVS, "refs/heads/*").setForce(true);
-
- ProjectControl u = user(local, REGISTERED_USERS);
- assertThat(u.controlForRef("refs/heads/master").canForceEditTopicName())
- .named("u can't edit topic name")
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(EDIT_TOPIC_NAME).ref("refs/heads/*").group(ANONYMOUS_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(EDIT_TOPIC_NAME).ref("refs/heads/*").group(DEVS).force(true))
+ .update();
+
+ ProjectControl u = user(localKey, REGISTERED_USERS);
+ assertWithMessage("u can't edit topic name")
+ .that(u.controlForRef("refs/heads/master").canForceEditTopicName())
.isFalse();
}
@Test
public void unblockRange() throws Exception {
- block(local, LABEL + "Code-Review", -1, +1, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, +1))
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCanVote(-2, range);
assertCanVote(2, range);
@@ -812,10 +967,14 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void unblockRangeOnMoreSpecificRef_Fails() throws Exception {
- block(local, LABEL + "Code-Review", -1, +1, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/master");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, +1))
+ .add(allowLabel("Code-Review").ref("refs/heads/master").group(DEVS).range(-2, +2))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCannotVote(-2, range);
assertCannotVote(2, range);
@@ -823,10 +982,15 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void unblockRangeOnLargerScope_Fails() throws Exception {
- block(local, LABEL + "Code-Review", -1, +1, ANONYMOUS_USERS, "refs/heads/master");
- allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(
+ blockLabel("Code-Review").ref("refs/heads/master").group(ANONYMOUS_USERS).range(-1, +1))
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCannotVote(-2, range);
assertCannotVote(2, range);
@@ -834,9 +998,13 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void nonconfiguredCannotVote() throws Exception {
- allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .update();
- ProjectControl u = user(local, REGISTERED_USERS);
+ ProjectControl u = user(localKey, REGISTERED_USERS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCannotVote(-1, range);
assertCannotVote(1, range);
@@ -844,10 +1012,18 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void unblockInLocalRange_Fails() throws Exception {
- block(parent, LABEL + "Code-Review", -1, 1, ANONYMOUS_USERS, "refs/heads/*");
- allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(ANONYMOUS_USERS).range(-1, 1))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-2, +2))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCannotVote(-2, range);
assertCannotVote(2, range);
@@ -855,9 +1031,13 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void unblockRangeForChangeOwner() throws Exception {
- allow(local, LABEL + "Code-Review", -2, +2, CHANGE_OWNER, "refs/heads/*");
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(CHANGE_OWNER).range(-2, +2))
+ .update();
- ProjectControl u = user(local, DEVS);
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range =
u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review", true);
assertCanVote(-2, range);
@@ -866,9 +1046,13 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void unblockRangeForNotChangeOwner() throws Exception {
- allow(local, LABEL + "Code-Review", -2, +2, CHANGE_OWNER, "refs/heads/*");
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(CHANGE_OWNER).range(-2, +2))
+ .update();
- ProjectControl u = user(local, DEVS);
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCannotVote(-2, range);
assertCannotVote(2, range);
@@ -876,9 +1060,13 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void blockChangeOwnerVote() throws Exception {
- block(local, LABEL + "Code-Review", -2, +2, CHANGE_OWNER, "refs/heads/*");
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(CHANGE_OWNER).range(-2, +2))
+ .update();
- ProjectControl u = user(local, DEVS);
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCannotVote(-2, range);
assertCannotVote(2, range);
@@ -886,10 +1074,14 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void unionOfPermissibleVotes() throws Exception {
- allow(local, LABEL + "Code-Review", -1, +1, DEVS, "refs/heads/*");
- allow(local, LABEL + "Code-Review", -2, +2, REGISTERED_USERS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-1, +1))
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, +2))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCanVote(-2, range);
assertCanVote(2, range);
@@ -897,10 +1089,14 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void unionOfPermissibleVotesPermissionOrder() throws Exception {
- allow(local, LABEL + "Code-Review", -2, +2, REGISTERED_USERS, "refs/heads/*");
- allow(local, LABEL + "Code-Review", -1, +1, DEVS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, +2))
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-1, +1))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCanVote(-2, range);
assertCanVote(2, range);
@@ -908,11 +1104,19 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void unionOfBlockedVotes() throws Exception {
- allow(parent, LABEL + "Code-Review", -1, +1, DEVS, "refs/heads/*");
- block(parent, LABEL + "Code-Review", -2, +2, REGISTERED_USERS, "refs/heads/*");
- block(local, LABEL + "Code-Review", -2, +1, REGISTERED_USERS, "refs/heads/*");
-
- ProjectControl u = user(local, DEVS);
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(allowLabel("Code-Review").ref("refs/heads/*").group(DEVS).range(-1, +1))
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, +2))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(blockLabel("Code-Review").ref("refs/heads/*").group(REGISTERED_USERS).range(-2, +1))
+ .update();
+
+ ProjectControl u = user(localKey, DEVS);
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
assertCanVote(-1, range);
assertCannotVote(1, range);
@@ -920,10 +1124,18 @@ public class RefControlTest extends GerritBaseTests {
@Test
public void blockOwner() throws Exception {
- block(parent, OWNER, ANONYMOUS_USERS, "refs/*");
- allow(local, OWNER, DEVS, "refs/*");
+ projectOperations
+ .project(parentKey)
+ .forUpdate()
+ .add(block(OWNER).ref("refs/*").group(ANONYMOUS_USERS))
+ .update();
+ projectOperations
+ .project(localKey)
+ .forUpdate()
+ .add(allow(OWNER).ref("refs/*").group(DEVS))
+ .update();
- assertThat(user(local, DEVS).isOwner()).isFalse();
+ assertThat(user(localKey, DEVS).isOwner()).isFalse();
}
@Test
@@ -935,14 +1147,16 @@ public class RefControlTest extends GerritBaseTests {
RefPattern.validate("^refs/heads/review/${username}/.+");
}
- @Test(expected = InvalidNameException.class)
+ @Test
public void testValidateBadRefPatternDoubleCaret() throws Exception {
- RefPattern.validate("^^refs/*");
+ assertThrows(InvalidNameException.class, () -> RefPattern.validate("^^refs/*"));
}
- @Test(expected = InvalidNameException.class)
+ @Test
public void testValidateBadRefPatternDanglingCharacter() throws Exception {
- RefPattern.validate("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}*");
+ assertThrows(
+ InvalidNameException.class,
+ () -> RefPattern.validate("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}*"));
}
@Test
@@ -950,53 +1164,19 @@ public class RefControlTest extends GerritBaseTests {
RefPattern.validate("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}");
}
- private InMemoryRepository add(ProjectConfig pc) {
- List<CommentLinkInfo> commentLinks = null;
-
- InMemoryRepository repo;
- try {
- repo = repoManager.createRepository(pc.getName());
- if (pc.getProject() == null) {
- pc.load(repo);
- }
- } catch (IOException | ConfigInvalidException e) {
- throw new RuntimeException(e);
- }
- all.put(
- pc.getName(),
- new ProjectState(
- projectCache,
- allProjectsName,
- allUsersName,
- repoManager,
- commentLinks,
- capabilityCollectionFactory,
- transferConfig,
- metricMaker,
- pc));
- return repo;
+ private ProjectState getProjectState(Project.NameKey nameKey) throws Exception {
+ return projectCache.checkedGet(nameKey, true);
}
- private ProjectControl user(ProjectConfig local, AccountGroup.UUID... memberOf) {
- return user(local, null, memberOf);
+ private ProjectControl user(Project.NameKey localKey, AccountGroup.UUID... memberOf)
+ throws Exception {
+ return user(localKey, null, memberOf);
}
private ProjectControl user(
- ProjectConfig local, @Nullable String name, AccountGroup.UUID... memberOf) {
- return new ProjectControl(
- Collections.emptySet(),
- Collections.emptySet(),
- sectionSorter,
- changeControlFactory,
- permissionBackend,
- refFilterFactory,
- new MockUser(name, memberOf),
- newProjectState(local));
- }
-
- private ProjectState newProjectState(ProjectConfig local) {
- add(local);
- return all.get(local.getProject().getNameKey());
+ Project.NameKey localKey, @Nullable String name, AccountGroup.UUID... memberOf)
+ throws Exception {
+ return projectControlFactory.create(new MockUser(name, memberOf), getProjectState(localKey));
}
private static class MockUser extends CurrentUser {
diff --git a/javatests/com/google/gerrit/server/plugins/AutoRegisterModulesTest.java b/javatests/com/google/gerrit/server/plugins/AutoRegisterModulesTest.java
index 0ffd4acbc1..55c9bc36e3 100644
--- a/javatests/com/google/gerrit/server/plugins/AutoRegisterModulesTest.java
+++ b/javatests/com/google/gerrit/server/plugins/AutoRegisterModulesTest.java
@@ -14,10 +14,11 @@
package com.google.gerrit.server.plugins;
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.annotations.Export;
@@ -31,26 +32,17 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.jar.Manifest;
-import org.easymock.EasyMockSupport;
import org.junit.Test;
public class AutoRegisterModulesTest {
@Test
public void shouldRegisterSshCommand() throws InvalidPluginException {
- EasyMockSupport ems = new EasyMockSupport();
- ModuleGenerator sshModule = ems.createNiceMock(ModuleGenerator.class);
+ ModuleGenerator sshModule = mock(ModuleGenerator.class);
+ PluginGuiceEnvironment env = mock(PluginGuiceEnvironment.class);
- PluginGuiceEnvironment env = ems.createNiceMock(PluginGuiceEnvironment.class);
- expect(env.hasSshModule()).andReturn(true);
- expect(env.newSshModuleGenerator()).andReturn(sshModule);
-
- sshModule.setPluginName("test_plugin_name");
- expectLastCall();
- sshModule.export(anyObject(Export.class), eq(TestSshCommand.class));
- expectLastCall();
-
- ems.replayAll();
+ when(env.hasSshModule()).thenReturn(true);
+ when(env.newSshModuleGenerator()).thenReturn(sshModule);
PluginContentScanner scanner = new TestPluginContextScanner();
ClassLoader classLoader = this.getClass().getClassLoader();
@@ -59,7 +51,8 @@ public class AutoRegisterModulesTest {
new AutoRegisterModules("test_plugin_name", env, scanner, classLoader);
objectUnderTest.discover();
- ems.verifyAll();
+ verify(sshModule).setPluginName("test_plugin_name");
+ verify(sshModule).export(any(Export.class), eq(TestSshCommand.class));
}
@Export(value = "test")
diff --git a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
index cf6d50fab6..61a2d2fb3c 100644
--- a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
+++ b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
@@ -14,27 +14,31 @@
package com.google.gerrit.server.project;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
import static com.google.gerrit.common.data.Permission.READ;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static org.eclipse.jgit.lib.Constants.R_REFS;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.restapi.project.CommitsCollection;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.InMemoryTestEnvironment;
import com.google.inject.Inject;
@@ -44,12 +48,13 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
/** Unit tests for {@link CommitsCollection}. */
-public class CommitsCollectionTest extends GerritBaseTests {
+public class CommitsCollectionTest {
@Rule public InMemoryTestEnvironment testEnvironment = new InMemoryTestEnvironment();
@Inject private AccountManager accountManager;
@@ -58,10 +63,10 @@ public class CommitsCollectionTest extends GerritBaseTests {
@Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
@Inject protected AllProjectsName allProjects;
@Inject private CommitsCollection commits;
- @Inject private ProjectConfig.Factory projectConfigFactory;
+ @Inject private ProjectOperations projectOperations;
private TestRepository<InMemoryRepository> repo;
- private ProjectConfig project;
+ private Project.NameKey project;
@Before
public void setUp() throws Exception {
@@ -69,17 +74,22 @@ public class CommitsCollectionTest extends GerritBaseTests {
Account.Id user = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
testEnvironment.setApiUser(user);
+ project = projectOperations.newProject().create();
+ repo = new TestRepository<>(repoManager.openRepository(project));
+ }
- Project.NameKey name = new Project.NameKey("project");
- InMemoryRepository inMemoryRepo = repoManager.createRepository(name);
- project = projectConfigFactory.create(name);
- project.load(inMemoryRepo);
- repo = new TestRepository<>(inMemoryRepo);
+ @After
+ public void tearDown() {
+ repo.getRepository().close();
}
@Test
public void canReadCommitWhenAllRefsVisible() throws Exception {
- allow(project, READ, REGISTERED_USERS, "refs/*");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
ObjectId id = repo.branch("master").commit().create();
ProjectState state = readProjectState();
RevWalk rw = repo.getRevWalk();
@@ -90,8 +100,12 @@ public class CommitsCollectionTest extends GerritBaseTests {
@Test
public void canReadCommitIfTwoRefsVisible() throws Exception {
- allow(project, READ, REGISTERED_USERS, "refs/heads/branch1");
- allow(project, READ, REGISTERED_USERS, "refs/heads/branch2");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(READ).ref("refs/heads/branch1").group(REGISTERED_USERS))
+ .add(allow(READ).ref("refs/heads/branch2").group(REGISTERED_USERS))
+ .update();
ObjectId id1 = repo.branch("branch1").commit().create();
ObjectId id2 = repo.branch("branch2").commit().create();
@@ -106,8 +120,12 @@ public class CommitsCollectionTest extends GerritBaseTests {
@Test
public void canReadCommitIfRefVisible() throws Exception {
- allow(project, READ, REGISTERED_USERS, "refs/heads/branch1");
- deny(project, READ, REGISTERED_USERS, "refs/heads/branch2");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(READ).ref("refs/heads/branch1").group(REGISTERED_USERS))
+ .add(deny(READ).ref("refs/heads/branch2").group(REGISTERED_USERS))
+ .update();
ObjectId id1 = repo.branch("branch1").commit().create();
ObjectId id2 = repo.branch("branch2").commit().create();
@@ -122,8 +140,12 @@ public class CommitsCollectionTest extends GerritBaseTests {
@Test
public void canReadCommitIfReachableFromVisibleRef() throws Exception {
- allow(project, READ, REGISTERED_USERS, "refs/heads/branch1");
- deny(project, READ, REGISTERED_USERS, "refs/heads/branch2");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(READ).ref("refs/heads/branch1").group(REGISTERED_USERS))
+ .add(deny(READ).ref("refs/heads/branch2").group(REGISTERED_USERS))
+ .update();
RevCommit parent1 = repo.commit().create();
repo.branch("branch1").commit().parent(parent1).create();
@@ -140,7 +162,11 @@ public class CommitsCollectionTest extends GerritBaseTests {
@Test
public void cannotReadAfterRollbackWithRestrictedRead() throws Exception {
- allow(project, READ, REGISTERED_USERS, "refs/heads/branch1");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(READ).ref("refs/heads/branch1").group(REGISTERED_USERS))
+ .update();
RevCommit parent1 = repo.commit().create();
ObjectId id1 = repo.branch("branch1").commit().parent(parent1).create();
@@ -159,7 +185,11 @@ public class CommitsCollectionTest extends GerritBaseTests {
@Test
public void canReadAfterRollbackWithAllRefsVisible() throws Exception {
- allow(project, READ, REGISTERED_USERS, "refs/*");
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allow(READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
RevCommit parent1 = repo.commit().create();
ObjectId id1 = repo.branch("branch1").commit().parent(parent1).create();
@@ -177,41 +207,19 @@ public class CommitsCollectionTest extends GerritBaseTests {
}
private ProjectState readProjectState() throws Exception {
- return projectCache.get(project.getName());
- }
-
- protected void allow(ProjectConfig project, String permission, AccountGroup.UUID id, String ref)
- throws Exception {
- Util.allow(project, permission, id, ref);
- saveProjectConfig(project);
- }
-
- protected void deny(ProjectConfig project, String permission, AccountGroup.UUID id, String ref)
- throws Exception {
- Util.deny(project, permission, id, ref);
- saveProjectConfig(project);
- }
-
- protected void saveProjectConfig(ProjectConfig cfg) throws Exception {
- try (MetaDataUpdate md = metaDataUpdateFactory.create(cfg.getName())) {
- cfg.commit(md);
- }
- projectCache.evict(cfg.getProject());
+ return projectCache.get(project);
}
private void setUpPermissions() throws Exception {
- ImmutableList<AccountGroup.UUID> admins = getAdmins();
-
// Remove read permissions for all users besides admin, because by default
// Anonymous user group has ALLOW READ permission in refs/*.
// This method is idempotent, so is safe to call on every test setup.
- ProjectConfig pc = projectCache.checkedGet(allProjects).getConfig();
- for (AccessSection sec : pc.getAccessSections()) {
- sec.removePermission(Permission.READ);
- }
- for (AccountGroup.UUID admin : admins) {
- allow(pc, Permission.READ, admin, "refs/*");
- }
+ TestProjectUpdate.Builder u = projectOperations.allProjectsForUpdate();
+ projectCache.checkedGet(allProjects).getConfig().getAccessSectionNames().stream()
+ .filter(sec -> sec.startsWith(R_REFS))
+ .forEach(sec -> u.remove(permissionKey(Permission.READ).ref(sec)));
+ getAdmins().forEach(admin -> u.add(allow(Permission.READ).ref("refs/*").group(admin)));
+ u.update();
}
private ImmutableList<AccountGroup.UUID> getAdmins() {
diff --git a/javatests/com/google/gerrit/server/project/GroupListTest.java b/javatests/com/google/gerrit/server/project/GroupListTest.java
index 08aca9f987..518f85dc48 100644
--- a/javatests/com/google/gerrit/server/project/GroupListTest.java
+++ b/javatests/com/google/gerrit/server/project/GroupListTest.java
@@ -14,22 +14,19 @@
package com.google.gerrit.server.project;
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.git.ValidationError;
-import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
@@ -37,8 +34,8 @@ import java.util.Set;
import org.junit.Before;
import org.junit.Test;
-public class GroupListTest extends GerritBaseTests {
- private static final Project.NameKey PROJECT = new Project.NameKey("project");
+public class GroupListTest {
+ private static final Project.NameKey PROJECT = Project.nameKey("project");
private static final String TEXT =
"# UUID \tGroup Name\n"
+ "#\n"
@@ -49,14 +46,13 @@ public class GroupListTest extends GerritBaseTests {
@Before
public void setup() throws IOException {
- ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class);
- replay(sink);
+ ValidationError.Sink sink = mock(ValidationError.Sink.class);
groupList = GroupList.parse(PROJECT, TEXT, sink);
}
@Test
public void byUUID() throws Exception {
- AccountGroup.UUID uuid = new AccountGroup.UUID("d96b998f8a66ff433af50befb975d0e2bb6e0999");
+ AccountGroup.UUID uuid = AccountGroup.uuid("d96b998f8a66ff433af50befb975d0e2bb6e0999");
GroupReference groupReference = groupList.byUUID(uuid);
@@ -66,7 +62,7 @@ public class GroupListTest extends GerritBaseTests {
@Test
public void put() {
- AccountGroup.UUID uuid = new AccountGroup.UUID("abc");
+ AccountGroup.UUID uuid = AccountGroup.uuid("abc");
GroupReference groupReference = new GroupReference(uuid, "Hutzliputz");
groupList.put(uuid, groupReference);
@@ -81,7 +77,7 @@ public class GroupListTest extends GerritBaseTests {
Collection<GroupReference> result = groupList.references();
assertEquals(2, result.size());
- AccountGroup.UUID uuid = new AccountGroup.UUID("ebe31c01aec2c9ac3b3c03e87a47450829ff4310");
+ AccountGroup.UUID uuid = AccountGroup.uuid("ebe31c01aec2c9ac3b3c03e87a47450829ff4310");
GroupReference expected = new GroupReference(uuid, "Administrators");
assertTrue(result.contains(expected));
@@ -92,27 +88,24 @@ public class GroupListTest extends GerritBaseTests {
Set<AccountGroup.UUID> result = groupList.uuids();
assertEquals(2, result.size());
- AccountGroup.UUID expected = new AccountGroup.UUID("ebe31c01aec2c9ac3b3c03e87a47450829ff4310");
+ AccountGroup.UUID expected = AccountGroup.uuid("ebe31c01aec2c9ac3b3c03e87a47450829ff4310");
assertTrue(result.contains(expected));
}
@Test
public void validationError() throws Exception {
- ValidationError.Sink sink = createMock(ValidationError.Sink.class);
- sink.error(anyObject(ValidationError.class));
- expectLastCall().times(2);
- replay(sink);
+ ValidationError.Sink sink = mock(ValidationError.Sink.class);
groupList = GroupList.parse(PROJECT, TEXT.replace("\t", " "), sink);
- verify(sink);
+ verify(sink, times(2)).error(any(ValidationError.class));
}
@Test
public void retainAll() throws Exception {
- AccountGroup.UUID uuid = new AccountGroup.UUID("d96b998f8a66ff433af50befb975d0e2bb6e0999");
+ AccountGroup.UUID uuid = AccountGroup.uuid("d96b998f8a66ff433af50befb975d0e2bb6e0999");
groupList.retainUUIDs(Collections.singleton(uuid));
assertNotNull(groupList.byUUID(uuid));
- assertNull(groupList.byUUID(new AccountGroup.UUID("ebe31c01aec2c9ac3b3c03e87a47450829ff4310")));
+ assertNull(groupList.byUUID(AccountGroup.uuid("ebe31c01aec2c9ac3b3c03e87a47450829ff4310")));
}
@Test
diff --git a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
index 3436153b41..0dd643627b 100644
--- a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
+++ b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.project;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.gerrit.reviewdb.client.BooleanProjectConfig.REQUIRE_CHANGE_ID;
+import static com.google.gerrit.entities.BooleanProjectConfig.REQUIRE_CHANGE_ID;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -26,18 +26,17 @@ import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.project.testing.Util;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.server.project.testing.TestLabels;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -62,9 +61,12 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-public class ProjectConfigTest extends GerritBaseTests {
+public class ProjectConfigTest {
private static final String LABEL_SCORES_CONFIG =
- " copyMinScore = "
+ " copyAnyScore = "
+ + !LabelType.DEF_COPY_ANY_SCORE
+ + "\n"
+ + " copyMinScore = "
+ !LabelType.DEF_COPY_MIN_SCORE
+ "\n"
+ " copyMaxScore = "
@@ -88,8 +90,8 @@ public class ProjectConfigTest extends GerritBaseTests {
@Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
private final GroupReference developers =
- new GroupReference(new AccountGroup.UUID("X"), "Developers");
- private final GroupReference staff = new GroupReference(new AccountGroup.UUID("Y"), "Staff");
+ new GroupReference(AccountGroup.uuid("X"), "Developers");
+ private final GroupReference staff = new GroupReference(AccountGroup.uuid("Y"), "Staff");
private SitePaths sitePaths;
private ProjectConfig.Factory factory;
@@ -260,6 +262,7 @@ public class ProjectConfigTest extends GerritBaseTests {
ProjectConfig cfg = read(rev);
Map<String, LabelType> labels = cfg.getLabelSections();
LabelType type = labels.entrySet().iterator().next().getValue();
+ assertThat(type.isCopyAnyScore()).isNotEqualTo(LabelType.DEF_COPY_ANY_SCORE);
assertThat(type.isCopyMinScore()).isNotEqualTo(LabelType.DEF_COPY_MIN_SCORE);
assertThat(type.isCopyMaxScore()).isNotEqualTo(LabelType.DEF_COPY_MAX_SCORE);
assertThat(type.isCopyAllScoresOnMergeFirstParentUpdate())
@@ -319,17 +322,17 @@ public class ProjectConfigTest extends GerritBaseTests {
+ "\tsubmit = group Staff\n"
+ " upload = group Developers\n"
+ " read = group Developers\n"
- + "[accounts]\n"
- + " sameGroupVisibility = group Staff\n"
- + "[contributor-agreement \"Individual\"]\n"
- + " description = A new description\n"
- + " accepted = group Staff\n"
- + " agreementUrl = http://www.example.com/agree\n"
- + "\texcludeProjects = ^/theirproject\n"
+ "[label \"CustomLabel\"]\n"
+ LABEL_SCORES_CONFIG
+ "\tfunction = MaxWithBlock\n" // label gets this function when it is created
- + "\tdefaultValue = 0\n"); // label gets this value when it is created
+ + "\tdefaultValue = 0\n" // label gets this value when it is created
+ + "[accounts]\n"
+ + "\tsameGroupVisibility = group Staff\n"
+ + "[contributor-agreement \"Individual\"]\n"
+ + "\tdescription = A new description\n"
+ + "\tagreementUrl = http://www.example.com/agree\n"
+ + "\taccepted = group Staff\n"
+ + "\texcludeProjects = ^/theirproject\n");
}
@Test
@@ -341,11 +344,11 @@ public class ProjectConfigTest extends GerritBaseTests {
cfg.getLabelSections()
.put(
"My-Label",
- Util.category(
+ TestLabels.label(
"My-Label",
- Util.value(-1, "Negative"),
- Util.value(0, "No score"),
- Util.value(1, "Positive")));
+ TestLabels.value(-1, "Negative"),
+ TestLabels.value(0, "No score"),
+ TestLabels.value(1, "Positive")));
rev = commit(cfg);
assertThat(text(rev, "project.config"))
.isEqualTo(
@@ -422,7 +425,7 @@ public class ProjectConfigTest extends GerritBaseTests {
@Test
public void readUnexistingPluginConfig() throws Exception {
- ProjectConfig cfg = factory.create(new Project.NameKey("test"));
+ ProjectConfig cfg = factory.create(Project.nameKey("test"));
cfg.load(db);
PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
assertThat(pluginCfg.getNames()).isEmpty();
@@ -625,7 +628,7 @@ public class ProjectConfigTest extends GerritBaseTests {
@Test
public void readOtherProjectIgnoresAllProjectsBaseConfig() throws Exception {
- ProjectConfig cfg = factory.create(new Project.NameKey("test"));
+ ProjectConfig cfg = factory.create(Project.nameKey("test"));
cfg.load(db);
assertThat(cfg.getProject().getBooleanConfig(REQUIRE_CHANGE_ID))
.isEqualTo(InheritableBoolean.INHERIT);
@@ -641,6 +644,118 @@ public class ProjectConfigTest extends GerritBaseTests {
.isEqualTo(InheritableBoolean.INHERIT);
}
+ @Test
+ public void accountsSectionIsUnsetIfNoSameGroupVisibilityIsSet() throws Exception {
+ RevCommit rev =
+ tr.commit()
+ .add(
+ "project.config",
+ "[commentlink \"bugzilla\"]\n"
+ + "\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n"
+ + "\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n"
+ + "[accounts]\n"
+ + " sameGroupVisibility = group Staff\n")
+ .create();
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ cfg.getAccountsSection().setSameGroupVisibility(ImmutableList.of());
+ rev = commit(cfg);
+ assertThat(text(rev, "project.config"))
+ .isEqualTo(
+ "[commentlink \"bugzilla\"]\n\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n");
+ }
+
+ @Test
+ public void contributorSectionIsUnsetIfNoContributorAgreementIsSet() throws Exception {
+ RevCommit rev =
+ tr.commit()
+ .add(
+ "project.config",
+ "[commentlink \"bugzilla\"]\n"
+ + "\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n"
+ + "\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n"
+ + "[contributor-agreement \"Individual\"]\n"
+ + " accepted = group Developers\n"
+ + " accepted = group Staff\n")
+ .create();
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ ContributorAgreement section = cfg.getContributorAgreement("Individual");
+ section.setAccepted(ImmutableList.of());
+ rev = commit(cfg);
+ assertThat(text(rev, "project.config"))
+ .isEqualTo(
+ "[commentlink \"bugzilla\"]\n\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n");
+ }
+
+ @Test
+ public void notifySectionIsUnsetIfNoNotificationsAreSet() throws Exception {
+ RevCommit rev =
+ tr.commit()
+ .add(
+ "project.config",
+ "[commentlink \"bugzilla\"]\n"
+ + "\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n"
+ + "\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n"
+ + "[notify \"name\"]\n"
+ + " email = example@example.com\n")
+ .create();
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ cfg.getNotifyConfigs().clear();
+ rev = commit(cfg);
+ assertThat(text(rev, "project.config"))
+ .isEqualTo(
+ "[commentlink \"bugzilla\"]\n\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n");
+ }
+
+ @Test
+ public void commentLinkSectionIsUnsetIfNoCommentLinksAreSet() throws Exception {
+ RevCommit rev =
+ tr.commit()
+ .add(
+ "project.config",
+ "[commentlink \"bugzilla\"]\n"
+ + "\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n"
+ + "\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n"
+ + "[notify \"name\"]\n"
+ + " email = example@example.com\n")
+ .create();
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ cfg.getCommentLinkSections().clear();
+ rev = commit(cfg);
+ assertThat(text(rev, "project.config"))
+ .isEqualTo("[notify \"name\"]\n\temail = example@example.com\n");
+ }
+
+ @Test
+ public void pluginSectionIsUnsetIfAllPluginConfigsAreEmpty() throws Exception {
+ RevCommit rev =
+ tr.commit()
+ .add(
+ "project.config",
+ "[commentlink \"bugzilla\"]\n"
+ + "\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n"
+ + "\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n"
+ + "[plugin \"somePlugin\"]\n"
+ + " key = value\n")
+ .create();
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
+ pluginCfg.unset("key");
+ rev = commit(cfg);
+ assertThat(text(rev, "project.config"))
+ .isEqualTo(
+ "[commentlink \"bugzilla\"]\n\tmatch = \"(bug\\\\s+#?)(\\\\d+)\"\n\tlink = http://bugs.example.com/show_bug.cgi?id=$2\n");
+ }
+
private Path writeDefaultAllProjectsConfig(String... lines) throws IOException {
Path dir = sitePaths.etc_dir.resolve(ALL_PROJECTS.get());
Files.createDirectories(dir);
@@ -648,7 +763,7 @@ public class ProjectConfigTest extends GerritBaseTests {
}
private ProjectConfig read(RevCommit rev) throws IOException, ConfigInvalidException {
- ProjectConfig cfg = factory.create(new Project.NameKey("test"));
+ ProjectConfig cfg = factory.create(Project.nameKey("test"));
cfg.load(db, rev);
return cfg;
}
diff --git a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
index a2b2866a72..e7f0812c24 100644
--- a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -15,13 +15,17 @@
package com.google.gerrit.server.query.account;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.access.AccessSectionInfo;
import com.google.gerrit.extensions.api.access.PermissionInfo;
@@ -47,8 +51,6 @@ import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -79,6 +81,7 @@ import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.testing.GerritServerTests;
+import com.google.gerrit.testing.GerritTestName;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
@@ -93,10 +96,13 @@ import org.eclipse.jgit.lib.Repository;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
@Ignore
public abstract class AbstractQueryAccountsTest extends GerritServerTests {
+ @Rule public final GerritTestName testName = new GerritTestName();
+
@Inject protected Accounts accounts;
@Inject @ServerInitiated protected Provider<AccountsUpdate> accountsUpdate;
@@ -253,7 +259,7 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
addEmails(user1, secondaryEmail);
AccountInfo user2 = newAccount("user");
- requestContext.setContext(newRequestContext(new Account.Id(user2._accountId)));
+ requestContext.setContext(newRequestContext(Account.id(user2._accountId)));
if (getSchemaVersion() < 5) {
assertMissingField(AccountField.PREFERRED_EMAIL);
@@ -340,7 +346,7 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
AccountInfo user2 = newAccountWithFullName("jroe", "Jane Roe");
AccountInfo user3 = newAccount("user");
- requestContext.setContext(newRequestContext(new Account.Id(user3._accountId)));
+ requestContext.setContext(newRequestContext(Account.id(user3._accountId)));
assertQuery("notexisting");
assertQuery("Not Existing");
@@ -575,13 +581,13 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
String[] secondaryEmails = new String[] {"dfg@example.com", "hij@example.com"};
addEmails(otherUser, secondaryEmails);
- requestContext.setContext(newRequestContext(new Account.Id(user._accountId)));
+ requestContext.setContext(newRequestContext(Account.id(user._accountId)));
List<AccountInfo> result = newQuery(otherUser.username).withSuggest(true).get();
assertThat(result.get(0).secondaryEmails).isNull();
-
- exception.expect(AuthException.class);
- newQuery(otherUser.username).withOption(ListAccountsOption.ALL_EMAILS).get();
+ assertThrows(
+ AuthException.class,
+ () -> newQuery(otherUser.username).withOption(ListAccountsOption.ALL_EMAILS).get());
}
@Test
@@ -600,7 +606,7 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
AccountInfo user1 = newAccountWithFullName("tester", "Test Usre");
// update account without reindex so that account index is stale
- Account.Id accountId = new Account.Id(user1._accountId);
+ Account.Id accountId = Account.id(user1._accountId);
String newName = "Test User";
try (Repository repo = repoManager.openRepository(allUsers)) {
MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsers, repo);
@@ -625,19 +631,22 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
public void rawDocument() throws Exception {
AccountInfo userInfo = gApi.accounts().id(admin.getAccountId().get()).get();
+ Schema<AccountState> schema = indexes.getSearchIndex().getSchema();
Optional<FieldBundle> rawFields =
indexes
.getSearchIndex()
.getRaw(
- new Account.Id(userInfo._accountId),
+ Account.id(userInfo._accountId),
QueryOptions.create(
- IndexConfig.createDefault(),
- 0,
- 1,
- indexes.getSearchIndex().getSchema().getStoredFields().keySet()));
+ IndexConfig.createDefault(), 0, 1, schema.getStoredFields().keySet()));
assertThat(rawFields).isPresent();
- assertThat(rawFields.get().getValue(AccountField.ID)).isEqualTo(userInfo._accountId);
+ if (schema.useLegacyNumericFields()) {
+ assertThat(rawFields.get().getValue(AccountField.ID)).isEqualTo(userInfo._accountId);
+ } else {
+ assertThat(Integer.valueOf(rawFields.get().getValue(AccountField.ID_STR)))
+ .isEqualTo(userInfo._accountId);
+ }
// The field EXTERNAL_ID_STATE is only supported from schema version 6.
if (getSchemaVersion() < 6) {
@@ -695,7 +704,7 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
in.name = name;
in.createEmptyCommit = true;
gApi.projects().create(in);
- return new Project.NameKey(name);
+ return Project.nameKey(name);
}
protected void blockRead(Project.NameKey project, GroupInfo group) throws RestApiException {
@@ -750,7 +759,7 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
return null;
}
- String suffix = getSanitizedMethodName();
+ String suffix = testName.getSanitizedMethodName();
if (name.contains("@")) {
return name + "." + suffix;
}
@@ -777,7 +786,7 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
}
private void addEmails(AccountInfo account, String... emails) throws Exception {
- Account.Id id = new Account.Id(account._accountId);
+ Account.Id id = Account.id(account._accountId);
for (String email : emails) {
accountManager.link(id, AuthRequest.forEmail(email));
}
@@ -802,15 +811,15 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
throws Exception {
List<AccountInfo> result = query.get();
Iterable<Integer> ids = ids(result);
- assertThat(ids)
- .named(format(query, result, accounts))
+ assertWithMessage(format(query, result, accounts))
+ .that(ids)
.containsExactlyElementsIn(ids(accounts))
.inOrder();
return result;
}
protected void assertAccounts(List<AccountState> accounts, AccountInfo... expectedAccounts) {
- assertThat(accounts.stream().map(a -> a.getAccount().getId().get()).collect(toList()))
+ assertThat(accounts.stream().map(a -> a.account().id().get()).collect(toList()))
.containsExactlyElementsIn(
Arrays.asList(expectedAccounts).stream().map(a -> a._accountId).collect(toList()));
}
@@ -860,8 +869,8 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
}
protected void assertMissingField(FieldDef<AccountState, ?> field) {
- assertThat(getSchema().hasField(field))
- .named("schema %s has field %s", getSchemaVersion(), field.getName())
+ assertWithMessage("schema %s has field %s", getSchemaVersion(), field.getName())
+ .that(getSchema().hasField(field))
.isFalse();
}
diff --git a/javatests/com/google/gerrit/server/query/account/BUILD b/javatests/com/google/gerrit/server/query/account/BUILD
index ba0f779e02..5c910a04f3 100644
--- a/javatests/com/google/gerrit/server/query/account/BUILD
+++ b/javatests/com/google/gerrit/server/query/account/BUILD
@@ -9,19 +9,20 @@ java_library(
srcs = ABSTRACT_QUERY_TEST,
visibility = ["//visibility:public"],
runtime_deps = [
+ "//java/com/google/gerrit/lucene",
"//prolog:gerrit-prolog-common",
],
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/lifecycle",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/truth",
"//lib/truth:truth-java8-extension",
],
@@ -39,7 +40,7 @@ junit_tests(
":abstract_query_tests",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:jgit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index ff45c086ae..558658b924 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -16,14 +16,15 @@ package com.google.gerrit.server.query.change;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.extensions.client.ListChangesOption.REVIEWED;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.project.testing.Util.allow;
-import static com.google.gerrit.server.project.testing.Util.category;
-import static com.google.gerrit.server.project.testing.Util.value;
-import static com.google.gerrit.server.project.testing.Util.verified;
+import static com.google.gerrit.server.project.testing.TestLabels.label;
+import static com.google.gerrit.server.project.testing.TestLabels.value;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
@@ -40,11 +41,21 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import com.google.common.truth.ThrowableSubject;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Patch;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AssigneeInput;
@@ -74,14 +85,6 @@ import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -93,6 +96,7 @@ import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.Accounts;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.account.VersionedAccountQueries;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeTriplet;
@@ -107,7 +111,6 @@ import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.util.ManualRequestContext;
@@ -159,7 +162,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Inject protected AllUsersName allUsersName;
@Inject protected BatchUpdate.Factory updateFactory;
@Inject protected ChangeInserter.Factory changeFactory;
- @Inject protected ChangeQueryBuilder queryBuilder;
+ @Inject protected Provider<ChangeQueryBuilder> queryBuilderProvider;
@Inject protected GerritApi gApi;
@Inject protected IdentifiedUser.GenericFactory userFactory;
@Inject protected ChangeIndexCollection indexes;
@@ -180,7 +183,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Inject protected ProjectCache projectCache;
@Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
@Inject protected IdentifiedUser.GenericFactory identifiedUserFactory;
- @Inject protected ProjectConfig.Factory projectConfigFactory;
+
+ @Inject private ProjectConfig.Factory projectConfigFactory;
+ @Inject private ProjectOperations projectOperations;
protected Injector injector;
protected LifecycleManager lifecycle;
@@ -416,13 +421,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byPrivate() throws Exception {
- if (getSchemaVersion() < 40) {
- assertMissingField(ChangeField.PRIVATE);
- assertFailingQuery(
- "is:private", "'is:private' operator is not supported by change index version");
- return;
- }
-
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo), userId);
Account.Id user2 =
@@ -447,12 +445,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byWip() throws Exception {
- if (getSchemaVersion() < 42) {
- assertMissingField(ChangeField.WIP);
- assertFailingQuery("is:wip", "'is:wip' operator is not supported by change index version");
- return;
- }
-
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo), userId);
@@ -469,24 +461,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
@Test
- public void excludeWipChangeFromReviewersDashboardsBeforeSchema42() throws Exception {
- assume().that(getSchemaVersion()).isLessThan(42);
-
- assertMissingField(ChangeField.WIP);
- assertFailingQuery("is:wip", "'is:wip' operator is not supported by change index version");
-
- Account.Id user1 = createAccount("user1");
- TestRepository<Repo> repo = createProject("repo");
- Change change1 = insert(repo, newChangeWorkInProgress(repo), userId);
- assertQuery("reviewer:" + user1, change1);
- gApi.changes().id(change1.getChangeId()).setWorkInProgress();
- assertQuery("reviewer:" + user1, change1);
- }
-
- @Test
public void excludeWipChangeFromReviewersDashboards() throws Exception {
- assume().that(getSchemaVersion()).isAtLeast(42);
-
Account.Id user1 = createAccount("user1");
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChangeWorkInProgress(repo), userId);
@@ -504,17 +479,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
@Test
- public void byStartedBeforeSchema44() throws Exception {
- assume().that(getSchemaVersion()).isLessThan(44);
- assertMissingField(ChangeField.STARTED);
- assertFailingQuery(
- "is:started", "'is:started' operator is not supported by change index version");
- }
-
- @Test
public void byStarted() throws Exception {
- assume().that(getSchemaVersion()).isAtLeast(44);
-
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChangeWorkInProgress(repo));
@@ -550,9 +515,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void restorePendingReviewers() throws Exception {
- assume().that(getSchemaVersion()).isAtLeast(44);
-
- Project.NameKey project = new Project.NameKey("repo");
+ Project.NameKey project = Project.nameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
ConfigInput conf = new ConfigInput();
conf.enableReviewerByEmail = InheritableBoolean.TRUE;
@@ -569,7 +532,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
.reviewer(user2.toString(), ReviewerState.CC, false)
.reviewer(email1)
.reviewer(email2, ReviewerState.CC, false);
- gApi.changes().id(change1.getId().get()).revision("current").review(in);
+ gApi.changes().id(change1.getId().get()).current().review(in);
List<ChangeInfo> changeInfos =
assertQuery(newQuery("is:wip").withOption(DETAILED_LABELS), change1);
@@ -579,8 +542,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
changeInfos.get(0).pendingReviewers;
assertThat(pendingReviewers).isNotNull();
- assertReviewers(
- pendingReviewers.get(ReviewerState.REVIEWER), userId.toString(), user1.toString(), email1);
+ assertReviewers(pendingReviewers.get(ReviewerState.REVIEWER), user1.toString(), email1);
assertReviewers(pendingReviewers.get(ReviewerState.CC), user2.toString(), email2);
assertReviewers(pendingReviewers.get(ReviewerState.REMOVED));
@@ -710,10 +672,10 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery(searchOperator + "\"John Smith\"");
// By invalid query.
- exception.expect(BadRequestException.class);
- exception.expectMessage("invalid value");
// SchemaUtil.getNameParts will return an empty set for query only containing these characters.
- assertQuery(searchOperator + "@.- /_");
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> assertQuery(searchOperator + "@.- /_"));
+ assertThat(thrown).hasMessageThat().contains("invalid value");
}
private Change createChange(TestRepository<Repo> repo, PersonIdent person) throws Exception {
@@ -1054,20 +1016,24 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
public void byLabelMulti() throws Exception {
TestRepository<Repo> repo = createProject("repo");
Project.NameKey project =
- new Project.NameKey(repo.getRepository().getDescription().getRepositoryName());
- ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
+ Project.nameKey(repo.getRepository().getDescription().getRepositoryName());
LabelType verified =
- category("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
- cfg.getLabelSections().put(verified.getName(), verified);
-
- String heads = RefNames.REFS_HEADS + "*";
- allow(cfg, Permission.forLabel(verified().getName()), -1, 1, REGISTERED_USERS, heads);
-
+ label("Verified", value(1, "Passes"), value(0, "No score"), value(-1, "Failed"));
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
+ ProjectConfig cfg = projectConfigFactory.create(project);
+ cfg.load(md);
+ cfg.getLabelSections().put(verified.getName(), verified);
cfg.commit(md);
}
- projectCache.evict(cfg.getProject());
+ projectCache.evict(project);
+
+ String heads = RefNames.REFS_HEADS + "*";
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(verified.getName()).ref(heads).group(REGISTERED_USERS).range(-1, 1))
+ .update();
ReviewInput reviewVerified = new ReviewInput().label("Verified", 1);
ChangeInserter ins = newChange(repo);
@@ -1204,9 +1170,9 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
String q = "status:new limit:" + i;
List<ChangeInfo> results = newQuery(q).get();
- assertThat(results).named(q).hasSize(expectedSize);
- assertThat(results.get(results.size() - 1)._moreChanges)
- .named(q)
+ assertWithMessage(q).that(results).hasSize(expectedSize);
+ assertWithMessage(q)
+ .that(results.get(results.size() - 1)._moreChanges)
.isEqualTo(expectedMoreChanges);
assertThat(results.get(0)._number).isEqualTo(last.getId().get());
}
@@ -1378,15 +1344,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byExtension() throws Exception {
- if (getSchemaVersion() < 52) {
- assertMissingField(ChangeField.EXTENSION);
- String unsupportedOperatorMsg =
- "'extension' operator is not supported by change index version";
- assertFailingQuery("extension:txt", unsupportedOperatorMsg);
- assertFailingQuery("ext:txt", unsupportedOperatorMsg);
- return;
- }
-
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChangeWithFiles(repo, "foo.h", "foo.cc"));
Change change2 = insert(repo, newChangeWithFiles(repo, "bar.H", "bar.CC"));
@@ -1410,15 +1367,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byOnlyExtensions() throws Exception {
- if (getSchemaVersion() < 53) {
- assertMissingField(ChangeField.ONLY_EXTENSIONS);
- String unsupportedOperatorMessage =
- "'onlyextensions' operator is not supported by change index version";
- assertFailingQuery("onlyextensions:txt,jpg", unsupportedOperatorMessage);
- assertFailingQuery("onlyexts:txt,jpg", unsupportedOperatorMessage);
- return;
- }
-
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChangeWithFiles(repo, "foo.h", "foo.cc", "bar.cc"));
Change change2 = insert(repo, newChangeWithFiles(repo, "bar.H", "bar.CC", "foo.H"));
@@ -1466,14 +1414,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byFooter() throws Exception {
- if (getSchemaVersion() < 54) {
- assertMissingField(ChangeField.FOOTER);
- assertFailingQuery(
- "footer:Change-Id=I3d2b978ed455f835d1dad2daa920be0b0ec2ae36",
- "'footer' operator is not supported by change index version");
- return;
- }
-
TestRepository<Repo> repo = createProject("repo");
RevCommit commit1 = repo.parseBody(repo.commit().message("Test\n\nfoo: bar").create());
Change change1 = insert(repo, newChangeForCommit(repo, commit1));
@@ -1523,15 +1463,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byDirectory() throws Exception {
- if (getSchemaVersion() < 55) {
- assertMissingField(ChangeField.DIRECTORY);
- String unsupportedOperatorMessage =
- "'directory' operator is not supported by change index version";
- assertFailingQuery("directory:src/java", unsupportedOperatorMessage);
- assertFailingQuery("dir:src/java", unsupportedOperatorMessage);
- return;
- }
-
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChangeWithFiles(repo, "src/foo.h", "src/foo.cc"));
Change change2 = insert(repo, newChangeWithFiles(repo, "src/java/foo.java", "src/js/bar.js"));
@@ -1598,8 +1529,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byDirectoryRegex() throws Exception {
- assume().that(getSchemaVersion()).isAtLeast(55);
-
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChangeWithFiles(repo, "src/java/foo.java", "src/js/bar.js"));
Change change2 =
@@ -1874,11 +1803,11 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
// change is visible to group ONLY when access is granted
grant(
- new Project.NameKey("repo"),
+ Project.nameKey("repo"),
"refs/*",
Permission.READ,
false,
- new AccountGroup.UUID(gApi.groups().id(g1).get().id));
+ AccountGroup.uuid(gApi.groups().id(g1).get().id));
assertQuery(q + " visibleto:" + g1, change1);
// Both changes are visible to InternalUser
@@ -1929,7 +1858,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
ProjectConfig config = projectConfigFactory.read(md);
AccessSection s = config.getAccessSection(ref, true);
Permission p = s.getPermission(permission, true);
- PermissionRule rule = Util.newRule(config, groupUUID);
+ PermissionRule rule = new PermissionRule(new GroupReference(groupUUID, groupUUID.get()));
rule.setForce(force);
p.add(rule);
config.commit(md);
@@ -2010,7 +1939,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byDraftByExcludesZombieDrafts() throws Exception {
- Project.NameKey project = new Project.NameKey("repo");
+ Project.NameKey project = Project.nameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
Change change = insert(repo, newChange(repo));
Change.Id id = change.getId();
@@ -2179,8 +2108,15 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("conflicts:" + change2.getId().get(), change1);
assertQuery("is:mergeable", change2, change1);
- gApi.changes().id(change1.getChangeId()).revision("current").review(ReviewInput.approve());
- gApi.changes().id(change1.getChangeId()).revision("current").submit();
+ gApi.changes().id(change1.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(change1.getChangeId()).current().submit();
+
+ // If a change gets submitted, the remaining open changes get reindexed asynchronously to update
+ // their mergeability information. If the further assertions in this test are done before the
+ // asynchronous reindex completed they fail because the mergeability information in the index
+ // was not updated yet. To avoid this flakiness reindexAfterRefUpdate is switched off for the
+ // tests and we index change2 synchronously here.
+ gApi.changes().id(change2.getChangeId()).index();
assertQuery("status:open conflicts:" + change2.getId().get());
assertQuery("status:open is:mergeable");
@@ -2249,7 +2185,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("is:reviewer");
assertQuery("reviewer:self");
- gApi.changes().id(change3.getChangeId()).revision("current").review(ReviewInput.recommend());
+ gApi.changes().id(change3.getChangeId()).current().review(ReviewInput.recommend());
assertQuery("is:reviewer", change3);
assertQuery("reviewer:self", change3);
@@ -2330,7 +2266,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void reviewerAndCcByEmail() throws Exception {
- Project.NameKey project = new Project.NameKey("repo");
+ Project.NameKey project = Project.nameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
ConfigInput conf = new ConfigInput();
conf.enableReviewerByEmail = InheritableBoolean.TRUE;
@@ -2353,30 +2289,17 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
rin.state = ReviewerState.CC;
gApi.changes().id(change2.getId().get()).addReviewer(rin);
- if (getSchemaVersion() >= 41) {
- assertQuery("reviewer:\"" + userByEmailWithName + "\"", change1);
- assertQuery("cc:\"" + userByEmailWithName + "\"", change2);
-
- // Omitting the name:
- assertQuery("reviewer:\"" + userByEmail + "\"", change1);
- assertQuery("cc:\"" + userByEmail + "\"", change2);
- } else {
- assertMissingField(ChangeField.REVIEWER_BY_EMAIL);
+ assertQuery("reviewer:\"" + userByEmailWithName + "\"", change1);
+ assertQuery("cc:\"" + userByEmailWithName + "\"", change2);
- assertFailingQuery(
- "reviewer:\"" + userByEmailWithName + "\"", "User " + userByEmailWithName + " not found");
- assertFailingQuery(
- "cc:\"" + userByEmailWithName + "\"", "User " + userByEmailWithName + " not found");
-
- // Omitting the name:
- assertFailingQuery("reviewer:\"" + userByEmail + "\"", "User " + userByEmail + " not found");
- assertFailingQuery("cc:\"" + userByEmail + "\"", "User " + userByEmail + " not found");
- }
+ // Omitting the name:
+ assertQuery("reviewer:\"" + userByEmail + "\"", change1);
+ assertQuery("cc:\"" + userByEmail + "\"", change2);
}
@Test
public void reviewerAndCcByEmailWithQueryForDifferentUser() throws Exception {
- Project.NameKey project = new Project.NameKey("repo");
+ Project.NameKey project = Project.nameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
ConfigInput conf = new ConfigInput();
conf.enableReviewerByEmail = InheritableBoolean.TRUE;
@@ -2398,17 +2321,8 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
rin.state = ReviewerState.CC;
gApi.changes().id(change2.getId().get()).addReviewer(rin);
- if (getSchemaVersion() >= 41) {
- assertQuery("reviewer:\"someone@example.com\"");
- assertQuery("cc:\"someone@example.com\"");
- } else {
- assertMissingField(ChangeField.REVIEWER_BY_EMAIL);
-
- String someoneEmail = "someone@example.com";
- assertFailingQuery(
- "reviewer:\"" + someoneEmail + "\"", "User " + someoneEmail + " not found");
- assertFailingQuery("cc:\"" + someoneEmail + "\"", "User " + someoneEmail + " not found");
- }
+ assertQuery("reviewer:\"someone@example.com\"");
+ assertQuery("cc:\"someone@example.com\"");
}
@Test
@@ -2511,7 +2425,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
public void byCommitsOnBranchNotMergedSkipsMissingChanges() throws Exception {
TestRepository<Repo> repo = createProject("repo");
ObjectId missing =
- repo.branch(new PatchSet.Id(new Change.Id(987654), 1).toRefName())
+ repo.branch(PatchSet.id(Change.id(987654), 1).toRefName())
.commit()
.message("No change for this commit")
.insertChangeId()
@@ -2526,7 +2440,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
List<String> shas = new ArrayList<>(n + extra.size());
extra.forEach(i -> shas.add(i.name()));
List<Integer> expectedIds = new ArrayList<>(n);
- Branch.NameKey dest = null;
+ BranchNameKey dest = null;
for (int i = 0; i < n; i++) {
ChangeInserter ins = newChange(repo);
insert(repo, ins);
@@ -2542,15 +2456,15 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
queryProvider.get().byCommitsOnBranchNotMerged(repo.getRepository(), dest, shas, i);
Iterable<Integer> ids = FluentIterable.from(cds).transform(in -> in.getId().get());
String name = "limit " + i;
- assertThat(ids).named(name).hasSize(n);
- assertThat(ids).named(name).containsExactlyElementsIn(expectedIds);
+ assertWithMessage(name).that(ids).hasSize(n);
+ assertWithMessage(name).that(ids).containsExactlyElementsIn(expectedIds);
}
}
@Test
public void reindexIfStale() throws Exception {
Account.Id user = createAccount("user");
- Project.NameKey project = new Project.NameKey("repo");
+ Project.NameKey project = Project.nameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
Change change = insert(repo, newChange(repo));
String changeId = change.getKey().get();
@@ -2563,8 +2477,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertThat(indexer.reindexIfStale(project, change.getId()).get()).isFalse();
// Delete edit ref behind index's back.
- RefUpdate ru =
- repo.getRepository().updateRef(RefNames.refsEdit(user, change.getId(), ps.getId()));
+ RefUpdate ru = repo.getRepository().updateRef(RefNames.refsEdit(user, change.getId(), ps.id()));
ru.setForceUpdate(true);
assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
@@ -2648,13 +2561,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void revertOf() throws Exception {
- if (getSchemaVersion() < 45) {
- assertMissingField(ChangeField.REVERT_OF);
- assertFailingQuery(
- "revertof:1", "'revertof' operator is not supported by change index version");
- return;
- }
-
TestRepository<Repo> repo = createProject("repo");
// Create two commits and revert second commit (initial commit can't be reverted)
Change initial = insert(repo, newChange(repo));
@@ -2667,8 +2573,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
gApi.changes().id(changeToRevert.id).current().submit();
ChangeInfo changeThatReverts = gApi.changes().id(changeToRevert.id).revert().get();
- assertQueryByIds(
- "revertof:" + changeToRevert._number, new Change.Id(changeThatReverts._number));
+ assertQueryByIds("revertof:" + changeToRevert._number, Change.id(changeThatReverts._number));
}
/** Change builder for helping in tests for dashboard sections. */
@@ -3123,27 +3028,27 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
Change change1 = insert(repo, newChange(repo));
Change change2 = insert(repo, newChangeForBranch(repo, "stable"));
- String queries =
+ String queryListText =
"query1\tproject:repo\n"
+ "query2\tproject:repo status:open\n"
+ "query3\tproject:repo branch:stable\n"
+ "query4\tproject:repo branch:other";
try (TestRepository<Repo> allUsers =
- new TestRepository<>(repoManager.openRepository(allUsersName))) {
- String refsUsers = RefNames.refsUsers(userId);
- allUsers.branch(refsUsers).commit().add("queries", queries).create();
-
- Ref userRef = allUsers.getRepository().exactRef(refsUsers);
- assertThat(userRef).isNotNull();
+ new TestRepository<>(repoManager.openRepository(allUsersName));
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsersName)) {
+ VersionedAccountQueries queries = VersionedAccountQueries.forUser(userId);
+ queries.load(md);
+ queries.setQueryList(queryListText);
+ queries.commit(md);
}
assertThatQueryException("query:foo").hasMessageThat().isEqualTo("Unknown named query: foo");
assertQuery("query:query1", change2, change1);
assertQuery("query:query2", change2, change1);
- gApi.changes().id(change1.getChangeId()).revision("current").review(ReviewInput.approve());
- gApi.changes().id(change1.getChangeId()).revision("current").submit();
+ gApi.changes().id(change1.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(change1.getChangeId()).current().submit();
assertQuery("query:query2", change2);
assertQuery("query:query3", change2);
assertQuery("query:query4");
@@ -3221,6 +3126,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery(ChangeIndexPredicate.none());
+ ChangeQueryBuilder queryBuilder = queryBuilderProvider.get();
for (Predicate<ChangeData> matchingOneChange :
ImmutableList.of(
// One index query, one post-filtering query.
@@ -3293,7 +3199,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
branch = "refs/heads/" + branch;
}
- Change.Id id = new Change.Id(seq.nextChangeId());
+ Change.Id id = Change.id(seq.nextChangeId());
ChangeInserter ins =
changeFactory
.create(id, commit, branch)
@@ -3321,7 +3227,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
Timestamp createdOn)
throws Exception {
Project.NameKey project =
- new Project.NameKey(repo.getRepository().getDescription().getRepositoryName());
+ Project.nameKey(repo.getRepository().getDescription().getRepositoryName());
Account.Id ownerId = owner != null ? owner : userId;
IdentifiedUser user = userFactory.create(ownerId);
try (BatchUpdate bu = updateFactory.create(project, user, createdOn)) {
@@ -3340,7 +3246,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
PatchSetInserter inserter =
patchSetFactory
- .create(changeNotesFactory.createChecked(c), new PatchSet.Id(c.getId(), n), commit)
+ .create(changeNotesFactory.createChecked(c), PatchSet.id(c.getId(), n), commit)
.setFireRevisionCreated(false)
.setValidate(false);
try (BatchUpdate bu = updateFactory.create(c.getProject(), user, TimeUtil.nowTs());
@@ -3380,7 +3286,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
protected TestRepository<Repo> createProject(String name) throws Exception {
gApi.projects().create(name).get();
- return new TestRepository<>(repoManager.openRepository(new Project.NameKey(name)));
+ return new TestRepository<>(repoManager.openRepository(Project.nameKey(name)));
}
protected TestRepository<Repo> createProject(String name, String parent) throws Exception {
@@ -3388,7 +3294,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
input.name = name;
input.parent = parent;
gApi.projects().create(input).get();
- return new TestRepository<>(repoManager.openRepository(new Project.NameKey(name)));
+ return new TestRepository<>(repoManager.openRepository(Project.nameKey(name)));
}
protected QueryRequest newQuery(Object query) {
@@ -3412,8 +3318,8 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
throws Exception {
List<ChangeInfo> result = query.get();
Iterable<Change.Id> ids = ids(result);
- assertThat(ids)
- .named(format(query.getQuery(), ids, changes))
+ assertWithMessage(format(query.getQuery(), ids, changes))
+ .that(ids)
.containsExactlyElementsIn(Arrays.asList(changes))
.inOrder();
return result;
@@ -3425,8 +3331,8 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
.map(ChangeData::getId)
.collect(toImmutableList());
Change.Id[] expectedIds = Arrays.stream(changes).map(Change::getId).toArray(Change.Id[]::new);
- assertThat(actualIds)
- .named(format(predicate.toString(), actualIds, expectedIds))
+ assertWithMessage(format(predicate.toString(), actualIds, expectedIds))
+ .that(actualIds)
.containsExactlyElementsIn(expectedIds)
.inOrder();
}
@@ -3457,7 +3363,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
.append(c.changeId)
.append("), ")
.append("dest=")
- .append(new Branch.NameKey(new Project.NameKey(c.project), c.branch))
+ .append(BranchNameKey.create(Project.nameKey(c.project), c.branch))
.append(", ")
.append("status=")
.append(c.status)
@@ -3478,7 +3384,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
protected static Iterable<Change.Id> ids(Iterable<ChangeInfo> changes) {
- return Streams.stream(changes).map(c -> new Change.Id(c._number)).collect(toList());
+ return Streams.stream(changes).map(c -> Change.id(c._number)).collect(toList());
}
protected static long lastUpdatedMs(Change c) {
@@ -3515,8 +3421,8 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
protected void assertMissingField(FieldDef<ChangeData, ?> field) {
- assertThat(getSchema().hasField(field))
- .named("schema %s has field %s", getSchemaVersion(), field.getName())
+ assertWithMessage("schema %s has field %s", getSchemaVersion(), field.getName())
+ .that(getSchema().hasField(field))
.isFalse();
}
diff --git a/javatests/com/google/gerrit/server/query/change/BUILD b/javatests/com/google/gerrit/server/query/change/BUILD
index 8347484e2e..d0162d3810 100644
--- a/javatests/com/google/gerrit/server/query/change/BUILD
+++ b/javatests/com/google/gerrit/server/query/change/BUILD
@@ -1,7 +1,10 @@
load("@rules_java//java:defs.bzl", "java_library")
load("//tools/bzl:junit.bzl", "junit_tests")
-ABSTRACT_QUERY_TEST = ["AbstractQueryChangesTest.java"]
+ABSTRACT_QUERY_TEST = [
+ "AbstractQueryChangesTest.java",
+ "LuceneQueryChangesTest.java",
+]
java_library(
name = "abstract_query_tests",
@@ -9,48 +12,53 @@ java_library(
srcs = ABSTRACT_QUERY_TEST,
visibility = ["//visibility:public"],
runtime_deps = [
+ "//java/com/google/gerrit/lucene",
"//prolog:gerrit-prolog-common",
],
deps = [
+ "//java/com/google/gerrit/acceptance/testsuite/project",
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/lifecycle",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/project/testing:project-test-util",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/truth",
],
)
-LUCENE_QUERY_TEST = ["LuceneQueryChangesTest.java"]
+LUCENE_QUERY_TEST = [
+ "LuceneQueryChangesLatestIndexVersionTest.java",
+ "LuceneQueryChangesPreviousIndexVersionTest.java",
+]
-junit_tests(
- name = "lucene_query_test",
+[junit_tests(
+ name = f[:f.index(".")],
size = "large",
- srcs = LUCENE_QUERY_TEST,
+ srcs = [f],
visibility = ["//visibility:public"],
deps = [
":abstract_query_tests",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/truth",
],
-)
+) for f in LUCENE_QUERY_TEST]
junit_tests(
name = "small_tests",
@@ -61,15 +69,15 @@ junit_tests(
),
visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/exceptions",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/proto/testing",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/cache/testing",
+ "//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit",
"//lib/truth",
"//lib/truth:truth-proto-extension",
"//proto:cache_java_proto",
diff --git a/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java b/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
index aba0018384..e42230ffbc 100644
--- a/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
+++ b/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
@@ -17,26 +17,36 @@ package com.google.gerrit.server.query.change;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.TestChanges;
+import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class ChangeDataTest extends GerritBaseTests {
+public class ChangeDataTest {
@Test
public void setPatchSetsClearsCurrentPatchSet() throws Exception {
- Project.NameKey project = new Project.NameKey("project");
- ChangeData cd = ChangeData.createForTest(project, new Change.Id(1), 1);
- cd.setChange(TestChanges.newChange(project, new Account.Id(1000)));
+ Project.NameKey project = Project.nameKey("project");
+ ChangeData cd = ChangeData.createForTest(project, Change.id(1), 1, ObjectId.zeroId());
+ cd.setChange(TestChanges.newChange(project, Account.id(1000)));
PatchSet curr1 = cd.currentPatchSet();
- int currId = curr1.getId().get();
- PatchSet ps1 = new PatchSet(new PatchSet.Id(cd.getId(), currId + 1));
- PatchSet ps2 = new PatchSet(new PatchSet.Id(cd.getId(), currId + 2));
+ int currId = curr1.id().get();
+ PatchSet ps1 = newPatchSet(cd.getId(), currId + 1);
+ PatchSet ps2 = newPatchSet(cd.getId(), currId + 2);
cd.setPatchSets(ImmutableList.of(ps1, ps2));
PatchSet curr2 = cd.currentPatchSet();
assertThat(curr2).isNotSameInstanceAs(curr1);
}
+
+ private static PatchSet newPatchSet(Change.Id changeId, int num) {
+ return PatchSet.builder()
+ .id(PatchSet.id(changeId, num))
+ .commitId(ObjectId.zeroId())
+ .uploader(Account.id(1234))
+ .createdOn(TimeUtil.nowTs())
+ .build();
+ }
}
diff --git a/javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java b/javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java
index e550f8e865..00c1a8083c 100644
--- a/javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java
+++ b/javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java
@@ -25,11 +25,10 @@ import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.proto.testing.SerializedClassSubject;
import com.google.gerrit.server.cache.proto.Cache.ConflictKeyProto;
-import com.google.gerrit.testing.GerritBaseTests;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class ConflictKeyTest extends GerritBaseTests {
+public class ConflictKeyTest {
@Test
public void ffOnlyPreservesInputOrder() {
ObjectId id1 = ObjectId.fromString("badc0feebadc0feebadc0feebadc0feebadc0fee");
diff --git a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesLatestIndexVersionTest.java b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesLatestIndexVersionTest.java
new file mode 100644
index 0000000000..52a9170fe1
--- /dev/null
+++ b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesLatestIndexVersionTest.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.gerrit.testing.IndexConfig;
+import org.eclipse.jgit.lib.Config;
+
+public class LuceneQueryChangesLatestIndexVersionTest extends LuceneQueryChangesTest {
+ @ConfigSuite.Default
+ public static Config defaultConfig() {
+ return IndexConfig.createForLucene();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesPreviousIndexVersionTest.java b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesPreviousIndexVersionTest.java
new file mode 100644
index 0000000000..62483fa0b7
--- /dev/null
+++ b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesPreviousIndexVersionTest.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.common.collect.Iterables;
+import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.gerrit.testing.IndexConfig;
+import com.google.gerrit.testing.IndexVersions;
+import org.eclipse.jgit.lib.Config;
+
+public class LuceneQueryChangesPreviousIndexVersionTest extends LuceneQueryChangesTest {
+ @ConfigSuite.Default
+ public static Config againstPreviousIndexVersion() {
+ // the current schema version is already tested by the inherited default config suite
+ return Iterables.getOnlyElement(
+ IndexVersions.asConfigMap(
+ ChangeSchemaDefinitions.INSTANCE,
+ IndexVersions.getWithoutLatest(ChangeSchemaDefinitions.INSTANCE),
+ "againstIndexVersion",
+ IndexConfig.createForLucene())
+ .values());
+ }
+}
diff --git a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
index 2ea198f6d8..6a83fb9d9c 100644
--- a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
@@ -14,37 +14,21 @@
package com.google.gerrit.server.query.change;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
-import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.InMemoryModule;
import com.google.gerrit.testing.InMemoryRepositoryManager.Repo;
-import com.google.gerrit.testing.IndexConfig;
-import com.google.gerrit.testing.IndexVersions;
import com.google.inject.Guice;
import com.google.inject.Injector;
-import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
-public class LuceneQueryChangesTest extends AbstractQueryChangesTest {
- @ConfigSuite.Default
- public static Config defaultConfig() {
- return IndexConfig.createForLucene();
- }
-
- @ConfigSuite.Configs
- public static Map<String, Config> againstPreviousIndexVersion() {
- // the current schema version is already tested by the inherited default config suite
- List<Integer> schemaVersions = IndexVersions.getWithoutLatest(ChangeSchemaDefinitions.INSTANCE);
- return IndexVersions.asConfigMap(
- ChangeSchemaDefinitions.INSTANCE, schemaVersions, "againstIndexVersion", defaultConfig());
- }
-
+public abstract class LuceneQueryChangesTest extends AbstractQueryChangesTest {
@Override
protected Injector createInjector() {
Config luceneConfig = new Config(config);
@@ -76,8 +60,10 @@ public class LuceneQueryChangesTest extends AbstractQueryChangesTest {
Change change1 = insert(repo, newChange(repo), userId);
String nameEmail = user.asIdentifiedUser().getNameEmail();
- exception.expect(BadRequestException.class);
- exception.expectMessage("Cannot create full-text query with value: \\");
- assertQuery("owner: \"" + nameEmail + "\"\\", change1);
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> assertQuery("owner: \"" + nameEmail + "\"\\", change1));
+ assertThat(thrown).hasMessageThat().contains("Cannot create full-text query with value: \\");
}
}
diff --git a/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java b/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
index 135e9c268d..72fdd149b3 100644
--- a/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
+++ b/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
@@ -17,13 +17,13 @@ package com.google.gerrit.server.query.change;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
import java.util.Arrays;
+import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class RegexPathPredicateTest extends GerritBaseTests {
+public class RegexPathPredicateTest {
@Test
public void prefixOnlyOptimization() {
RegexPathPredicate p = predicate("^a/b/.*");
@@ -83,7 +83,8 @@ public class RegexPathPredicateTest extends GerritBaseTests {
private static ChangeData change(String... files) {
Arrays.sort(files);
- ChangeData cd = ChangeData.createForTest(new Project.NameKey("project"), new Change.Id(1), 1);
+ ChangeData cd =
+ ChangeData.createForTest(Project.nameKey("project"), Change.id(1), 1, ObjectId.zeroId());
cd.setCurrentFilePaths(Arrays.asList(files));
return cd;
}
diff --git a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
index 4a3c75577d..d80eac0c47 100644
--- a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
@@ -15,11 +15,15 @@
package com.google.gerrit.server.query.group;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.accounts.AccountInput;
import com.google.gerrit.extensions.api.groups.GroupInput;
@@ -32,8 +36,6 @@ import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -57,6 +59,7 @@ import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.testing.GerritServerTests;
+import com.google.gerrit.testing.GerritTestName;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
@@ -68,10 +71,13 @@ import java.util.Optional;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
@Ignore
public abstract class AbstractQueryGroupsTest extends GerritServerTests {
+ @Rule public final GerritTestName testName = new GerritTestName();
+
@Inject protected Accounts accounts;
@Inject @ServerInitiated protected Provider<AccountsUpdate> accountsUpdate;
@@ -184,7 +190,7 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
@Test
public void byInname() throws Exception {
- String namePart = getSanitizedMethodName();
+ String namePart = testName.getSanitizedMethodName();
namePart = CharMatcher.is('_').removeFrom(namePart);
GroupInfo group1 = createGroup("group-" + namePart);
@@ -204,9 +210,9 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
assertQuery("description:non-existing");
- exception.expect(BadRequestException.class);
- exception.expectMessage("description operator requires a value");
- assertQuery("description:\"\"");
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> assertQuery("description:\"\""));
+ assertThat(thrown).hasMessageThat().contains("description operator requires a value");
}
@Test
@@ -340,7 +346,7 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
// update group in the database so that group index is stale
String newDescription = "barY";
- AccountGroup.UUID groupUuid = new AccountGroup.UUID(group1.id);
+ AccountGroup.UUID groupUuid = AccountGroup.uuid(group1.id);
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setDescription(newDescription).build();
groupsUpdateProvider.get().updateGroupInNoteDb(groupUuid, groupUpdate);
@@ -356,7 +362,7 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
@Test
public void rawDocument() throws Exception {
GroupInfo group1 = createGroup(name("group1"));
- AccountGroup.UUID uuid = new AccountGroup.UUID(group1.id);
+ AccountGroup.UUID uuid = AccountGroup.uuid(group1.id);
Optional<FieldBundle> rawFields =
indexes
@@ -376,7 +382,7 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
@Test
public void byDeletedGroup() throws Exception {
GroupInfo group = createGroup(name("group"));
- AccountGroup.UUID uuid = new AccountGroup.UUID(group.id);
+ AccountGroup.UUID uuid = AccountGroup.uuid(group.id);
String query = "uuid:" + uuid;
assertQuery(query, group);
@@ -459,8 +465,8 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
throws Exception {
List<GroupInfo> result = query.get();
Iterable<String> uuids = uuids(result);
- assertThat(uuids)
- .named(format(query, result, groups))
+ assertWithMessage(format(query, result, groups))
+ .that(uuids)
.containsExactlyElementsIn(uuids(groups))
.inOrder();
return result;
@@ -535,7 +541,7 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
return null;
}
- return name + "_" + getSanitizedMethodName();
+ return name + "_" + testName.getSanitizedMethodName();
}
protected int getSchemaVersion() {
diff --git a/javatests/com/google/gerrit/server/query/group/BUILD b/javatests/com/google/gerrit/server/query/group/BUILD
index 1271f4e1ed..e14350f093 100644
--- a/javatests/com/google/gerrit/server/query/group/BUILD
+++ b/javatests/com/google/gerrit/server/query/group/BUILD
@@ -8,17 +8,18 @@ java_library(
testonly = True,
srcs = ABSTRACT_QUERY_TEST,
visibility = ["//visibility:public"],
+ runtime_deps = ["//java/com/google/gerrit/lucene"],
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/lifecycle",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/truth",
"//lib/truth:truth-java8-extension",
],
@@ -36,7 +37,7 @@ junit_tests(
":abstract_query_tests",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:jgit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
index 08ef2b0e05..dfd7928bf0 100644
--- a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
@@ -15,12 +15,16 @@
package com.google.gerrit.server.query.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.access.AccessSectionInfo;
import com.google.gerrit.extensions.api.access.PermissionInfo;
@@ -37,8 +41,6 @@ import com.google.gerrit.index.Schema;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -57,6 +59,7 @@ import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.testing.GerritServerTests;
+import com.google.gerrit.testing.GerritTestName;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
@@ -68,10 +71,13 @@ import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
@Ignore
public abstract class AbstractQueryProjectsTest extends GerritServerTests {
+ @Rule public final GerritTestName testName = new GerritTestName();
+
@Inject protected Accounts accounts;
@Inject @ServerInitiated protected Provider<AccountsUpdate> accountsUpdate;
@@ -185,7 +191,7 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
@Test
public void byInname() throws Exception {
- String namePart = getSanitizedMethodName();
+ String namePart = testName.getSanitizedMethodName();
namePart = CharMatcher.is('_').removeFrom(namePart);
ProjectInfo project1 = createProject(name("project1-" + namePart));
@@ -207,9 +213,9 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
assertQuery("description:non-existing");
- exception.expect(BadRequestException.class);
- exception.expectMessage("description operator requires a value");
- assertQuery("description:\"\"");
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> assertQuery("description:\"\""));
+ assertThat(thrown).hasMessageThat().contains("description operator requires a value");
}
@Test
@@ -224,16 +230,18 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
@Test
public void byState_emptyQuery() throws Exception {
- exception.expect(BadRequestException.class);
- exception.expectMessage("state operator requires a value");
- assertQuery("state:\"\"");
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> assertQuery("state:\"\""));
+ assertThat(thrown).hasMessageThat().contains("state operator requires a value");
}
@Test
public void byState_badQuery() throws Exception {
- exception.expect(BadRequestException.class);
- exception.expectMessage("state operator must be either 'active' or 'read-only'");
- assertQuery("state:bla");
+ BadRequestException thrown =
+ assertThrows(BadRequestException.class, () -> assertQuery("state:bla"));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("state operator must be either 'active' or 'read-only'");
}
@Test
@@ -375,8 +383,8 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
throws Exception {
List<ProjectInfo> result = query.get();
Iterable<String> names = names(result);
- assertThat(names)
- .named(format(query, result, projects))
+ assertWithMessage(format(query, result, projects))
+ .that(names)
.containsExactlyElementsIn(names(projects))
.inOrder();
return result;
@@ -443,6 +451,6 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
return null;
}
- return name + "_" + getSanitizedMethodName();
+ return name + "_" + testName.getSanitizedMethodName();
}
}
diff --git a/javatests/com/google/gerrit/server/query/project/BUILD b/javatests/com/google/gerrit/server/query/project/BUILD
index 5afc7da590..984d82470f 100644
--- a/javatests/com/google/gerrit/server/query/project/BUILD
+++ b/javatests/com/google/gerrit/server/query/project/BUILD
@@ -8,18 +8,19 @@ java_library(
testonly = True,
srcs = ABSTRACT_QUERY_TEST,
visibility = ["//visibility:public"],
+ runtime_deps = ["//java/com/google/gerrit/lucene"],
deps = [
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index/project",
"//java/com/google/gerrit/lifecycle",
- "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
"//lib/truth",
],
)
@@ -36,7 +37,7 @@ junit_tests(
":abstract_query_tests",
"//java/com/google/gerrit/index/project",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:jgit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/javatests/com/google/gerrit/server/rules/BUILD b/javatests/com/google/gerrit/server/rules/BUILD
index 1e335db063..250b0cefba 100644
--- a/javatests/com/google/gerrit/server/rules/BUILD
+++ b/javatests/com/google/gerrit/server/rules/BUILD
@@ -8,14 +8,15 @@ junit_tests(
runtime_deps = ["//prolog:gerrit-prolog-common"],
deps = [
"//java/com/google/gerrit/common:server",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/project/testing:project-test-util",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib/mockito",
"//lib/prolog:runtime",
"//lib/truth",
],
diff --git a/javatests/com/google/gerrit/server/rules/GerritCommonTest.java b/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
index 180c16b26d..9d7afbc8cb 100644
--- a/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
+++ b/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
@@ -14,10 +14,13 @@
package com.google.gerrit.server.rules;
-import static org.easymock.EasyMock.expect;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.server.project.testing.Util;
+import com.google.gerrit.server.project.testing.TestLabels;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.AbstractModule;
import com.googlecode.prolog_cafe.exceptions.CompileException;
@@ -29,7 +32,6 @@ import com.googlecode.prolog_cafe.lang.SymbolTerm;
import java.io.PushbackReader;
import java.io.StringReader;
import java.util.Arrays;
-import org.easymock.EasyMock;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
import org.junit.Test;
@@ -56,10 +58,10 @@ public class GerritCommonTest extends PrologTestCase {
@Override
protected void setUpEnvironment(PrologEnvironment env) throws Exception {
- LabelTypes labelTypes = new LabelTypes(Arrays.asList(Util.codeReview(), Util.verified()));
- ChangeData cd = EasyMock.createMock(ChangeData.class);
- expect(cd.getLabelTypes()).andStubReturn(labelTypes);
- EasyMock.replay(cd);
+ LabelTypes labelTypes =
+ new LabelTypes(Arrays.asList(TestLabels.codeReview(), TestLabels.verified()));
+ ChangeData cd = mock(ChangeData.class);
+ when(cd.getLabelTypes()).thenReturn(labelTypes);
env.set(StoredValues.CHANGE_DATA, cd);
}
@@ -82,11 +84,14 @@ public class GerritCommonTest extends PrologTestCase {
throw new CompileException("Cannot consult " + nameTerm);
}
- exception.expect(ReductionLimitException.class);
- exception.expectMessage("exceeded reduction limit of 1300");
- env.once(
- Prolog.BUILTIN,
- "call",
- new StructureTerm(":", SymbolTerm.create("user"), SymbolTerm.create("loopy")));
+ ReductionLimitException thrown =
+ assertThrows(
+ ReductionLimitException.class,
+ () ->
+ env.once(
+ Prolog.BUILTIN,
+ "call",
+ new StructureTerm(":", SymbolTerm.create("user"), SymbolTerm.create("loopy"))));
+ assertThat(thrown).hasMessageThat().contains("exceeded reduction limit of 1300");
}
}
diff --git a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
index 14124fa90c..d8af0e50b5 100644
--- a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
+++ b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
@@ -19,12 +19,11 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.LabelId;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.PatchSetApproval;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
@@ -32,9 +31,9 @@ import java.util.Date;
import java.util.List;
import org.junit.Test;
-public class IgnoreSelfApprovalRuleTest extends GerritBaseTests {
- private static final Change.Id CHANGE_ID = new Change.Id(100);
- private static final PatchSet.Id PS_ID = new PatchSet.Id(CHANGE_ID, 1);
+public class IgnoreSelfApprovalRuleTest {
+ private static final Change.Id CHANGE_ID = Change.id(100);
+ private static final PatchSet.Id PS_ID = PatchSet.id(CHANGE_ID, 1);
private static final LabelType VERIFIED = makeLabel("Verified");
private static final Account.Id USER1 = makeAccount(100001);
@@ -82,16 +81,14 @@ public class IgnoreSelfApprovalRuleTest extends GerritBaseTests {
}
private static PatchSetApproval makeApproval(LabelId labelId, Account.Id accountId, int value) {
- PatchSetApproval.Key key = makeKey(PS_ID, accountId, labelId);
- return new PatchSetApproval(key, (short) value, Date.from(Instant.now()));
- }
-
- private static PatchSetApproval.Key makeKey(
- PatchSet.Id psId, Account.Id accountId, LabelId labelId) {
- return new PatchSetApproval.Key(psId, accountId, labelId);
+ return PatchSetApproval.builder()
+ .key(PatchSetApproval.key(PS_ID, accountId, labelId))
+ .value(value)
+ .granted(Date.from(Instant.now()))
+ .build();
}
private static Account.Id makeAccount(int account) {
- return new Account.Id(account);
+ return Account.id(account);
}
}
diff --git a/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java b/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
index 6eb0747680..8622b32d35 100644
--- a/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
+++ b/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.server.rules;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class PrologRuleEvaluatorTest extends GerritBaseTests {
+public class PrologRuleEvaluatorTest {
@Test
public void validLabelNamesAreKept() {
diff --git a/javatests/com/google/gerrit/server/rules/PrologTestCase.java b/javatests/com/google/gerrit/server/rules/PrologTestCase.java
index f4d8eacb3d..c2b6dbb4c1 100644
--- a/javatests/com/google/gerrit/server/rules/PrologTestCase.java
+++ b/javatests/com/google/gerrit/server/rules/PrologTestCase.java
@@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.Guice;
import com.google.inject.Module;
import com.googlecode.prolog_cafe.exceptions.CompileException;
@@ -45,7 +44,7 @@ import org.junit.Ignore;
/** Base class for any tests written in Prolog. */
@Ignore
-public abstract class PrologTestCase extends GerritBaseTests {
+public abstract class PrologTestCase {
private static final SymbolTerm test_1 = SymbolTerm.intern("test", 1);
private String pkg;
diff --git a/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java b/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
index e5890c936b..d5ddeff88f 100644
--- a/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
+++ b/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
@@ -21,20 +21,20 @@ import static com.google.gerrit.server.schema.testing.AllProjectsCreatorTestUtil
import static com.google.gerrit.server.schema.testing.AllProjectsCreatorTestUtil.getAllProjectsWithoutDefaultAcls;
import static com.google.gerrit.server.schema.testing.AllProjectsCreatorTestUtil.getDefaultAllProjectsWithAllDefaultSections;
import static com.google.gerrit.server.schema.testing.AllProjectsCreatorTestUtil.readAllProjectsConfig;
+import static com.google.gerrit.truth.ConfigSubject.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.entities.AccountGroup;
+import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.Sequences;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.Config;
@@ -43,7 +43,7 @@ import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
-public class AllProjectsCreatorTest extends GerritBaseTests {
+public class AllProjectsCreatorTest {
private static final LabelType TEST_LABEL =
new LabelType(
"Test-Label",
@@ -127,7 +127,7 @@ public class AllProjectsCreatorTest extends GerritBaseTests {
allProjectsCreator.create(allProjectsInput);
Config config = readAllProjectsConfig(repoManager, allProjectsName);
- assertThat(config.getString("project", null, "description")).isEqualTo(testDescription);
+ assertThat(config).stringValue("project", null, "description").isEqualTo(testDescription);
}
@Test
@@ -143,7 +143,7 @@ public class AllProjectsCreatorTest extends GerritBaseTests {
allProjectsCreator.create(allProjectsInput);
Config config = readAllProjectsConfig(repoManager, allProjectsName);
- assertThat(config.getBoolean("submit", null, "rejectEmptyCommit", false)).isTrue();
+ assertThat(config).booleanValue("submit", null, "rejectEmptyCommit", false).isTrue();
}
@Test
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
index 5c1b20152f..059b7f391f 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
@@ -15,15 +15,15 @@
package com.google.gerrit.server.schema;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.server.schema.NoteDbSchemaUpdater.requiredUpgrades;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
@@ -31,7 +31,6 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.IntBlob;
import com.google.gerrit.server.notedb.RepoSequence;
import com.google.gerrit.server.notedb.Sequences;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.TestUpdateUI;
import java.io.IOException;
@@ -44,7 +43,7 @@ import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
import org.junit.Test;
-public class NoteDbSchemaUpdaterTest extends GerritBaseTests {
+public class NoteDbSchemaUpdaterTest {
@Test
public void requiredUpgradesFromNoVersion() throws Exception {
assertThat(requiredUpgrades(0, versions(10))).containsExactly(10).inOrder();
@@ -62,26 +61,20 @@ public class NoteDbSchemaUpdaterTest extends GerritBaseTests {
@Test
public void downgradeNotSupported() throws Exception {
- try {
- requiredUpgrades(14, versions(10, 11, 12, 13));
- assert_().fail("expected StorageException");
- } catch (StorageException e) {
- assertThat(e)
- .hasMessageThat()
- .contains("Cannot downgrade NoteDb schema from version 14 to 13");
- }
+ StorageException thrown =
+ assertThrows(StorageException.class, () -> requiredUpgrades(14, versions(10, 11, 12, 13)));
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Cannot downgrade NoteDb schema from version 14 to 13");
}
@Test
public void skipToFirstVersionNotSupported() throws Exception {
ImmutableSortedSet<Integer> versions = versions(10, 11, 12);
assertThat(requiredUpgrades(9, versions)).containsExactly(10, 11, 12).inOrder();
- try {
- requiredUpgrades(8, versions);
- assert_().fail("expected StorageException");
- } catch (StorageException e) {
- assertThat(e).hasMessageThat().contains("Cannot skip NoteDb schema from version 8 to 10");
- }
+ StorageException thrown =
+ assertThrows(StorageException.class, () -> requiredUpgrades(8, versions));
+ assertThat(thrown).hasMessageThat().contains("Cannot skip NoteDb schema from version 8 to 10");
}
private static class TestUpdate {
@@ -231,12 +224,8 @@ public class NoteDbSchemaUpdaterTest extends GerritBaseTests {
seedGroupSequenceRef();
}
};
- try {
- u.update();
- assert_().fail("expected StorageException");
- } catch (StorageException e) {
- assertThat(e).hasMessageThat().contains("NoteDb change migration was not completed");
- }
+ StorageException thrown = assertThrows(StorageException.class, () -> u.update());
+ assertThat(thrown).hasMessageThat().contains("NoteDb change migration was not completed");
assertThat(u.getMessages()).isEmpty();
assertThat(u.readVersion()).isEmpty();
}
@@ -250,12 +239,8 @@ public class NoteDbSchemaUpdaterTest extends GerritBaseTests {
setNotesMigrationConfig();
}
};
- try {
- u.update();
- assert_().fail("expected StorageException");
- } catch (StorageException e) {
- assertThat(e).hasMessageThat().contains("upgrade to 2.16.x first");
- }
+ StorageException thrown = assertThrows(StorageException.class, () -> u.update());
+ assertThat(thrown).hasMessageThat().contains("upgrade to 2.16.x first");
assertThat(u.getMessages()).isEmpty();
assertThat(u.readVersion()).isEmpty();
}
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionCheckTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionCheckTest.java
new file mode 100644
index 0000000000..a5fd4a2932
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionCheckTest.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.testing.InMemoryRepositoryManager;
+import com.google.inject.ProvisionException;
+import java.io.IOException;
+import java.nio.file.Paths;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NoteDbSchemaVersionCheckTest {
+ private NoteDbSchemaVersionManager versionManager;
+ private SitePaths sitePaths;
+
+ @Before
+ public void setup() throws Exception {
+ AllProjectsName allProjectsName = new AllProjectsName("All-Projects");
+ GitRepositoryManager repoManager = new InMemoryRepositoryManager();
+ repoManager.createRepository(allProjectsName);
+ versionManager = new NoteDbSchemaVersionManager(allProjectsName, repoManager);
+ versionManager.init();
+
+ sitePaths = new SitePaths(Paths.get("/tmp/foo"));
+ }
+
+ @Test
+ public void shouldNotFailIfCurrentVersionIsExpected() {
+ new NoteDbSchemaVersionCheck(versionManager, sitePaths, new Config()).start();
+ // No exceptions should be thrown
+ }
+
+ @Test
+ public void shouldFailIfCurrentVersionIsOneMoreThanExpected() throws IOException {
+ versionManager.increment(NoteDbSchemaVersions.LATEST);
+
+ ProvisionException e =
+ assertThrows(
+ ProvisionException.class,
+ () -> new NoteDbSchemaVersionCheck(versionManager, sitePaths, new Config()).start());
+
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Unsupported schema version " + (NoteDbSchemaVersions.LATEST + 1));
+ }
+
+ @Test
+ public void
+ shouldNotFailWithExperimentalRollingUpgradeEnabledAndCurrentVersionIsOneMoreThanExpected()
+ throws IOException {
+ Config gerritConfig = new Config();
+ gerritConfig.setBoolean("gerrit", null, "experimentalRollingUpgrade", true);
+ versionManager.increment(NoteDbSchemaVersions.LATEST);
+
+ NoteDbSchemaVersionCheck versionCheck =
+ new NoteDbSchemaVersionCheck(versionManager, sitePaths, gerritConfig);
+ versionCheck.start();
+ // No exceptions should be thrown
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
index 9c62d7f870..38e19f76bb 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
@@ -15,20 +15,19 @@
package com.google.gerrit.server.schema;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_VERSION;
+import static com.google.gerrit.entities.RefNames.REFS_VERSION;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Before;
import org.junit.Test;
-public class NoteDbSchemaVersionManagerTest extends GerritBaseTests {
+public class NoteDbSchemaVersionManagerTest {
private NoteDbSchemaVersionManager manager;
private TestRepository<?> tr;
@@ -55,14 +54,10 @@ public class NoteDbSchemaVersionManagerTest extends GerritBaseTests {
public void readInvalid() throws Exception {
ObjectId blobId = tr.blob(" 1 2 3 ");
tr.update(REFS_VERSION, blobId);
- try {
- manager.read();
- assert_().fail("expected StorageException");
- } catch (StorageException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo("invalid value in refs/meta/version blob at " + blobId.name());
- }
+ StorageException thrown = assertThrows(StorageException.class, () -> manager.read());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo("invalid value in refs/meta/version blob at " + blobId.name());
}
@Test
@@ -81,13 +76,9 @@ public class NoteDbSchemaVersionManagerTest extends GerritBaseTests {
@Test
public void incrementWrongOldVersion() throws Exception {
tr.update(REFS_VERSION, tr.blob("123"));
- try {
- manager.increment(456);
- assert_().fail("expected StorageException");
- } catch (StorageException e) {
- assertThat(e)
- .hasMessageThat()
- .isEqualTo("Expected old version 456 for refs/meta/version, found 123");
- }
+ StorageException thrown = assertThrows(StorageException.class, () -> manager.increment(456));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo("Expected old version 456 for refs/meta/version, found 123");
}
}
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java
index 7bc3848f6d..31697fd855 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java
@@ -24,11 +24,10 @@ import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Streams;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.stream.IntStream;
import org.junit.Test;
-public class NoteDbSchemaVersionsTest extends GerritBaseTests {
+public class NoteDbSchemaVersionsTest {
@Test
public void testGuessVersion() {
assertThat(guessVersion(getClass())).isEmpty();
diff --git a/javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java b/javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java
index c5c956c997..da22f76247 100644
--- a/javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java
+++ b/javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.schema;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
@@ -103,7 +103,7 @@ public class ProjectConfigSchemaUpdateTest {
return factory
.read(
new MetaDataUpdate(
- GitReferenceUpdated.DISABLED, new Project.NameKey(ALL_PROJECTS), repo, null))
+ GitReferenceUpdated.DISABLED, Project.nameKey(ALL_PROJECTS), repo, null))
.getConfig();
}
}
diff --git a/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
index 35f580c771..c92a8e0436 100644
--- a/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
+++ b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
@@ -25,7 +25,6 @@ import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Inject;
import java.util.ArrayList;
@@ -36,7 +35,7 @@ import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
-public class SchemaCreatorImplTest extends GerritBaseTests {
+public class SchemaCreatorImplTest {
@Inject private AllProjectsName allProjects;
@Inject private GitRepositoryManager repoManager;
@@ -82,7 +81,7 @@ public class SchemaCreatorImplTest extends GerritBaseTests {
private void assertValueRange(LabelType label, Integer... range) {
List<Integer> rangeList = Arrays.asList(range);
assertThat(rangeList).isNotEmpty();
- assertThat(rangeList).isStrictlyOrdered();
+ assertThat(rangeList).isInStrictOrder();
assertThat(label.getValues().stream().map(v -> (int) v.getValue()))
.containsExactlyElementsIn(rangeList)
diff --git a/javatests/com/google/gerrit/server/schema/TestGroup.java b/javatests/com/google/gerrit/server/schema/TestGroup.java
index 4627e8be0e..cca6d6c918 100644
--- a/javatests/com/google/gerrit/server/schema/TestGroup.java
+++ b/javatests/com/google/gerrit/server/schema/TestGroup.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.schema;
import com.google.auto.value.AutoValue;
-import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.server.util.time.TimeUtil;
import java.sql.Timestamp;
import java.util.Optional;
@@ -47,7 +47,7 @@ public abstract class TestGroup {
public abstract Builder setNameKey(AccountGroup.NameKey nameKey);
public Builder setName(String name) {
- return setNameKey(new AccountGroup.NameKey(name));
+ return setNameKey(AccountGroup.nameKey(name));
}
public abstract Builder setGroupUuid(AccountGroup.UUID uuid);
@@ -66,10 +66,9 @@ public abstract class TestGroup {
public AccountGroup build() {
TestGroup testGroup = autoBuild();
- AccountGroup.NameKey name = testGroup.getNameKey().orElse(new AccountGroup.NameKey("users"));
- AccountGroup.Id id = testGroup.getId().orElse(new AccountGroup.Id(Math.abs(name.hashCode())));
- AccountGroup.UUID uuid =
- testGroup.getGroupUuid().orElse(new AccountGroup.UUID(name + "-UUID"));
+ AccountGroup.NameKey name = testGroup.getNameKey().orElse(AccountGroup.nameKey("users"));
+ AccountGroup.Id id = testGroup.getId().orElse(AccountGroup.id(Math.abs(name.hashCode())));
+ AccountGroup.UUID uuid = testGroup.getGroupUuid().orElse(AccountGroup.uuid(name + "-UUID"));
Timestamp createdOn = testGroup.getCreatedOn().orElseGet(TimeUtil::nowTs);
AccountGroup accountGroup = new AccountGroup(name, id, uuid, createdOn);
testGroup.getOwnerGroupUuid().ifPresent(accountGroup::setOwnerGroupUUID);
diff --git a/javatests/com/google/gerrit/server/update/BUILD b/javatests/com/google/gerrit/server/update/BUILD
index c98b35fc3a..d1030b5ca0 100644
--- a/javatests/com/google/gerrit/server/update/BUILD
+++ b/javatests/com/google/gerrit/server/update/BUILD
@@ -4,15 +4,23 @@ junit_tests(
name = "small_tests",
size = "small",
srcs = glob(["*.java"]),
+ runtime_deps = [
+ "//java/com/google/gerrit/lucene",
+ "//prolog:gerrit-prolog-common",
+ ],
deps = [
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/entities",
+ "//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/truth",
"//lib/truth:truth-java8-extension",
],
diff --git a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
index cca844ebdd..a2f800a012 100644
--- a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
+++ b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
@@ -14,36 +14,67 @@
package com.google.gerrit.server.update;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeInserter;
+import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.logging.RequestId;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.notedb.ChangeUpdate;
+import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.server.notedb.TooManyUpdatesException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryTestEnvironment;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-public class BatchUpdateTest extends GerritBaseTests {
- @Rule public InMemoryTestEnvironment testEnvironment = new InMemoryTestEnvironment();
+public class BatchUpdateTest {
+ private static final int MAX_UPDATES = 4;
+
+ @Rule
+ public InMemoryTestEnvironment testEnvironment =
+ new InMemoryTestEnvironment(
+ () -> {
+ Config cfg = new Config();
+ cfg.setInt("change", null, "maxUpdates", MAX_UPDATES);
+ return cfg;
+ });
- @Inject private GitRepositoryManager repoManager;
@Inject private BatchUpdate.Factory batchUpdateFactory;
+ @Inject private ChangeInserter.Factory changeInserterFactory;
+ @Inject private ChangeNotes.Factory changeNotesFactory;
+ @Inject private GitRepositoryManager repoManager;
+ @Inject private PatchSetInserter.Factory patchSetInserterFactory;
@Inject private Provider<CurrentUser> user;
+ @Inject private Sequences sequences;
private Project.NameKey project;
private TestRepository<Repository> repo;
@Before
public void setUp() throws Exception {
- project = new Project.NameKey("test");
+ project = Project.nameKey("test");
Repository inMemoryRepo = repoManager.createRepository(project);
repo = new TestRepository<>(inMemoryRepo);
@@ -68,4 +99,220 @@ public class BatchUpdateTest extends GerritBaseTests {
assertThat(repo.getRepository().exactRef("refs/heads/master").getObjectId())
.isEqualTo(branchCommit.getId());
}
+
+ @Test
+ public void cannotExceedMaxUpdates() throws Exception {
+ Change.Id id = createChangeWithUpdates(MAX_UPDATES);
+ ObjectId oldMetaId = getMetaId(id);
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.addOp(id, new AddMessageOp("Excessive update"));
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> bu.execute());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(TooManyUpdatesException.message(id, MAX_UPDATES));
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(MAX_UPDATES);
+ assertThat(getMetaId(id)).isEqualTo(oldMetaId);
+ }
+
+ @Test
+ public void cannotExceedMaxUpdatesCountingMultipleChangeUpdatesInSingleBatch() throws Exception {
+ Change.Id id = createChangeWithTwoPatchSets(MAX_UPDATES - 1);
+
+ ObjectId oldMetaId = getMetaId(id);
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.addOp(id, new AddMessageOp("Update on PS1", PatchSet.id(id, 1)));
+ bu.addOp(id, new AddMessageOp("Update on PS2", PatchSet.id(id, 2)));
+ ResourceConflictException thrown =
+ assertThrows(ResourceConflictException.class, () -> bu.execute());
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(TooManyUpdatesException.message(id, MAX_UPDATES));
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(MAX_UPDATES - 1);
+ assertThat(getMetaId(id)).isEqualTo(oldMetaId);
+ }
+
+ @Test
+ public void exceedingMaxUpdatesAllowedWithCompleteNoOp() throws Exception {
+ Change.Id id = createChangeWithUpdates(MAX_UPDATES);
+ ObjectId oldMetaId = getMetaId(id);
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.addOp(
+ id,
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ return false;
+ }
+ });
+ bu.execute();
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(MAX_UPDATES);
+ assertThat(getMetaId(id)).isEqualTo(oldMetaId);
+ }
+
+ @Test
+ public void exceedingMaxUpdatesAllowedWithNoOpAfterPopulatingUpdate() throws Exception {
+ Change.Id id = createChangeWithUpdates(MAX_UPDATES);
+ ObjectId oldMetaId = getMetaId(id);
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.addOp(
+ id,
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ctx.getUpdate(ctx.getChange().currentPatchSetId()).setChangeMessage("No-op");
+ return false;
+ }
+ });
+ bu.execute();
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(MAX_UPDATES);
+ assertThat(getMetaId(id)).isEqualTo(oldMetaId);
+ }
+
+ @Test
+ public void exceedingMaxUpdatesAllowedWithSubmit() throws Exception {
+ Change.Id id = createChangeWithUpdates(MAX_UPDATES);
+ ObjectId oldMetaId = getMetaId(id);
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.addOp(id, new SubmitOp());
+ bu.execute();
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(MAX_UPDATES + 1);
+ assertThat(getMetaId(id)).isNotEqualTo(oldMetaId);
+ }
+
+ @Test
+ public void exceedingMaxUpdatesAllowedWithSubmitAfterOtherOp() throws Exception {
+ Change.Id id = createChangeWithTwoPatchSets(MAX_UPDATES - 1);
+ ObjectId oldMetaId = getMetaId(id);
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.addOp(id, new AddMessageOp("Message on PS1", PatchSet.id(id, 1)));
+ bu.addOp(id, new SubmitOp());
+ bu.execute();
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(MAX_UPDATES + 1);
+ assertThat(getMetaId(id)).isNotEqualTo(oldMetaId);
+ }
+ // Not possible to write a variant of this test that submits first and adds a message second in
+ // the same batch, since submit always comes last.
+
+ @Test
+ public void exceedingMaxUpdatesAllowedWithAbandon() throws Exception {
+ Change.Id id = createChangeWithUpdates(MAX_UPDATES);
+ ObjectId oldMetaId = getMetaId(id);
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.addOp(
+ id,
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ChangeUpdate update = ctx.getUpdate(ctx.getChange().currentPatchSetId());
+ update.setChangeMessage("Abandon");
+ update.setStatus(Change.Status.ABANDONED);
+ return true;
+ }
+ });
+ bu.execute();
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(MAX_UPDATES + 1);
+ assertThat(getMetaId(id)).isNotEqualTo(oldMetaId);
+ }
+
+ private Change.Id createChangeWithUpdates(int totalUpdates) throws Exception {
+ checkArgument(totalUpdates > 0);
+ checkArgument(totalUpdates <= MAX_UPDATES);
+ Change.Id id = Change.id(sequences.nextChangeId());
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.insertChange(
+ changeInserterFactory.create(
+ id, repo.commit().message("Change").insertChangeId().create(), "refs/heads/master"));
+ bu.execute();
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(1);
+ for (int i = 2; i <= totalUpdates; i++) {
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ bu.addOp(id, new AddMessageOp("Update " + i));
+ bu.execute();
+ }
+ }
+ assertThat(getUpdateCount(id)).isEqualTo(totalUpdates);
+ return id;
+ }
+
+ private Change.Id createChangeWithTwoPatchSets(int totalUpdates) throws Exception {
+ Change.Id id = createChangeWithUpdates(totalUpdates - 1);
+ ChangeNotes notes = changeNotesFactory.create(project, id);
+
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
+ ObjectId commitId = repo.amend(notes.getCurrentPatchSet().commitId()).message("PS2").create();
+ bu.addOp(
+ id,
+ patchSetInserterFactory
+ .create(notes, PatchSet.id(id, 2), commitId)
+ .setMessage("Add PS2"));
+ bu.execute();
+ }
+
+ assertThat(getUpdateCount(id)).isEqualTo(totalUpdates);
+ return id;
+ }
+
+ private static class AddMessageOp implements BatchUpdateOp {
+ private final String message;
+ @Nullable private final PatchSet.Id psId;
+
+ AddMessageOp(String message) {
+ this(message, null);
+ }
+
+ AddMessageOp(String message, PatchSet.Id psId) {
+ this.message = message;
+ this.psId = psId;
+ }
+
+ @Override
+ public boolean updateChange(ChangeContext ctx) throws Exception {
+ PatchSet.Id psIdToUpdate = psId;
+ if (psIdToUpdate == null) {
+ psIdToUpdate = ctx.getChange().currentPatchSetId();
+ } else {
+ checkState(
+ ctx.getNotes().getPatchSets().containsKey(psIdToUpdate),
+ "%s not in %s",
+ psIdToUpdate,
+ ctx.getNotes().getPatchSets().keySet());
+ }
+ ctx.getUpdate(psIdToUpdate).setChangeMessage(message);
+ return true;
+ }
+ }
+
+ private int getUpdateCount(Change.Id changeId) throws Exception {
+ return changeNotesFactory.create(project, changeId).getUpdateCount();
+ }
+
+ private ObjectId getMetaId(Change.Id changeId) throws Exception {
+ return repo.getRepository().exactRef(RefNames.changeMetaRef(changeId)).getObjectId();
+ }
+
+ private static class SubmitOp implements BatchUpdateOp {
+ @Override
+ public boolean updateChange(ChangeContext ctx) throws Exception {
+ SubmitRecord sr = new SubmitRecord();
+ sr.status = SubmitRecord.Status.OK;
+ SubmitRecord.Label cr = new SubmitRecord.Label();
+ cr.status = SubmitRecord.Label.Status.OK;
+ cr.appliedBy = ctx.getAccountId();
+ cr.label = "Code-Review";
+ sr.labels = ImmutableList.of(cr);
+ ChangeUpdate update = ctx.getUpdate(ctx.getChange().currentPatchSetId());
+ update.merge(new RequestId(), ImmutableList.of(sr));
+ update.setChangeMessage("Submitted");
+ return true;
+ }
+ }
}
diff --git a/javatests/com/google/gerrit/server/update/RepoViewTest.java b/javatests/com/google/gerrit/server/update/RepoViewTest.java
index b41c66c280..b37e302dbf 100644
--- a/javatests/com/google/gerrit/server/update/RepoViewTest.java
+++ b/javatests/com/google/gerrit/server/update/RepoViewTest.java
@@ -18,8 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
@@ -31,7 +30,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class RepoViewTest extends GerritBaseTests {
+public class RepoViewTest {
private static final String MASTER = "refs/heads/master";
private static final String BRANCH = "refs/heads/branch";
@@ -42,7 +41,7 @@ public class RepoViewTest extends GerritBaseTests {
@Before
public void setUp() throws Exception {
InMemoryRepositoryManager repoManager = new InMemoryRepositoryManager();
- Project.NameKey project = new Project.NameKey("project");
+ Project.NameKey project = Project.nameKey("project");
repo = repoManager.createRepository(project);
tr = new TestRepository<>(repo);
tr.branch(MASTER).commit().create();
diff --git a/javatests/com/google/gerrit/server/util/IdGeneratorTest.java b/javatests/com/google/gerrit/server/util/IdGeneratorTest.java
index e702656bd1..808eca8691 100644
--- a/javatests/com/google/gerrit/server/util/IdGeneratorTest.java
+++ b/javatests/com/google/gerrit/server/util/IdGeneratorTest.java
@@ -17,11 +17,10 @@ package com.google.gerrit.server.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.HashSet;
import org.junit.Test;
-public class IdGeneratorTest extends GerritBaseTests {
+public class IdGeneratorTest {
@Test
public void test1234() {
final HashSet<Integer> seen = new HashSet<>();
diff --git a/javatests/com/google/gerrit/server/util/LabelVoteTest.java b/javatests/com/google/gerrit/server/util/LabelVoteTest.java
index 3048b75deb..bda99a8bf9 100644
--- a/javatests/com/google/gerrit/server/util/LabelVoteTest.java
+++ b/javatests/com/google/gerrit/server/util/LabelVoteTest.java
@@ -15,14 +15,13 @@
package com.google.gerrit.server.util;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.server.util.LabelVote.parse;
import static com.google.gerrit.server.util.LabelVote.parseWithEquals;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class LabelVoteTest extends GerritBaseTests {
+public class LabelVoteTest {
@Test
public void labelVoteParse() {
assertLabelVoteEquals(parse("Code-Review-2"), "Code-Review", -2);
@@ -83,11 +82,6 @@ public class LabelVoteTest extends GerritBaseTests {
}
private void assertParseWithEqualsFails(String value) {
- try {
- parseWithEquals(value);
- assert_().fail("expected IllegalArgumentException when parsing \"%s\"", value);
- } catch (IllegalArgumentException e) {
- // Expected.
- }
+ assertThrows(IllegalArgumentException.class, () -> parseWithEquals(value));
}
}
diff --git a/javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java b/javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java
index 9ea17f394f..025bf847eb 100644
--- a/javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java
+++ b/javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java
@@ -16,10 +16,9 @@ package com.google.gerrit.server.util;
import static org.junit.Assert.assertTrue;
-import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class MostSpecificComparatorTest extends GerritBaseTests {
+public class MostSpecificComparatorTest {
private MostSpecificComparator cmp;
diff --git a/javatests/com/google/gerrit/server/util/SocketUtilTest.java b/javatests/com/google/gerrit/server/util/SocketUtilTest.java
index 018b8dbee6..25114f9360 100644
--- a/javatests/com/google/gerrit/server/util/SocketUtilTest.java
+++ b/javatests/com/google/gerrit/server/util/SocketUtilTest.java
@@ -14,17 +14,18 @@
package com.google.gerrit.server.util;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.server.util.SocketUtil.hostname;
import static com.google.gerrit.server.util.SocketUtil.isIPv6;
import static com.google.gerrit.server.util.SocketUtil.parse;
import static com.google.gerrit.server.util.SocketUtil.resolve;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static java.net.InetAddress.getByName;
import static java.net.InetSocketAddress.createUnresolved;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.google.gerrit.testing.GerritBaseTests;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -32,7 +33,7 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import org.junit.Test;
-public class SocketUtilTest extends GerritBaseTests {
+public class SocketUtilTest {
@Test
public void testIsIPv6() throws UnknownHostException {
final InetAddress ipv6 = getByName("1:2:3:4:5:6:7:8");
@@ -105,16 +106,16 @@ public class SocketUtilTest extends GerritBaseTests {
@Test
public void testParseInvalidIPv6() {
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage("invalid IPv6: [:3");
- parse("[:3", 80);
+ IllegalArgumentException thrown =
+ assertThrows(IllegalArgumentException.class, () -> parse("[:3", 80));
+ assertThat(thrown).hasMessageThat().contains("invalid IPv6: [:3");
}
@Test
public void testParseInvalidPort() {
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage("invalid port: localhost:A");
- parse("localhost:A", 80);
+ IllegalArgumentException thrown =
+ assertThrows(IllegalArgumentException.class, () -> parse("localhost:A", 80));
+ assertThat(thrown).hasMessageThat().contains("invalid port: localhost:A");
}
@Test
diff --git a/javatests/com/google/gerrit/server/util/git/BUILD b/javatests/com/google/gerrit/server/util/git/BUILD
index 0cb7b8aee9..883898f873 100644
--- a/javatests/com/google/gerrit/server/util/git/BUILD
+++ b/javatests/com/google/gerrit/server/util/git/BUILD
@@ -8,20 +8,18 @@ junit_tests(
),
visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server/util/git",
- "//java/com/google/gerrit/testing:gerrit-test-util",
- "//java/com/google/gerrit/truth",
"//lib:gson",
"//lib:guava",
"//lib:guava-retrying",
+ "//lib:jgit",
+ "//lib:jgit-junit",
"//lib:protobuf",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/commons:codec",
"//lib/guice",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
"//lib/truth",
"//lib/truth:truth-java8-extension",
"//lib/truth:truth-proto-extension",
diff --git a/javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java b/javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java
index c5e683fefe..531785fb20 100644
--- a/javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java
+++ b/javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java
@@ -17,20 +17,19 @@ package com.google.gerrit.server.util.git;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
-import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.SubmoduleSubscription;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
-public class SubmoduleSectionParserTest extends GerritBaseTests {
+public class SubmoduleSectionParserTest {
private static final String THIS_SERVER = "http://localhost/";
@Test
public void followMasterBranch() throws Exception {
- Project.NameKey p = new Project.NameKey("proj");
+ Project.NameKey p = Project.nameKey("proj");
Config cfg = new Config();
cfg.fromText(
""
@@ -40,7 +39,7 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ p.get()
+ "\n"
+ "branch = master\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
@@ -48,14 +47,14 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
new SubmoduleSubscription(
- targetBranch, new Branch.NameKey(p, "master"), "localpath-to-a"));
+ targetBranch, BranchNameKey.create(p, "master"), "localpath-to-a"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void followMatchingBranch() throws Exception {
- Project.NameKey p = new Project.NameKey("a");
+ Project.NameKey p = Project.nameKey("a");
Config cfg = new Config();
cfg.fromText(
""
@@ -66,32 +65,32 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = .\n");
- Branch.NameKey targetBranch1 = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch1 = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res1 =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch1).parseAllSections();
Set<SubmoduleSubscription> expected1 =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch1, new Branch.NameKey(p, "master"), "a"));
+ new SubmoduleSubscription(targetBranch1, BranchNameKey.create(p, "master"), "a"));
assertThat(res1).containsExactlyElementsIn(expected1);
- Branch.NameKey targetBranch2 = new Branch.NameKey(new Project.NameKey("project"), "somebranch");
+ BranchNameKey targetBranch2 = BranchNameKey.create(Project.nameKey("project"), "somebranch");
Set<SubmoduleSubscription> res2 =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch2).parseAllSections();
Set<SubmoduleSubscription> expected2 =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch2, new Branch.NameKey(p, "somebranch"), "a"));
+ new SubmoduleSubscription(targetBranch2, BranchNameKey.create(p, "somebranch"), "a"));
assertThat(res2).containsExactlyElementsIn(expected2);
}
@Test
public void followAnotherBranch() throws Exception {
- Project.NameKey p = new Project.NameKey("a");
+ Project.NameKey p = Project.nameKey("a");
Config cfg = new Config();
cfg.fromText(
""
@@ -102,21 +101,21 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = anotherbranch\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p, "anotherbranch"), "a"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p, "anotherbranch"), "a"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void withAnotherURI() throws Exception {
- Project.NameKey p = new Project.NameKey("a");
+ Project.NameKey p = Project.nameKey("a");
Config cfg = new Config();
cfg.fromText(
""
@@ -127,21 +126,21 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = master\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p, "master"), "a"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p, "master"), "a"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void withSlashesInProjectName() throws Exception {
- Project.NameKey p = new Project.NameKey("project/with/slashes/a");
+ Project.NameKey p = Project.nameKey("project/with/slashes/a");
Config cfg = new Config();
cfg.fromText(
""
@@ -152,21 +151,21 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = master\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p, "master"), "a"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p, "master"), "a"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void withSlashesInPath() throws Exception {
- Project.NameKey p = new Project.NameKey("a");
+ Project.NameKey p = Project.nameKey("a");
Config cfg = new Config();
cfg.fromText(
""
@@ -177,22 +176,23 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = master\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p, "master"), "a/b/c/d/e"));
+ new SubmoduleSubscription(
+ targetBranch, BranchNameKey.create(p, "master"), "a/b/c/d/e"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void withMoreSections() throws Exception {
- Project.NameKey p1 = new Project.NameKey("a");
- Project.NameKey p2 = new Project.NameKey("b");
+ Project.NameKey p1 = Project.nameKey("a");
+ Project.NameKey p2 = Project.nameKey("b");
Config cfg = new Config();
cfg.fromText(
""
@@ -209,23 +209,23 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ " branch = master\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p1, "master"), "a"),
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p2, "master"), "b"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p1, "master"), "a"),
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p2, "master"), "b"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void withSubProjectFound() throws Exception {
- Project.NameKey p1 = new Project.NameKey("a/b");
- Project.NameKey p2 = new Project.NameKey("b");
+ Project.NameKey p1 = Project.nameKey("a/b");
+ Project.NameKey p2 = Project.nameKey("b");
Config cfg = new Config();
cfg.fromText(
"\n"
@@ -242,25 +242,25 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = .\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p2, "master"), "b"),
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p1, "master"), "a/b"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p2, "master"), "b"),
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p1, "master"), "a/b"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void withAnInvalidSection() throws Exception {
- Project.NameKey p1 = new Project.NameKey("a");
- Project.NameKey p2 = new Project.NameKey("b");
- Project.NameKey p3 = new Project.NameKey("d");
- Project.NameKey p4 = new Project.NameKey("e");
+ Project.NameKey p1 = Project.nameKey("a");
+ Project.NameKey p2 = Project.nameKey("b");
+ Project.NameKey p3 = Project.nameKey("d");
+ Project.NameKey p4 = Project.nameKey("e");
Config cfg = new Config();
cfg.fromText(
"\n"
@@ -293,15 +293,15 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ " branch = refs/heads/master\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p1, "master"), "a"),
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p4, "master"), "e"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p1, "master"), "a"),
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p4, "master"), "e"));
assertThat(res).containsExactlyElementsIn(expected);
}
@@ -317,7 +317,7 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
// Project "a" doesn't exist
+ "branch = .\\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
@@ -327,7 +327,7 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
@Test
public void withSectionToOtherServer() throws Exception {
- Project.NameKey p1 = new Project.NameKey("a");
+ Project.NameKey p1 = Project.nameKey("a");
Config cfg = new Config();
cfg.fromText(
""
@@ -338,7 +338,7 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = .");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
@@ -348,7 +348,7 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
@Test
public void withRelativeURI() throws Exception {
- Project.NameKey p1 = new Project.NameKey("a");
+ Project.NameKey p1 = Project.nameKey("a");
Config cfg = new Config();
cfg.fromText(
""
@@ -359,21 +359,21 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = master\n");
- Branch.NameKey targetBranch = new Branch.NameKey(new Project.NameKey("project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p1, "master"), "a"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p1, "master"), "a"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void withDeepRelativeURI() throws Exception {
- Project.NameKey p1 = new Project.NameKey("a");
+ Project.NameKey p1 = Project.nameKey("a");
Config cfg = new Config();
cfg.fromText(
""
@@ -384,22 +384,21 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = master\n");
- Branch.NameKey targetBranch =
- new Branch.NameKey(new Project.NameKey("nested/project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("nested/project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p1, "master"), "a"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p1, "master"), "a"));
assertThat(res).containsExactlyElementsIn(expected);
}
@Test
public void withOverlyDeepRelativeURI() throws Exception {
- Project.NameKey p1 = new Project.NameKey("nested/a");
+ Project.NameKey p1 = Project.nameKey("nested/a");
Config cfg = new Config();
cfg.fromText(
""
@@ -410,15 +409,14 @@ public class SubmoduleSectionParserTest extends GerritBaseTests {
+ "\n"
+ "branch = master\n");
- Branch.NameKey targetBranch =
- new Branch.NameKey(new Project.NameKey("nested/project"), "master");
+ BranchNameKey targetBranch = BranchNameKey.create(Project.nameKey("nested/project"), "master");
Set<SubmoduleSubscription> res =
new SubmoduleSectionParser(cfg, THIS_SERVER, targetBranch).parseAllSections();
Set<SubmoduleSubscription> expected =
Sets.newHashSet(
- new SubmoduleSubscription(targetBranch, new Branch.NameKey(p1, "master"), "a"));
+ new SubmoduleSubscription(targetBranch, BranchNameKey.create(p1, "master"), "a"));
assertThat(res).containsExactlyElementsIn(expected);
}
diff --git a/javatests/com/google/gerrit/sshd/BUILD b/javatests/com/google/gerrit/sshd/BUILD
index c010d7c4d3..3e11ff22e2 100644
--- a/javatests/com/google/gerrit/sshd/BUILD
+++ b/javatests/com/google/gerrit/sshd/BUILD
@@ -6,7 +6,6 @@ junit_tests(
deps = [
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/sshd",
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib/mina:sshd",
"//lib/truth",
],
diff --git a/javatests/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java b/javatests/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java
index b663849419..777cb4fb1e 100644
--- a/javatests/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java
+++ b/javatests/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java
@@ -17,13 +17,12 @@ package com.google.gerrit.sshd.commands;
import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.extensions.api.projects.ConfigValue;
-import com.google.gerrit.testing.GerritBaseTests;
import java.util.Collections;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
-public class ProjectConfigParamParserTest extends GerritBaseTests {
+public class ProjectConfigParamParserTest {
private CreateProjectCommand cmd;
diff --git a/javatests/com/google/gerrit/testing/GerritJUnitTest.java b/javatests/com/google/gerrit/testing/GerritJUnitTest.java
index 430f48f832..56dda083f1 100644
--- a/javatests/com/google/gerrit/testing/GerritJUnitTest.java
+++ b/javatests/com/google/gerrit/testing/GerritJUnitTest.java
@@ -15,7 +15,7 @@
package com.google.gerrit.testing;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import org.junit.Test;
@@ -68,7 +68,7 @@ public class GerritJUnitTest {
() -> {
throw new MyException("foo");
});
- assert_().fail("expected AssertionError");
+ assertWithMessage("expected AssertionError").fail();
} catch (AssertionError e) {
assertThat(e).hasMessageThat().contains(IllegalStateException.class.getSimpleName());
assertThat(e).hasMessageThat().contains(MyException.class.getSimpleName());
@@ -81,7 +81,7 @@ public class GerritJUnitTest {
public void assertThrowsThrowsAssertionErrorWhenNothingThrown() {
try {
assertThrows(MyException.class, () -> {});
- assert_().fail("expected AssertionError");
+ assertWithMessage("expected AssertionError").fail();
} catch (AssertionError e) {
assertThat(e).hasMessageThat().contains(MyException.class.getSimpleName());
assertThat(e).hasCauseThat().isNull();
diff --git a/javatests/com/google/gerrit/testing/IndexVersionsTest.java b/javatests/com/google/gerrit/testing/IndexVersionsTest.java
index 36247f8d6a..0362ddc271 100644
--- a/javatests/com/google/gerrit/testing/IndexVersionsTest.java
+++ b/javatests/com/google/gerrit/testing/IndexVersionsTest.java
@@ -16,6 +16,7 @@ package com.google.gerrit.testing;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static com.google.gerrit.testing.IndexVersions.ALL;
import static com.google.gerrit.testing.IndexVersions.CURRENT;
import static com.google.gerrit.testing.IndexVersions.PREVIOUS;
@@ -25,7 +26,7 @@ import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
-public class IndexVersionsTest extends GerritBaseTests {
+public class IndexVersionsTest {
private static final ChangeSchemaDefinitions SCHEMA_DEF = ChangeSchemaDefinitions.INSTANCE;
@Test
@@ -133,8 +134,8 @@ public class IndexVersionsTest extends GerritBaseTests {
}
private void assertIllegalArgument(String value, String expectedMessage) {
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage(expectedMessage);
- get(value);
+ IllegalArgumentException thrown =
+ assertThrows(IllegalArgumentException.class, () -> get(value));
+ assertThat(thrown).hasMessageThat().contains(expectedMessage);
}
}
diff --git a/javatests/com/google/gerrit/util/http/BUILD b/javatests/com/google/gerrit/util/http/BUILD
index 8f6afbb070..4711faa589 100644
--- a/javatests/com/google/gerrit/util/http/BUILD
+++ b/javatests/com/google/gerrit/util/http/BUILD
@@ -4,12 +4,10 @@ junit_tests(
name = "http_tests",
srcs = glob(["**/*.java"]),
deps = [
- "//java/com/google/gerrit/testing:gerrit-test-util",
"//java/com/google/gerrit/util/http",
"//javatests/com/google/gerrit/util/http/testutil",
"//lib:junit",
"//lib:servlet-api-without-neverlink",
- "//lib/easymock",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/util/http/RequestUtilTest.java b/javatests/com/google/gerrit/util/http/RequestUtilTest.java
index adda5e7092..bef9d4b173 100644
--- a/javatests/com/google/gerrit/util/http/RequestUtilTest.java
+++ b/javatests/com/google/gerrit/util/http/RequestUtilTest.java
@@ -18,11 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.util.http.RequestUtil.getEncodedPathInfo;
import static com.google.gerrit.util.http.RequestUtil.getRestPathWithoutIds;
-import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.util.http.testutil.FakeHttpServletRequest;
import org.junit.Test;
-public class RequestUtilTest extends GerritBaseTests {
+public class RequestUtilTest {
@Test
public void getEncodedPathInfo_emptyContextPath() {
assertThat(getEncodedPathInfo(fakeRequest("", "/s", "/foo/bar"))).isEqualTo("/foo/bar");
diff --git a/javatests/com/google/gerrit/util/http/testutil/BUILD b/javatests/com/google/gerrit/util/http/testutil/BUILD
index d7ac5bdf4e..3a67d45daf 100644
--- a/javatests/com/google/gerrit/util/http/testutil/BUILD
+++ b/javatests/com/google/gerrit/util/http/testutil/BUILD
@@ -7,8 +7,8 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//lib:guava",
+ "//lib:jgit",
"//lib:servlet-api",
"//lib/httpcomponents:httpclient",
- "//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/lib/BUILD b/lib/BUILD
index 2ea8f54214..2e5668e224 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -36,6 +36,49 @@ java_library(
)
java_library(
+ name = "jgit",
+ data = ["//lib:LICENSE-jgit"],
+ visibility = ["//visibility:public"],
+ exports = ["@jgit//org.eclipse.jgit:jgit"],
+ runtime_deps = [
+ ":javaewah",
+ "//lib/log:api",
+ ],
+)
+
+java_library(
+ name = "jgit-archive",
+ data = ["//lib:LICENSE-jgit"],
+ visibility = ["//visibility:public"],
+ exports = ["@jgit//org.eclipse.jgit.archive:jgit-archive"],
+ runtime_deps = [":jgit"],
+)
+
+java_library(
+ name = "jgit-junit",
+ testonly = True,
+ data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
+ visibility = ["//visibility:public"],
+ exports = ["@jgit//org.eclipse.jgit.junit:junit"],
+ runtime_deps = [":jgit"],
+)
+
+java_library(
+ name = "jgit-servlet",
+ data = ["//lib:LICENSE-jgit"],
+ visibility = ["//visibility:public"],
+ exports = ["@jgit//org.eclipse.jgit.http.server:jgit-servlet"],
+ runtime_deps = [":jgit"],
+)
+
+java_library(
+ name = "javaewah",
+ data = ["//lib:LICENSE-Apache2.0"],
+ visibility = ["//visibility:public"],
+ exports = ["@javaewah//jar"],
+)
+
+java_library(
name = "protobuf",
data = ["//lib:LICENSE-protobuf"],
visibility = ["//visibility:public"],
@@ -71,6 +114,7 @@ java_library(
name = "caffeine",
data = ["//lib:LICENSE-Apache2.0"],
visibility = [
+ "//java/com/google/gerrit/acceptance:__pkg__",
"//java/com/google/gerrit/server/cache/mem:__pkg__",
],
exports = ["@caffeine//jar"],
@@ -85,6 +129,7 @@ java_library(
name = "caffeine-guava",
data = ["//lib:LICENSE-Apache2.0"],
visibility = [
+ "//java/com/google/gerrit/acceptance:__pkg__",
"//java/com/google/gerrit/server/cache/mem:__pkg__",
],
exports = [":caffeine-guava-renamed"],
@@ -442,13 +487,6 @@ java_library(
)
java_library(
- name = "javassist",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = ["@javassist//jar"],
-)
-
-java_library(
name = "soy",
data = ["//lib:LICENSE-Apache2.0"],
visibility = ["//visibility:public"],
@@ -485,6 +523,18 @@ java_library(
exports = ["@icu4j//jar"],
)
+java_library(
+ name = "javax-annotation",
+ data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
+ visibility = [
+ "//java/com/google/gerrit/acceptance:__pkg__",
+ "//java/com/google/gerrit/extensions:__pkg__",
+ "//java/com/google/gerrit/server:__pkg__",
+ "//plugins:__pkg__",
+ ],
+ exports = ["@javax-annotation//jar"],
+)
+
sh_test(
name = "nongoogle_test",
srcs = ["nongoogle_test.sh"],
diff --git a/lib/LICENSE-shadycss b/lib/LICENSE-shadycss
new file mode 100644
index 0000000000..0fe5c522de
--- /dev/null
+++ b/lib/LICENSE-shadycss
@@ -0,0 +1,20 @@
+# License
+
+Everything in this repo is BSD style license unless otherwise specified.
+
+Copyright (c) 2015 The Polymer Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+* Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/lib/easymock/BUILD b/lib/easymock/BUILD
deleted file mode 100644
index 90c9673d31..0000000000
--- a/lib/easymock/BUILD
+++ /dev/null
@@ -1,26 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-
-java_library(
- name = "easymock",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = ["@easymock//jar"],
- runtime_deps = [
- ":cglib-3_2",
- ":objenesis",
- ],
-)
-
-java_library(
- name = "cglib-3_2",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = ["@cglib-3_2//jar"],
-)
-
-java_library(
- name = "objenesis",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = ["@objenesis//jar"],
-)
diff --git a/lib/errorprone/BUILD b/lib/errorprone/BUILD
new file mode 100644
index 0000000000..456860aaee
--- /dev/null
+++ b/lib/errorprone/BUILD
@@ -0,0 +1,8 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "annotations",
+ data = ["//lib:LICENSE-Apache2.0"],
+ visibility = ["//visibility:public"],
+ exports = ["@error-prone-annotations//jar"],
+)
diff --git a/lib/greenmail/BUILD b/lib/greenmail/BUILD
index e8845e2628..68da16a514 100644
--- a/lib/greenmail/BUILD
+++ b/lib/greenmail/BUILD
@@ -17,7 +17,7 @@ java_library(
data = ["//lib:LICENSE-Apache2.0"],
exports = ["@greenmail//jar"],
runtime_deps = select({
- "//:java9": POST_JDK8_DEPS,
+ "//:java11": POST_JDK8_DEPS,
"//:java_next": POST_JDK8_DEPS,
"//conditions:default": [],
}),
diff --git a/lib/guava.bzl b/lib/guava.bzl
index c36bf14c2f..18a8355c13 100644
--- a/lib/guava.bzl
+++ b/lib/guava.bzl
@@ -1,5 +1,5 @@
-GUAVA_VERSION = "27.1-jre"
+GUAVA_VERSION = "28.1-jre"
-GUAVA_BIN_SHA1 = "e47b59c893079b87743cdcfb6f17ca95c08c592c"
+GUAVA_BIN_SHA1 = "b0e91dcb6a44ffb6221b5027e12a5cb34b841145"
GUAVA_DOC_URL = "https://google.github.io/guava/releases/" + GUAVA_VERSION + "/api/docs/"
diff --git a/lib/jackson/BUILD b/lib/jackson/BUILD
index 3eed77ad6e..d5253a0eb6 100644
--- a/lib/jackson/BUILD
+++ b/lib/jackson/BUILD
@@ -4,6 +4,7 @@ java_library(
name = "jackson-core",
data = ["//lib:LICENSE-Apache2.0"],
visibility = [
+ "//java/com/google/gerrit/acceptance:__pkg__",
"//java/com/google/gerrit/elasticsearch:__pkg__",
"//plugins:__pkg__",
],
diff --git a/lib/jgit/BUILD b/lib/jgit/BUILD
deleted file mode 100644
index e69de29bb2..0000000000
--- a/lib/jgit/BUILD
+++ /dev/null
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
deleted file mode 100644
index db0e1e153f..0000000000
--- a/lib/jgit/jgit.bzl
+++ /dev/null
@@ -1,75 +0,0 @@
-load("//tools/bzl:maven_jar.bzl", "MAVEN_CENTRAL", "maven_jar")
-
-_JGIT_VERS = "5.3.7.202002110540-r"
-
-_DOC_VERS = _JGIT_VERS # Set to _JGIT_VERS unless using a snapshot
-
-JGIT_DOC_URL = "https://archive.eclipse.org/jgit/site/" + _DOC_VERS + "/apidocs"
-
-_JGIT_REPO = MAVEN_CENTRAL # Leave here even if set to MAVEN_CENTRAL.
-
-# set this to use a local version.
-# "/home/<user>/projects/jgit"
-LOCAL_JGIT_REPO = ""
-
-def jgit_repos():
- if LOCAL_JGIT_REPO:
- native.local_repository(
- name = "jgit",
- path = LOCAL_JGIT_REPO,
- )
- jgit_maven_repos_dev()
- else:
- jgit_maven_repos()
-
-def jgit_maven_repos_dev():
- # Transitive dependencies from JGit's WORKSPACE.
- maven_jar(
- name = "hamcrest-library",
- artifact = "org.hamcrest:hamcrest-library:1.3",
- sha1 = "4785a3c21320980282f9f33d0d1264a69040538f",
- )
- maven_jar(
- name = "jzlib",
- artifact = "com.jcraft:jzlib:1.1.1",
- sha1 = "a1551373315ffc2f96130a0e5704f74e151777ba",
- )
-
-def jgit_maven_repos():
- maven_jar(
- name = "jgit-lib",
- artifact = "org.eclipse.jgit:org.eclipse.jgit:" + _JGIT_VERS,
- repository = _JGIT_REPO,
- sha1 = "b1714d4917750d6fad0d19d3b0e258b373db819a",
- )
- maven_jar(
- name = "jgit-servlet",
- artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + _JGIT_VERS,
- repository = _JGIT_REPO,
- sha1 = "cf61e6e00a758a6f33995e53883aede76d3b2400",
- )
- maven_jar(
- name = "jgit-archive",
- artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + _JGIT_VERS,
- repository = _JGIT_REPO,
- sha1 = "3c0b259040d3bc3a9e884a301055cf4f2e1bb1e2",
- )
- maven_jar(
- name = "jgit-junit",
- artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + _JGIT_VERS,
- repository = _JGIT_REPO,
- sha1 = "f78409fb808c5a108c629ec3cba74cc6c14ebff2",
- )
-
-def jgit_dep(name):
- mapping = {
- "@jgit-archive//jar": "@jgit//org.eclipse.jgit.archive:jgit-archive",
- "@jgit-junit//jar": "@jgit//org.eclipse.jgit.junit:junit",
- "@jgit-lib//jar": "@jgit//org.eclipse.jgit:jgit",
- "@jgit-servlet//jar": "@jgit//org.eclipse.jgit.http.server:jgit-servlet",
- }
-
- if LOCAL_JGIT_REPO:
- return mapping[name]
- else:
- return name
diff --git a/lib/jgit/org.eclipse.jgit.archive/BUILD b/lib/jgit/org.eclipse.jgit.archive/BUILD
deleted file mode 100644
index 151cd71b6f..0000000000
--- a/lib/jgit/org.eclipse.jgit.archive/BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-load("//lib/jgit:jgit.bzl", "jgit_dep")
-
-java_library(
- name = "jgit-archive",
- data = ["//lib:LICENSE-jgit"],
- visibility = ["//visibility:public"],
- exports = [jgit_dep("@jgit-archive//jar")],
- runtime_deps = ["//lib/jgit/org.eclipse.jgit:jgit"],
-)
diff --git a/lib/jgit/org.eclipse.jgit.http.server/BUILD b/lib/jgit/org.eclipse.jgit.http.server/BUILD
deleted file mode 100644
index fd634a5cc9..0000000000
--- a/lib/jgit/org.eclipse.jgit.http.server/BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-load("//lib/jgit:jgit.bzl", "jgit_dep")
-
-java_library(
- name = "jgit-servlet",
- data = ["//lib:LICENSE-jgit"],
- visibility = ["//visibility:public"],
- exports = [jgit_dep("@jgit-servlet//jar")],
- runtime_deps = ["//lib/jgit/org.eclipse.jgit:jgit"],
-)
diff --git a/lib/jgit/org.eclipse.jgit.junit/BUILD b/lib/jgit/org.eclipse.jgit.junit/BUILD
deleted file mode 100644
index abc522b8a0..0000000000
--- a/lib/jgit/org.eclipse.jgit.junit/BUILD
+++ /dev/null
@@ -1,11 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-load("//lib/jgit:jgit.bzl", "jgit_dep")
-
-java_library(
- name = "junit",
- testonly = True,
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = [jgit_dep("@jgit-junit//jar")],
- runtime_deps = ["//lib/jgit/org.eclipse.jgit:jgit"],
-)
diff --git a/lib/jgit/org.eclipse.jgit/BUILD b/lib/jgit/org.eclipse.jgit/BUILD
deleted file mode 100644
index c1f260740f..0000000000
--- a/lib/jgit/org.eclipse.jgit/BUILD
+++ /dev/null
@@ -1,20 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-load("//lib/jgit:jgit.bzl", "jgit_dep")
-
-java_library(
- name = "jgit",
- data = ["//lib:LICENSE-jgit"],
- visibility = ["//visibility:public"],
- exports = [jgit_dep("@jgit-lib//jar")],
- runtime_deps = [
- ":javaewah",
- "//lib/log:api",
- ],
-)
-
-java_library(
- name = "javaewah",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@javaewah//jar"],
-)
diff --git a/lib/js/bower_archives.bzl b/lib/js/bower_archives.bzl
index 1597c02848..1cd8d52610 100644
--- a/lib/js/bower_archives.bzl
+++ b/lib/js/bower_archives.bzl
@@ -34,44 +34,44 @@ def load_bower_archives():
bower_archive(
name = "iron-a11y-announcer",
package = "PolymerElements/iron-a11y-announcer",
- version = "1.0.6",
- sha1 = "14aed1e1b300ea344e80362e875919ea3d104dcc",
+ version = "2.1.0",
+ sha1 = "bda12ed6fe7b98a64bf5f70f3e84384053763190",
)
bower_archive(
name = "iron-a11y-keys-behavior",
package = "PolymerElements/iron-a11y-keys-behavior",
- version = "1.1.9",
- sha1 = "f58358ee652c67e6e721364ba50fb77a2ece1465",
+ version = "2.1.1",
+ sha1 = "4c8f303479253301e81c63b8ba7bd4cfb62ddf55",
)
bower_archive(
name = "iron-behaviors",
package = "PolymerElements/iron-behaviors",
- version = "1.0.18",
- sha1 = "e231a1a02b090f5183db917639fdb96cdd0dca18",
+ version = "2.1.1",
+ sha1 = "d2418e886c3237dcbc8d74a956eec367a95cd068",
)
bower_archive(
name = "iron-checked-element-behavior",
package = "PolymerElements/iron-checked-element-behavior",
- version = "1.0.6",
- sha1 = "93ad3554cec119d8c5732d1c722ad113e1866370",
+ version = "2.1.1",
+ sha1 = "822b6c73e349cf5174e3a17aa9b3d2cb823c37ac",
)
bower_archive(
name = "iron-fit-behavior",
package = "PolymerElements/iron-fit-behavior",
- version = "1.2.7",
- sha1 = "01c485fbf898307029bbb72ac7e132db1570a842",
+ version = "2.2.1",
+ sha1 = "7b12bc96bf05f04bbb6ad78a16d6c39758263a14",
)
bower_archive(
name = "iron-flex-layout",
package = "PolymerElements/iron-flex-layout",
- version = "1.3.9",
- sha1 = "d987b924cf29fcfe4b393833e81fdc9f1e268796",
+ version = "2.0.3",
+ sha1 = "c88e9577cabb005ea6d33f35b97d9c39c68f3d9e",
)
bower_archive(
name = "iron-form-element-behavior",
package = "PolymerElements/iron-form-element-behavior",
- version = "1.0.7",
- sha1 = "7b5a79e02cc32f0918725dd26925d0df1e03ed12",
+ version = "2.1.3",
+ sha1 = "634f01cdedd7a616ae025fdcde85c6c5804f6377",
)
bower_archive(
name = "iron-menu-behavior",
@@ -82,20 +82,20 @@ def load_bower_archives():
bower_archive(
name = "iron-meta",
package = "PolymerElements/iron-meta",
- version = "1.1.3",
- sha1 = "f77eba3f6f6817f10bda33918bde8f963d450041",
+ version = "2.1.1",
+ sha1 = "7985a9f18b6c32d62f5d3870d58d73ef66613cb9",
)
bower_archive(
name = "iron-resizable-behavior",
- package = "polymerelements/iron-resizable-behavior",
- version = "1.0.6",
- sha1 = "719c2a8a1a784f8aefcdeef41fcc2e5a03518d9e",
+ package = "PolymerElements/iron-resizable-behavior",
+ version = "2.1.1",
+ sha1 = "31e32da6880a983da32da21ee3f483525b24e458",
)
bower_archive(
name = "iron-validatable-behavior",
package = "PolymerElements/iron-validatable-behavior",
- version = "1.1.2",
- sha1 = "7111f34ff32e1510131dfbdb1eaa51bfa291e8be",
+ version = "2.1.0",
+ sha1 = "b5dcf3bf4d95b074b74f8170d7122d34ab417daf",
)
bower_archive(
name = "lodash",
@@ -111,15 +111,15 @@ def load_bower_archives():
)
bower_archive(
name = "neon-animation",
- package = "polymerelements/neon-animation",
- version = "1.2.5",
- sha1 = "588d289f779d02b21ce5b676e257bbd6155649e8",
+ package = "PolymerElements/neon-animation",
+ version = "2.2.1",
+ sha1 = "865f4252c6306b91609769fefefb4f641361931f",
)
bower_archive(
name = "paper-behaviors",
package = "PolymerElements/paper-behaviors",
- version = "1.0.13",
- sha1 = "a81eab28a952e124c208430e17508d9a1aae4ee7",
+ version = "2.1.1",
+ sha1 = "af59936a9015cda4abcfb235f831090a41faa2c4",
)
bower_archive(
name = "paper-icon-button",
@@ -130,16 +130,22 @@ def load_bower_archives():
bower_archive(
name = "paper-ripple",
package = "PolymerElements/paper-ripple",
- version = "1.0.10",
- sha1 = "21199db50d02b842da54bd6f4f1d1b10b474e893",
+ version = "2.1.1",
+ sha1 = "d402c8165c6a09d17c12a2b421e69ea54e2fc8ef",
)
bower_archive(
name = "paper-styles",
package = "PolymerElements/paper-styles",
- # Basically 1.3.1 but with
- # https://github.com/PolymerElements/paper-styles/pull/164 applied
- version = "dd0b13e186b9690d5e74a93f6e51e0835ea60495",
- sha1 = "f859a8dee403fbb724e8d0cf009db79c6dd61b47",
+ # Basically 2.1.0 but with
+ # https://github.com/PolymerElements/paper-styles/pull/165 applied
+ version = "a6c207e6eee3402fd7a6550e6f9c387ca22ec4c4",
+ sha1 = "6bd17410578b5d4017ccef330393a4b41b1c716e",
+ )
+ bower_archive(
+ name = "shadycss",
+ package = "webcomponents/shadycss",
+ version = "1.9.1",
+ sha1 = "3ef3bd54280ea2d7ce90434620354a2022c8e13d",
)
bower_archive(
name = "sinon-chai",
@@ -160,14 +166,8 @@ def load_bower_archives():
sha1 = "d6c07a0112ab2e9677fe085933744466a89232fb",
)
bower_archive(
- name = "web-animations-js",
- package = "web-animations/web-animations-js",
- version = "2.3.1",
- sha1 = "2ba5548d36188fe54555eaad0a576de4b027661e",
- )
- bower_archive(
name = "webcomponentsjs",
package = "webcomponents/webcomponentsjs",
- version = "0.7.24",
- sha1 = "559227f8ee9db9bfbd81989f24510cc0c1bfc65c",
+ version = "1.3.3",
+ sha1 = "bbad90bd8301a2f2f5e014e750e0c86351579391",
)
diff --git a/lib/js/bower_components.bzl b/lib/js/bower_components.bzl
index 64ab611362..7fd61c7956 100644
--- a/lib/js/bower_components.bzl
+++ b/lib/js/bower_components.bzl
@@ -77,7 +77,6 @@ def define_bower_components():
deps = [
":iron-behaviors",
":iron-overlay-behavior",
- ":iron-resizable-behavior",
":neon-animation",
":polymer",
],
@@ -195,11 +194,9 @@ def define_bower_components():
name = "neon-animation",
license = "//lib:LICENSE-polymer",
deps = [
- ":iron-meta",
":iron-resizable-behavior",
":iron-selector",
":polymer",
- ":web-animations-js",
],
)
bower_component(
@@ -331,14 +328,15 @@ def define_bower_components():
bower_component(
name = "polymer",
license = "//lib:LICENSE-polymer",
- deps = [":webcomponentsjs"],
+ deps = [
+ ":shadycss",
+ ":webcomponentsjs",
+ ],
seed = True,
)
bower_component(
- name = "promise-polyfill",
- license = "//lib:LICENSE-promise-polyfill",
- deps = [":polymer"],
- seed = True,
+ name = "shadycss",
+ license = "//lib:LICENSE-shadycss",
)
bower_component(
name = "sinon-chai",
@@ -358,10 +356,6 @@ def define_bower_components():
seed = True,
)
bower_component(
- name = "web-animations-js",
- license = "//lib:LICENSE-Apache2.0",
- )
- bower_component(
name = "web-component-tester",
license = "//lib:LICENSE-DO_NOT_DISTRIBUTE",
deps = [
diff --git a/lib/js/npm.bzl b/lib/js/npm.bzl
index 8a9e1ee6e5..5a6a8c0fcf 100644
--- a/lib/js/npm.bzl
+++ b/lib/js/npm.bzl
@@ -1,11 +1,11 @@
NPM_VERSIONS = {
"bower": "1.8.8",
"crisper": "2.0.2",
- "polymer-bundler": "4.0.2",
+ "polymer-bundler": "4.0.9",
}
NPM_SHA1S = {
"bower": "82544be34a33aeae7efb8bdf9905247b2cffa985",
"crisper": "7183c58cea33632fb036c91cefd1b43e390d22a2",
- "polymer-bundler": "6b296b6099ab5a0e93ca914cbe93e753f2395910",
+ "polymer-bundler": "c80c9815690d76656d1fa6a231481850b4fa3874",
}
diff --git a/lib/log/BUILD b/lib/log/BUILD
index b119b9465b..fa5bc45beb 100644
--- a/lib/log/BUILD
+++ b/lib/log/BUILD
@@ -5,7 +5,7 @@ java_library(
data = ["//lib:LICENSE-slf4j"],
visibility = [
"//javatests/com/google/gerrit/elasticsearch:__pkg__",
- "//lib/jgit/org.eclipse.jgit:__pkg__",
+ "//lib:__pkg__",
"//plugins:__pkg__",
],
exports = ["@log-api//jar"],
diff --git a/lib/mina/BUILD b/lib/mina/BUILD
index 5ad47cd3f1..2f98ee3e39 100644
--- a/lib/mina/BUILD
+++ b/lib/mina/BUILD
@@ -6,6 +6,7 @@ java_library(
visibility = ["//visibility:public"],
exports = [
":eddsa",
+ "@sshd-common//jar",
"@sshd-mina//jar",
"@sshd//jar",
],
diff --git a/lib/mockito/BUILD b/lib/mockito/BUILD
index cff36cd019..2aaf56d08a 100644
--- a/lib/mockito/BUILD
+++ b/lib/mockito/BUILD
@@ -8,8 +8,7 @@ package(
java_library(
name = "mockito",
data = ["//lib:LICENSE-mockito"],
- # Only exposed for plugin tests; core tests should use Easymock
- visibility = ["//java/com/google/gerrit/acceptance:__pkg__"],
+ visibility = ["//visibility:public"],
exports = ["@mockito//jar"],
runtime_deps = [
":byte-buddy",
@@ -21,13 +20,13 @@ java_library(
java_library(
name = "byte-buddy",
data = ["//lib:LICENSE-Apache2.0"],
- exports = ["@byte-buddy//jar"],
+ exports = ["@bytebuddy//jar"],
)
java_library(
name = "byte-buddy-agent",
data = ["//lib:LICENSE-Apache2.0"],
- exports = ["@byte-buddy-agent//jar"],
+ exports = ["@bytebuddy-agent//jar"],
)
java_library(
diff --git a/lib/nongoogle_test.sh b/lib/nongoogle_test.sh
index b04a454154..80195c8515 100755
--- a/lib/nongoogle_test.sh
+++ b/lib/nongoogle_test.sh
@@ -23,20 +23,14 @@ httpasyncclient
httpcore-nio
j2objc
jackson-core
-javassist
jna
jruby
mina-core
nekohtml
objenesis
openid-consumer
-powermock-api-easymock
-powermock-api-support
-powermock-core
-powermock-module-junit4
-powermock-module-junit4-common
-powermock-reflect
sshd
+sshd-common
sshd-mina
testcontainers
testcontainers-elasticsearch
diff --git a/lib/powermock/BUILD b/lib/powermock/BUILD
deleted file mode 100644
index 39df164aa8..0000000000
--- a/lib/powermock/BUILD
+++ /dev/null
@@ -1,69 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-
-java_library(
- name = "powermock-module-junit4",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = [
- ":powermock-module-junit4-common",
- "//lib:junit",
- "@powermock-module-junit4//jar",
- ],
-)
-
-java_library(
- name = "powermock-module-junit4-common",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = [
- ":powermock-reflect",
- "//lib:junit",
- "@powermock-module-junit4-common//jar",
- ],
-)
-
-java_library(
- name = "powermock-reflect",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = [
- "//lib:junit",
- "//lib/easymock:objenesis",
- "@powermock-reflect//jar",
- ],
-)
-
-java_library(
- name = "powermock-api-easymock",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = [
- ":powermock-api-support",
- "//lib/easymock",
- "@powermock-api-easymock//jar",
- ],
-)
-
-java_library(
- name = "powermock-api-support",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = [
- ":powermock-core",
- ":powermock-reflect",
- "//lib:junit",
- "@powermock-api-support//jar",
- ],
-)
-
-java_library(
- name = "powermock-core",
- data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
- visibility = ["//visibility:public"],
- exports = [
- ":powermock-reflect",
- "//lib:javassist",
- "//lib:junit",
- "@powermock-core//jar",
- ],
-)
diff --git a/modules/jgit b/modules/jgit
new file mode 160000
+Subproject a79c5b1f1046f4b41928bc40ab433155168dacc
diff --git a/package.json b/package.json
index db09e9c4c2..56acf61fae 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gerrit",
- "version": "3.0.7-SNAPSHOT",
+ "version": "3.1.0-SNAPSHOT",
"description": "Gerrit Code Review",
"dependencies": {},
"devDependencies": {
diff --git a/plugins/BUILD b/plugins/BUILD
index b9776ead8e..5f9c1426e2 100644
--- a/plugins/BUILD
+++ b/plugins/BUILD
@@ -44,7 +44,7 @@ EXPORTS = [
"//java/com/google/gerrit/mail",
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/metrics/dropwizard",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server/api",
"//java/com/google/gerrit/server/audit",
"//java/com/google/gerrit/server/cache/mem",
@@ -69,8 +69,8 @@ EXPORTS = [
"//lib/httpcomponents:httpclient",
"//lib/httpcomponents:httpcore",
"//lib/jackson:jackson-core",
- "//lib/jgit/org.eclipse.jgit.http.server:jgit-servlet",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:jgit-servlet",
+ "//lib:jgit",
"//lib:jsr305",
"//lib/log:api",
"//lib/log:log4j",
@@ -94,7 +94,18 @@ EXPORTS = [
]
java_binary(
+ name = "bouncycastle-deploy-env",
+ main_class = "Dummy",
+ runtime_deps = [
+ "//lib/bouncycastle:bcpg",
+ "//lib/bouncycastle:bcpkix",
+ "//lib/bouncycastle:bcprov",
+ ],
+)
+
+java_binary(
name = "plugin-api",
+ deploy_env = ["bouncycastle-deploy-env"],
main_class = "Dummy",
visibility = ["//visibility:public"],
runtime_deps = [":plugin-lib"],
@@ -121,12 +132,12 @@ java_binary(
"//antlr3:libquery_parser-src.jar",
"//java/com/google/gerrit/common:libannotations-src.jar",
"//java/com/google/gerrit/common:libserver-src.jar",
+ "//java/com/google/gerrit/entities:libentities-src.jar",
"//java/com/google/gerrit/extensions:libapi-src.jar",
"//java/com/google/gerrit/httpd:libhttpd-src.jar",
"//java/com/google/gerrit/index:libindex-src.jar",
"//java/com/google/gerrit/index:libquery_exception-src.jar",
"//java/com/google/gerrit/pgm/init/api:libapi-src.jar",
- "//java/com/google/gerrit/reviewdb:libserver-src.jar",
"//java/com/google/gerrit/server:libserver-src.jar",
"//java/com/google/gerrit/server/restapi:librestapi-src.jar",
"//java/com/google/gerrit/sshd:libsshd-src.jar",
@@ -143,7 +154,7 @@ java_doc(
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/entities",
"//java/com/google/gerrit/util/http",
],
pkgs = ["com.google.gerrit"],
diff --git a/plugins/delete-project b/plugins/delete-project
-Subproject 8b22484caeaf118bce493a46130f6a23f779cac
+Subproject debaa2c6d66b5a3c1bc95bc09283344c88407d4
diff --git a/plugins/download-commands b/plugins/download-commands
-Subproject 41c61bf8c1869bff4e0b436f69478c2137d0ca0
+Subproject 1b98be8d371e68237182df0f04361017f2be2ea
diff --git a/plugins/gitiles b/plugins/gitiles
-Subproject 0f6ffbecc51a0571c8a77248b2655a834700d6a
+Subproject dcac54b1afe9275bdd0fc8f3afc2c019b6617ad
diff --git a/plugins/hooks b/plugins/hooks
-Subproject 089687bdcc64b003d09a77f00eaa77bb79b15b9
+Subproject 5678f93fa62df1ec2baedc3a407aff71ca96556
diff --git a/plugins/plugin-manager b/plugins/plugin-manager
-Subproject d3b2a6eabcb641e952f253e61b927cd1f7f6e30
+Subproject 20bec5084c7b90029b8860cbb2fb9a12928f697
diff --git a/plugins/replication b/plugins/replication
-Subproject 4604b01e43c3b4aee3d03cb84945b3238c150ed
+Subproject e09b5a08e183d4f92410bfa06289a1784cefb43
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
-Subproject be5037839d987a319dac2236e9c1221d4d31848
+Subproject b9e1e8d61a324ca0592bbb7c80c2802618ef5bc
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
-Subproject 8a3b6faeaebc0f9b7d1af19eb0022b32994e476
+Subproject d04c4c33ad36e2e11ccc8b798357dd1e4e979a1
diff --git a/plugins/webhooks b/plugins/webhooks
-Subproject 8f7a232ef94f60b20c059fff7066d0d1864bec8
+Subproject 11b6fb81b5e4fb0bf3909f862c120d20d4fa9aa
diff --git a/polygerrit-ui/BUILD b/polygerrit-ui/BUILD
index 0e9b4bb3f5..1c685ab6e1 100644
--- a/polygerrit-ui/BUILD
+++ b/polygerrit-ui/BUILD
@@ -31,7 +31,7 @@ bower_component_bundle(
"//lib/js:paper-toggle-button",
"//lib/js:polymer",
"//lib/js:polymer-resin",
- "//lib/js:promise-polyfill",
+ "//lib/js:shadycss",
],
)
diff --git a/polygerrit-ui/Polymer2.md b/polygerrit-ui/Polymer2.md
new file mode 100644
index 0000000000..96bf77994b
--- /dev/null
+++ b/polygerrit-ui/Polymer2.md
@@ -0,0 +1,15 @@
+## Polymer 2 upgrade
+
+Gerrit is updating to use polymer 2 from polymer 1 by following the [Polymer 2.0 upgrade guide](https://polymer-library.polymer-project.org/2.0/docs/upgrade).
+
+Polymer 2 contains several breaking changes that may affect some of the UI features and plugins. One of the biggest change is to have the shadow DOM enabled. This will affect how you query elements inside of your component, how css style works within and across components, and several other usages.
+
+If you are owner of any plugins, please start following the [Polymer 2.0 upgrade guide](https://polymer-library.polymer-project.org/2.0/docs/upgrade) to migrate your plugins to be polymer 2 ready.
+
+If you notice any issues or need help with anything, don't hesitate to report to us [here](https://bugs.chromium.org/p/gerrit/issues/list).
+
+
+### Related resources
+
+- [Polymer 2.0 upgrade guide](https://polymer-library.polymer-project.org/2.0/docs/upgrade)
+- [Polymer Shadow DOM](https://polymer-library.polymer-project.org/2.0/docs/devguide/shadow-dom)
diff --git a/polygerrit-ui/README.md b/polygerrit-ui/README.md
index 4dbe14678d..1fbf581b07 100644
--- a/polygerrit-ui/README.md
+++ b/polygerrit-ui/README.md
@@ -1,4 +1,8 @@
-# PolyGerrit
+# Gerrit Polymer Frontend
+
+Follow the
+[setup instructions for Gerrit backend developers](https://gerrit-review.googlesource.com/Documentation/dev-readme.html)
+where applicable.
## Installing [Bazel](https://bazel.build/)
@@ -20,8 +24,8 @@ brew install node
brew install npm
```
-All other platforms: [download from
-nodejs.org](https://nodejs.org/en/download/).
+All other platforms:
+[download from nodejs.org](https://nodejs.org/en/download/).
Various steps below require installing additional npm packages. The full list of
dependencies can be installed with:
@@ -33,17 +37,12 @@ npm install
It may complain about a missing `typescript@2.3.4` peer dependency, which is
harmless.
-If you're interested in the details, keep reading.
-
-## Local UI, Production Data
+## Running locally against production data
-This is a quick and easy way to test your local changes against real data.
-Unfortunately, you can't sign in, so testing certain features will require
-you to use the "test data" technique described below.
+#### Go server
-### Running the server
-
-To test the local UI against gerrit-review.googlesource.com:
+To test the local Polymer frontend against gerrit-review.googlesource.com
+simply execute:
```sh
./polygerrit-ui/run-server.sh
@@ -51,37 +50,49 @@ To test the local UI against gerrit-review.googlesource.com:
Then visit http://localhost:8081
-## Local UI, Test Data
-
-1. [Build Gerrit](https://gerrit-review.googlesource.com/Documentation/dev-bazel.html#_gerrit_development_war_file)
-2. Set up a local test site. Docs
- [here](https://gerrit-review.googlesource.com/Documentation/linux-quickstart.html) and
- [here](https://gerrit-review.googlesource.com/Documentation/dev-readme.html#init).
-
-When your project is set up and works using the classic UI, run a test server
-that serves PolyGerrit:
+This method is based on a
+[simple hand-written Go webserver](https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/server.go).
+Mostly it just switches between serving files locally and proxying the real
+server based on the file name. It also does some basic response rewriting, e.g.
+it patches the `config/server/info` response with plugin information provided on
+the command line:
```sh
-bazel build gerrit &&
- $(bazel info output_base)/external/local_jdk/bin/java -DsourceRoot=/path/to/my/checkout \
- -jar bazel-bin/gerrit.war daemon --polygerrit-dev \
- -d ../gerrit_testsite --console-log --show-stack-trace
+./polygerrit-ui/run-server.sh --plugins=plugins/my_plugin/static/my_plugin.js,plugins/my_plugin/static/my_plugin.html
```
-Serving plugins
+The biggest draw back of this method is that you cannot log in, so cannot test
+scenarios that require it.
-> Local dev plugins must be put inside of gerrit/plugins
+#### MITM Proxy
-Loading a single plugin file:
+[MITM Proxy](https://mitmproxy.org/) is an open source product for proxying
+https servers. The
+[contrib/mitm-ui/](https://gerrit.googlesource.com/gerrit/+/master/contrib/mitm-ui/)
+directory contains scripts (and documentation) for using this technology
+(instead of the Go server). These scripts are somewhat experimental and
+unmaintained though.
-```sh
-./polygerrit-ui/run-server.sh --plugins=plugins/my_plugin/static/my_plugin.js
-```
+## Running locally against a Gerrit test site
+
+Set up a local test site once:
+
+1. [Build Gerrit](https://gerrit-review.googlesource.com/Documentation/dev-bazel.html#_gerrit_development_war_file)
+2. [Set up a local test site](https://gerrit-review.googlesource.com/Documentation/dev-readme.html#init).
+3. Optionally [populate](https://gerrit.googlesource.com/gerrit/+/master/contrib/populate-fixture-data.py) your test site with some test data.
-Loading multiple plugin files:
+For running a locally built Gerrit war against your test instance use
+[this command](https://gerrit-review.googlesource.com/Documentation/dev-readme.html#run_daemon),
+and add the `--polygerrit-dev` option, if you want to serve the Polymer frontend
+directly from the sources in `polygerrit_ui/app/` instead of from the war:
```sh
-./polygerrit-ui/run-server.sh --plugins=plugins/my_plugin/static/my_plugin.js,plugins/my_plugin/static/my_plugin.html
+$(bazel info output_base)/external/local_jdk/bin/java \
+ -DsourceRoot=$(bazel info workspace) \
+ -jar bazel-bin/gerrit.war daemon \
+ -d $GERRIT_SITE \
+ --console-log \
+ --polygerrit-dev
```
## Running Tests
@@ -93,7 +104,7 @@ to the `npm install` command to avoid file permission errors.
For daily development you typically only want to run and debug individual tests.
Run the local [Go proxy server](#go-server) and navigate for example to
-<http://localhost:8081/elements/change/gr-account-entry/gr-account-entry_test.html>.
+<http://localhost:8081/elements/shared/gr-account-entry/gr-account-entry_test.html>.
Check "Disable cache" in the "Network" tab of Chrome's dev tools, so code
changes are picked up on "reload".
@@ -115,11 +126,6 @@ To run Chrome tests in headless mode:
WCT_HEADLESS_MODE=1 WCT_ARGS='--verbose -l chrome' ./polygerrit-ui/app/run_test.sh
```
-Toolchain requirements for headless mode:
-
-* Chrome: 59+
-* web-component-tester: v6.5.0+
-
## Style guide
We follow the [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml)
@@ -176,11 +182,14 @@ npm run polylint
```
## Template Type Safety
-Polymer elements are not type checked against the element definition, making it trivial to break the display when refactoring or moving code. We now run additional tests to help ensure that template types are checked.
+Polymer elements are not type checked against the element definition, making it
+trivial to break the display when refactoring or moving code. We now run
+additional tests to help ensure that template types are checked.
A few notes to ensure that these tests pass
- Any functions with optional parameters will need closure annotations.
-- Any Polymer parameters that are nullable or can be multiple types (other than the one explicitly delared) will need type annotations.
+- Any Polymer parameters that are nullable or can be multiple types (other than
+ the one explicitly delared) will need type annotations.
These tests require the `typescript` and `fried-twinkie` npm packages.
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index 3a3c586970..5ec0274d31 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -181,7 +181,7 @@ sh_test(
"embed/test.html",
"test/common-test-setup.html",
":embed_test_files",
- ":polygerrit_embed_ui.zip",
+ ":pg_code.zip",
":test_components.zip",
],
# Should not run sandboxed.
diff --git a/polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html b/polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html
index fec459b33f..970bfc7f61 100644
--- a/polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/async-foreach-behavior/async-foreach-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>async-foreach-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="async-foreach-behavior.html">
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
index c21e96f357..b61b142825 100644
--- a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>base-url-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<script>
/** @type {string} */
@@ -52,7 +54,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [
Gerrit.BaseUrlBehavior,
],
diff --git a/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
index 96d4a08538..2c513f3ded 100644
--- a/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
@@ -15,10 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Polymer included for the html import polyfill. -->
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<title>docs-url-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
<link rel="import" href="docs-url-behavior.html">
@@ -38,7 +40,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'docs-url-behavior-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.DocsUrlBehavior],
});
});
diff --git a/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html b/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
index e445a7848a..8323ac6f3d 100644
--- a/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>dom-util-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="dom-util-behavior.html">
@@ -46,7 +48,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.DomUtilBehavior],
});
});
diff --git a/polygerrit-ui/app/behaviors/fire-behavior/fire-behavior.html b/polygerrit-ui/app/behaviors/fire-behavior/fire-behavior.html
new file mode 100644
index 0000000000..b5afab14c5
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/fire-behavior/fire-behavior.html
@@ -0,0 +1,55 @@
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<script>
+(function(window) {
+ 'use strict';
+
+ window.Gerrit = window.Gerrit || {};
+
+ /** @polymerBehavior Gerrit.FireBehavior */
+ Gerrit.FireBehavior = {
+ /**
+ * Dispatches a custom event with an optional detail value.
+ *
+ * @param {string} type Name of event type.
+ * @param {*=} detail Detail value containing event-specific
+ * payload.
+ * @param {{ bubbles: (boolean|undefined), cancelable: (boolean|undefined),
+ * composed: (boolean|undefined) }=}
+ * options Object specifying options. These may include:
+ * `bubbles` (boolean, defaults to `true`),
+ * `cancelable` (boolean, defaults to false), and
+ * `composed` (boolean, defaults to true).
+ * @return {!Event} The new event that was fired.
+ * @override
+ */
+ fire(type, detail, options) {
+ options = options || {};
+ detail = (detail === null || detail === undefined) ? {} : detail;
+ const event = new Event(type, {
+ bubbles: options.bubbles === undefined ? true : options.bubbles,
+ cancelable: Boolean(options.cancelable),
+ composed: options.composed === undefined ? true: options.composed,
+ });
+ event.detail = detail;
+ this.dispatchEvent(event);
+ return event;
+ },
+ };
+})(window);
+</script>
diff --git a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
index 530c76c120..0c75c44887 100644
--- a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
@@ -138,6 +138,7 @@ limitations under the License.
* object.
*/
toSortedArray(obj) {
+ if (!obj) { return []; }
return Object.keys(obj).map(key => {
return {
id: key,
diff --git a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
index 0b37a0d8f6..0d1ee575f1 100644
--- a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>keyboard-shortcut-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="gr-access-behavior.html">
@@ -38,7 +40,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.AccessBehavior],
});
});
diff --git a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
index d2175779e1..0285e35fb5 100644
--- a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>keyboard-shortcut-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="gr-admin-nav-behavior.html">
@@ -41,7 +43,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [
Gerrit.AdminNavBehavior,
],
diff --git a/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html b/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
index b052d06737..791e2afaa2 100644
--- a/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>keyboard-shortcut-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="gr-change-table-behavior.html">
@@ -48,7 +50,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.ChangeTableBehavior],
});
});
diff --git a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior.html
index 40379e48ea..3106fc8ba2 100644
--- a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior.html
@@ -15,33 +15,28 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
+<script src="../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
+
<script>
(function(window) {
'use strict';
- const ANONYMOUS_NAME = 'Anonymous';
-
window.Gerrit = window.Gerrit || {};
- /** @polymerBehavior Gerrit.AnonymousNameBehavior */
- Gerrit.AnonymousNameBehavior = {
+ /** @polymerBehavior Gerrit.DisplayNameBehavior */
+ Gerrit.DisplayNameBehavior = {
+ // TODO(dmfilippov) replace DisplayNameBehavior with GrDisplayNameUtils
+
/**
* enableEmail when true enables to fallback to using email if
* the account name is not avilable.
*/
getUserName(config, account, enableEmail) {
- if (account && account.name) {
- return account.name;
- } else if (account && account.username) {
- return account.username;
- } else if (enableEmail && account && account.email) {
- return account.email;
- } else if (config && config.user &&
- config.user.anonymous_coward_name !== 'Anonymous Coward') {
- return config.user.anonymous_coward_name;
- }
-
- return ANONYMOUS_NAME;
+ return GrDisplayNameUtils.getUserName(config, account, enableEmail);
+ },
+
+ getGroupDisplayName(group) {
+ return GrDisplayNameUtils.getGroupDisplayName(group);
},
};
})(window);
diff --git a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html
index 820d6bc602..3d4eca1b85 100644
--- a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-display-name-behavior/gr-display-name-behavior_test.html
@@ -17,12 +17,14 @@ limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-anonymous-name-behavior</title>
+<title>gr-display-name-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
-<link rel="import" href="gr-anonymous-name-behavior.html">
+<link rel="import" href="gr-display-name-behavior.html">
<test-fixture id="basic">
<template>
@@ -31,7 +33,7 @@ limitations under the License.
</test-fixture>
<script>
- suite('gr-anonymous-name-behavior tests', () => {
+ suite('gr-display-name-behavior tests', () => {
let element;
// eslint-disable-next-line no-unused-vars
const config = {
@@ -44,9 +46,8 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element-anon',
- _legacyUndefinedCheck: true,
behaviors: [
- Gerrit.AnonymousNameBehavior,
+ Gerrit.DisplayNameBehavior,
],
});
});
@@ -55,21 +56,21 @@ limitations under the License.
element = fixture('basic');
});
- test('test for it to return name', () => {
+ test('getUserName name only', () => {
const account = {
name: 'test-name',
};
assert.deepEqual(element.getUserName(config, account, true), 'test-name');
});
- test('test for it to return username', () => {
+ test('getUserName username only', () => {
const account = {
username: 'test-user',
};
assert.deepEqual(element.getUserName(config, account, true), 'test-user');
});
- test('test for it to return email', () => {
+ test('getUserName email only', () => {
const account = {
email: 'test-user@test-url.com',
};
@@ -77,11 +78,11 @@ limitations under the License.
'test-user@test-url.com');
});
- test('test for it not to Anonymous Coward as the anon name', () => {
+ test('getUserName returns not Anonymous Coward as the anon name', () => {
assert.deepEqual(element.getUserName(config, null, true), 'Anonymous');
});
- test('test for the config returning the anon name', () => {
+ test('getUserName for the config returning the anon name', () => {
const config = {
user: {
anonymous_coward_name: 'Test Anon',
@@ -89,5 +90,10 @@ limitations under the License.
};
assert.deepEqual(element.getUserName(config, null, true), 'Test Anon');
});
+
+ test('getGroupDisplayName', () => {
+ assert.equal(element.getGroupDisplayName({name: 'Some user name'}),
+ 'Some user name (group)');
+ });
});
</script>
diff --git a/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html b/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
index f6c765f8f7..535483d989 100644
--- a/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>keyboard-shortcut-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="gr-list-view-behavior.html">
@@ -40,7 +42,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.ListViewBehavior],
});
});
@@ -68,10 +69,10 @@ limitations under the License.
test('getFilterValue', () => {
let params;
- assert.equal(element.getFilterValue(params), null);
+ assert.equal(element.getFilterValue(params), '');
params = {filter: null};
- assert.equal(element.getFilterValue(params), null);
+ assert.equal(element.getFilterValue(params), '');
params = {filter: 'test'};
assert.equal(element.getFilterValue(params), 'test');
diff --git a/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html b/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html
index a4c1e86ae1..28d69900d0 100644
--- a/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html
@@ -31,7 +31,7 @@ limitations under the License.
window.Gerrit = window.Gerrit || {};
- /** @polymerBehavior this */
+ /** @polymerBehavior Gerrit.PatchSetBehavior*/
Gerrit.PatchSetBehavior = {
EDIT_NAME: 'edit',
PARENT_NAME: 'PARENT',
diff --git a/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html b/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html
index b858c51a8a..3db40847c8 100644
--- a/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html
@@ -15,10 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Polymer included for the html import polyfill. -->
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<title>gr-patch-set-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
<link rel="import" href="gr-patch-set-behavior.html">
diff --git a/polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html b/polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html
index 75c24330ba..0046290e69 100644
--- a/polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html
@@ -15,10 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Polymer included for the html import polyfill. -->
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<title>gr-path-list-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
<link rel="import" href="gr-path-list-behavior.html">
diff --git a/polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html b/polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html
index 2dc070d2b1..2fa9191f93 100644
--- a/polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html
@@ -20,7 +20,7 @@ limitations under the License.
window.Gerrit = window.Gerrit || {};
- /** @polymerBehavior this */
+ /** @polymerBehavior Gerrit.RepoPluginConfig*/
Gerrit.RepoPluginConfig = {
// Should be kept in sync with
// gerrit/java/com/google/gerrit/extensions/api/projects/ProjectConfigEntryType.java.
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html
index 07d3484e62..0e2e99fdca 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../elements/shared/gr-tooltip/gr-tooltip.html">
<script src="../../scripts/rootElement.js"></script>
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
index 04d8b6e62b..0bf620f030 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
@@ -89,7 +89,7 @@
this._tooltip = tooltip;
this.listen(window, 'scroll', '_handleWindowScroll');
this.listen(this, 'mouseleave', '_handleHideTooltip');
- this.listen(this, 'tap', '_handleHideTooltip');
+ this.listen(this, 'click', '_handleHideTooltip');
},
_handleHideTooltip(e) {
@@ -101,7 +101,7 @@
this.unlisten(window, 'scroll', '_handleWindowScroll');
this.unlisten(this, 'mouseleave', '_handleHideTooltip');
- this.unlisten(this, 'tap', '_handleHideTooltip');
+ this.unlisten(this, 'click', '_handleHideTooltip');
this.setAttribute('title', this._titleText);
if (this._tooltip && this._tooltip.parentNode) {
this._tooltip.parentNode.removeChild(this._tooltip);
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
index 943e0001d5..173c8d4ac5 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<title>tooltip-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="gr-tooltip-behavior.html">
@@ -51,7 +53,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'tooltip-behavior-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.TooltipBehavior],
});
});
diff --git a/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html b/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
index d909e86019..73e51d3c93 100644
--- a/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<title>gr-url-encoding-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="gr-url-encoding-behavior.html">
@@ -40,7 +42,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.URLEncodingBehavior],
});
});
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
index df7f3cff7d..3c5a733c17 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
@@ -96,8 +96,8 @@ by gr-app, and actually implemented by gr-comment-thread.
NOTE: doc-only shortcuts will not be customizable in the same way that other
shortcuts are.
-->
-<link rel="import" href="../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
<script>
(function(window) {
@@ -180,6 +180,7 @@ shortcuts are.
SEARCH: 'SEARCH',
SEND_REPLY: 'SEND_REPLY',
+ EMOJI_DROPDOWN: 'EMOJI_DROPDOWN',
};
const _help = new Map();
@@ -292,6 +293,8 @@ shortcuts are.
'Show/hide selected inline diff');
_describe(Shortcut.SEND_REPLY, ShortcutSection.REPLY_DIALOG, 'Send reply');
+ _describe(Shortcut.EMOJI_DROPDOWN, ShortcutSection.REPLY_DIALOG,
+ 'Emoji dropdown');
// Must be declared outside behavior implementation to be accessed inside
// behavior functions.
@@ -423,6 +426,9 @@ shortcuts are.
}
describeBinding(binding) {
+ if (binding.length === 1) {
+ return [binding];
+ }
return binding.split(':')[0].split('+').map(part => {
switch (part) {
case 'shift':
@@ -457,7 +463,7 @@ shortcuts are.
window.Gerrit = window.Gerrit || {};
- /** @polymerBehavior KeyboardShortcutBehavior */
+ /** @polymerBehavior Gerrit.KeyboardShortcutBehavior*/
Gerrit.KeyboardShortcutBehavior = [
Polymer.IronA11yKeysBehavior,
{
@@ -506,7 +512,7 @@ shortcuts are.
},
// Alias for getKeyboardEvent.
- /** @return {!(Event|PolymerDomApi|PolymerEventApi)} */
+ /** @return {!Event} */
getKeyboardEvent(e) {
return getKeyboardEvent(e);
},
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
index b4451fecd2..3183c7eff9 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>keyboard-shortcut-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="keyboard-shortcut-behavior.html">
@@ -50,7 +52,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.KeyboardShortcutBehavior],
keyBindings: {
k: '_handleKey',
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
index 354bedc2a7..85bc6a194d 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../base-url-behavior/base-url-behavior.html">
<script>
(function(window) {
@@ -97,6 +97,12 @@ limitations under the License.
// Skip mergeability data.
SKIP_MERGEABLE: 22,
+
+ /**
+ * Skip diffstat computation that compute the insertions field (number of lines inserted) and
+ * deletions field (number of lines deleted)
+ */
+ SKIP_DIFFSTAT: 23,
},
listChangesOptionsToHex(...args) {
@@ -123,8 +129,8 @@ limitations under the License.
return this.getBaseUrl() + '/c/' + changeNum;
},
- changeIsOpen(status) {
- return status === this.ChangeStatus.NEW;
+ changeIsOpen(change) {
+ return change && change.status === this.ChangeStatus.NEW;
},
/**
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
index 6af43dc449..a77a01f303 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>keyboard-shortcut-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<script>
/** @type {string} */
@@ -54,7 +56,6 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
- _legacyUndefinedCheck: true,
behaviors: [
Gerrit.BaseUrlBehavior,
Gerrit.RESTClientBehavior,
diff --git a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
index 6e040a3b05..ab446f1a77 100644
--- a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<title>safe-types-behavior</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../test/common-test-setup.html"/>
<link rel="import" href="safe-types-behavior.html">
@@ -39,7 +41,6 @@ limitations under the License.
suiteSetup(() => {
Polymer({
is: 'safe-types-element',
- _legacyUndefinedCheck: true,
behaviors: [Gerrit.SafeTypes],
});
});
@@ -119,4 +120,4 @@ limitations under the License.
});
});
});
-</script> \ No newline at end of file
+</script>
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html
index 45bc5f67d8..ac65360684 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.html
@@ -15,10 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -33,7 +34,7 @@ limitations under the License.
<style include="shared-styles">
:host {
display: block;
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
fieldset {
border: 1px solid var(--border-color);
@@ -50,13 +51,13 @@ limitations under the License.
display: flex;
justify-content: space-between;
min-height: 3em;
- padding: 0 .7em;
+ padding: 0 var(--spacing-m);
}
#deletedContainer {
border-bottom: 0;
}
.sectionContent {
- padding: .7em;
+ padding: var(--spacing-m);
}
#editBtn,
.editing #editBtn.global,
@@ -65,11 +66,11 @@ limitations under the License.
#addPermission,
#deleteBtn,
.editingRef .name,
- #editRefInput {
+ .editRefInput {
display: none;
}
.editing #editBtn,
- .editingRef #editRefInput {
+ .editingRef .editRefInput {
display: flex;
}
.deleted #deletedContainer {
@@ -82,7 +83,7 @@ limitations under the License.
}
.editing #deleteBtn,
#undoRemoveBtn {
- padding-right: .7em;
+ padding-right: var(--spacing-m);
}
</style>
<style include="gr-form-styles"></style>
@@ -96,20 +97,26 @@ limitations under the License.
id="editBtn"
link
class$="[[_computeEditBtnClass(section.id)]]"
- on-tap="editReference">
+ on-click="editReference">
<iron-icon id="icon" icon="gr-icons:create"></iron-icon>
</gr-button>
</div>
- <input
- id="editRefInput"
+ <iron-input
+ class="editRefInput"
bind-value="{{section.id}}"
- is="iron-input"
type="text"
on-input="_handleValueChange">
+ <input
+ class="editRefInput"
+ bind-value="{{section.id}}"
+ is="iron-input"
+ type="text"
+ on-input="_handleValueChange">
+ </iron-input>
<gr-button
link
id="deleteBtn"
- on-tap="_handleRemoveReference">Remove</gr-button>
+ on-click="_handleRemoveReference">Remove</gr-button>
</div><!-- end header -->
<div class="sectionContent">
<template
@@ -140,7 +147,7 @@ limitations under the License.
<gr-button
link
id="addBtn"
- on-tap="_handleAddPermission">Add</gr-button>
+ on-click="_handleAddPermission">Add</gr-button>
</div>
<!-- end addPermission -->
</div><!-- end sectionContent -->
@@ -150,7 +157,7 @@ limitations under the License.
<gr-button
link
id="undoRemoveBtn"
- on-tap="_handleUndoRemove">Undo</gr-button>
+ on-click="_handleUndoRemove">Undo</gr-button>
</div><!-- end deletedContainer -->
</fieldset>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
index 71f8d26ecf..81787090ae 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
@@ -39,7 +39,6 @@
Polymer({
is: 'gr-access-section',
- _legacyUndefinedCheck: true,
properties: {
capabilities: Object,
@@ -72,6 +71,11 @@
behaviors: [
Gerrit.AccessBehavior,
+ /**
+ * Unused in this element, but called by other elements in tests
+ * e.g gr-repo-access_test.
+ */
+ Gerrit.FireBehavior,
],
listeners: {
@@ -79,7 +83,15 @@
},
_updateSection(section) {
- this._permissions = this.toSortedArray(section.value.permissions);
+ let permissions = this.toSortedArray(section.value.permissions);
+ // We do not care about permissions for global capabilities that are not
+ // currently supported by the server (f.i. capabilities provided by
+ // plugins that are no longer installed).
+ if (section.id === GLOBAL_NAME) {
+ permissions = permissions.filter(
+ p => this.capabilities.hasOwnProperty(p.id));
+ }
+ this._permissions = permissions;
this._originalId = section.id;
},
@@ -96,7 +108,8 @@
// For a new section, this is not fired because new permissions and
// rules have to be added in order to save, modifying the ref is not
// enough.
- this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'access-modified', {bubbles: true, composed: true}));
}
this.section.value.updatedId = this.section.id;
},
@@ -123,6 +136,9 @@
_computePermissions(name, capabilities, labels) {
let allPermissions;
+ if (!this.section || !this.section.value) {
+ return [];
+ }
if (name === GLOBAL_NAME) {
allPermissions = this.toSortedArray(capabilities);
} else {
@@ -147,6 +163,7 @@
_computeLabelOptions(labels) {
const labelOptions = [];
+ if (!labels) { return []; }
for (const labelName of Object.keys(labels)) {
labelOptions.push({
id: 'label-' + labelName,
@@ -200,12 +217,13 @@
_handleRemoveReference() {
if (this.section.value.added) {
- this.dispatchEvent(new CustomEvent('added-section-removed',
- {bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'added-section-removed', {bubbles: true, composed: true}));
}
this._deleted = true;
this.section.value.deleted = true;
- this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('access-modified', {bubbles: true, composed: true}));
},
_handleUndoRemove() {
@@ -213,13 +231,19 @@
delete this.section.value.deleted;
},
+ editRefInput() {
+ return Polymer.dom(this.root).querySelector(Polymer.Element ?
+ 'iron-input.editRefInput' :
+ 'input[is=iron-input].editRefInput');
+ },
+
editReference() {
this._editingRef = true;
- this.$.editRefInput.focus();
+ this.editRefInput().focus();
},
_isEditEnabled(canUpload, ownerOf, sectionId) {
- return canUpload || ownerOf.indexOf(sectionId) >= 0;
+ return canUpload || (ownerOf && ownerOf.indexOf(sectionId) >= 0);
},
_computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted) {
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html
index 56f321b5c7..557501428f 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-access-section</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-access-section.html">
@@ -327,6 +329,24 @@ limitations under the License.
default_value: 0,
},
};
+ element.capabilities = {
+ accessDatabase: {
+ id: 'accessDatabase',
+ name: 'Access Database',
+ },
+ administrateServer: {
+ id: 'administrateServer',
+ name: 'Administrate Server',
+ },
+ batchChangesLimit: {
+ id: 'batchChangesLimit',
+ name: 'Batch Changes Limit',
+ },
+ createAccount: {
+ id: 'createAccount',
+ name: 'Create Account',
+ },
+ };
});
suite('Global section', () => {
setup(() => {
@@ -340,24 +360,6 @@ limitations under the License.
},
},
};
- element.capabilities = {
- accessDatabase: {
- id: 'accessDatabase',
- name: 'Access Database',
- },
- administrateServer: {
- id: 'administrateServer',
- name: 'Administrate Server',
- },
- batchChangesLimit: {
- id: 'batchChangesLimit',
- name: 'Batch Changes Limit',
- },
- createAccount: {
- id: 'createAccount',
- name: 'Create Account',
- },
- };
element._updateSection(element.section);
flushAsynchronousOperations();
});
@@ -385,7 +387,6 @@ limitations under the License.
},
},
};
- element.capabilities = {};
element._updateSection(element.section);
flushAsynchronousOperations();
});
@@ -455,7 +456,7 @@ limitations under the License.
1);
});
- test('edit section reference', () => {
+ test('edit section reference', done => {
element.canUpload = true;
element.ownerOf = [];
element.section = {id: 'refs/for/bar', value: {permissions: {}}};
@@ -464,14 +465,16 @@ limitations under the License.
assert.isTrue(element.$.section.classList.contains('editing'));
assert.isFalse(element._editingRef);
MockInteractions.tap(element.$.editBtn);
- element.$.editRefInput.bindValue='new/ref';
- flushAsynchronousOperations();
- assert.equal(element.section.id, 'new/ref');
- assert.isTrue(element._editingRef);
- assert.isTrue(element.$.section.classList.contains('editingRef'));
- element.editing = false;
- assert.isFalse(element._editingRef);
- assert.equal(element.section.id, 'refs/for/bar');
+ element.editRefInput().bindValue='new/ref';
+ setTimeout(() => {
+ assert.equal(element.section.id, 'new/ref');
+ assert.isTrue(element._editingRef);
+ assert.isTrue(element.$.section.classList.contains('editingRef'));
+ element.editing = false;
+ assert.isFalse(element._editingRef);
+ assert.equal(element.section.id, 'refs/for/bar');
+ done();
+ });
});
test('_handleValueChange', () => {
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html
index ea08d8943d..dd8758f332 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.html
@@ -15,10 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/gr-table-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
@@ -54,7 +54,7 @@ limitations under the License.
<template is="dom-repeat" items="[[_shownGroups]]">
<tr class="table">
<td class="name">
- <a href$="[[_computeGroupUrl(item.group_id)]]">[[item.name]]</a>
+ <a href$="[[_computeGroupUrl(item.id)]]">[[item.name]]</a>
</td>
<td class="description">[[item.description]]</td>
<td class="visibleToAll">[[_visibleToAll(item)]]</td>
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
index 9db2e34471..cf2100d81d 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-admin-group-list',
- _legacyUndefinedCheck: true,
properties: {
/**
@@ -68,6 +67,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.ListViewBehavior,
],
@@ -97,8 +97,13 @@
}
},
+ /**
+ * Generates groups link (/admin/groups/<uuid>)
+ *
+ * @param {string} id
+ */
_computeGroupUrl(id) {
- return Gerrit.Nav.getUrlForGroup(id);
+ return Gerrit.Nav.getUrlForGroup(decodeURIComponent(id));
},
_getCreateGroupCapability() {
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html
index 065a757dc1..bd9b30a9a5 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-admin-group-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
@@ -67,6 +69,30 @@ limitations under the License.
sandbox.restore();
});
+ test('_computeGroupUrl', () => {
+ let urlStub = sandbox.stub(Gerrit.Nav, 'getUrlForGroup',
+ () => '/admin/groups/e2cd66f88a2db4d391ac068a92d987effbe872f5');
+
+ let group = {
+ id: 'e2cd66f88a2db4d391ac068a92d987effbe872f5',
+ };
+ assert.equal(element._computeGroupUrl(group),
+ '/admin/groups/e2cd66f88a2db4d391ac068a92d987effbe872f5');
+
+ urlStub.restore();
+
+ urlStub = sandbox.stub(Gerrit.Nav, 'getUrlForGroup',
+ () => '/admin/groups/user/test');
+
+ group = {
+ id: 'user%2Ftest',
+ };
+ assert.equal(element._computeGroupUrl(group),
+ '/admin/groups/user/test');
+
+ urlStub.restore();
+ });
+
suite('list with groups', () => {
setup(done => {
groups = _.times(26, groupGenerator);
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
index b6a6d271a4..c7187a943d 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html">
@@ -56,7 +56,7 @@ limitations under the License.
padding: 5px 4px;
}
iron-icon {
- margin: 0 .2em;
+ margin: 0 var(--spacing-xs);
}
.breadcrumb {
align-items: center;
@@ -74,7 +74,7 @@ limitations under the License.
display: inline-block;
}
main.breadcrumbs:not(.table) {
- margin-top: 1em;
+ margin-top: var(--spacing-l);
}
</style>
<gr-page-nav class="navStyles">
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
index 4d964d7bb5..72cca9e510 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-admin-view',
- _legacyUndefinedCheck: true,
properties: {
/** @type {?} */
@@ -198,10 +197,6 @@
this.reload();
},
- _computeSelectedTitle(params) {
- return this.getSelectedTitle(params.view);
- },
-
// TODO (beckysiegel): Update these functions after router abstraction is
// updated. They are currently copied from gr-dropdown (and should be
// updated there as well once complete).
@@ -228,6 +223,7 @@
* @param {string=} opt_detailType
*/
_computeSelectedClass(itemView, params, opt_detailType) {
+ if (!params) return '';
// Group params are structured differently from admin params. Compute
// selected differently for groups.
// TODO(wyatta): Simplify this when all routes work like group params.
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
index ff25377277..984be198ef 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-admin-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-admin-view.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html
index 38730830cb..9d8ee18d8d 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../../styles/shared-styles.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
index 9c0e405292..acc76de6c9 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
@@ -25,7 +25,6 @@
Polymer({
is: 'gr-confirm-delete-item-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -44,6 +43,10 @@
itemType: String,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
_handleConfirmTap(e) {
e.preventDefault();
e.stopPropagation();
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html
index d735acbc12..3292cec8f0 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-confirm-delete-item-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-confirm-delete-item-dialog.html">
@@ -49,19 +51,23 @@ limitations under the License.
test('_handleConfirmTap', () => {
const confirmHandler = sandbox.stub();
element.addEventListener('confirm', confirmHandler);
- sandbox.stub(element, '_handleConfirmTap');
+ sandbox.spy(element, '_handleConfirmTap');
element.$$('gr-dialog').fire('confirm');
assert.isTrue(confirmHandler.called);
+ assert.isTrue(confirmHandler.calledOnce);
assert.isTrue(element._handleConfirmTap.called);
+ assert.isTrue(element._handleConfirmTap.calledOnce);
});
test('_handleCancelTap', () => {
const cancelHandler = sandbox.stub();
element.addEventListener('cancel', cancelHandler);
- sandbox.stub(element, '_handleCancelTap');
+ sandbox.spy(element, '_handleCancelTap');
element.$$('gr-dialog').fire('cancel');
assert.isTrue(cancelHandler.called);
+ assert.isTrue(cancelHandler.calledOnce);
assert.isTrue(element._handleCancelTap.called);
+ assert.isTrue(element._handleCancelTap.calledOnce);
});
test('_computeItemName function for branches', () => {
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
index da1c871a9f..2a95991c5a 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
@@ -15,10 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -42,7 +43,7 @@ limitations under the License.
}
gr-autocomplete {
--gr-autocomplete: {
- padding: 0 .15em;
+ padding: 0 var(--spacing-xs);
}
}
.hide {
@@ -69,23 +70,33 @@ limitations under the License.
<section class$="[[_computeBranchClass(baseChange)]]">
<span class="title">Provide base commit sha1 for change</span>
<span class="value">
- <input
- is="iron-input"
- id="baseCommitInput"
+ <iron-input
maxlength="40"
placeholder="(optional)"
bind-value="{{baseCommit}}">
+ <input
+ is="iron-input"
+ id="baseCommitInput"
+ maxlength="40"
+ placeholder="(optional)"
+ bind-value="{{baseCommit}}">
+ </iron-input>
</span>
</section>
<section>
<span class="title">Enter topic for new change</span>
<span class="value">
- <input
- is="iron-input"
- id="tagNameInput"
+ <iron-input
maxlength="1024"
placeholder="(optional)"
bind-value="{{topic}}">
+ <input
+ is="iron-input"
+ id="tagNameInput"
+ maxlength="1024"
+ placeholder="(optional)"
+ bind-value="{{topic}}">
+ </iron-input>
</span>
</section>
<section id="description">
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
index 5a4c0646ad..e29e5f847e 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-create-change-dialog',
- _legacyUndefinedCheck: true,
properties: {
repoName: String,
@@ -50,6 +49,11 @@
behaviors: [
Gerrit.BaseUrlBehavior,
+ /**
+ * Unused in this element, but called by other elements in tests
+ * e.g gr-repo-commands_test.
+ */
+ Gerrit.FireBehavior,
Gerrit.URLEncodingBehavior,
],
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
index aa4da68937..3a3683f5ce 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-create-change-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-create-change-dialog.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.html
index d96a935e35..8a4287b68b 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.html
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -39,10 +39,12 @@ limitations under the License.
<div id="form">
<section>
<span class="title">Group name</span>
- <input
- is="iron-input"
- id="groupNameInput"
+ <iron-input
bind-value="{{_name}}">
+ <input
+ is="iron-input"
+ bind-value="{{_name}}">
+ </iron-input>
</section>
</div>
</div>
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
index ec667ee8a9..01aeb4382f 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-create-group-dialog',
- _legacyUndefinedCheck: true,
properties: {
params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html
index 95ffdb19f1..ebca2893dd 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-create-group-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-create-group-dialog.html">
@@ -51,13 +53,16 @@ limitations under the License.
sandbox.restore();
});
- test('name is updated correctly', () => {
+ test('name is updated correctly', done => {
assert.isFalse(element.hasNewGroupName);
- element.$.groupNameInput.bindValue = GROUP_NAME;
+ ironInput(element.root).bindValue = GROUP_NAME;
- assert.isTrue(element.hasNewGroupName);
- assert.deepEqual(element._name, GROUP_NAME);
+ setTimeout(() => {
+ assert.isTrue(element.hasNewGroupName);
+ assert.deepEqual(element._name, GROUP_NAME);
+ done();
+ });
});
test('test for redirecting to group on successful creation', done => {
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.html
index aa9639b98d..33153eb4c0 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.html
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -45,29 +45,39 @@ limitations under the License.
</style>
<div class="gr-form-styles">
<div id="form">
- <section>
+ <section id="itemNameSection">
<span class="title">[[detailType]] name</span>
- <input
- is="iron-input"
- id="itemNameInput"
+ <iron-input
placeholder="[[detailType]] Name"
bind-value="{{_itemName}}">
+ <input
+ is="iron-input"
+ placeholder="[[detailType]] Name"
+ bind-value="{{_itemName}}">
+ </iron-input>
</section>
- <section>
+ <section id="itemRevisionSection">
<span class="title">Initial Revision</span>
- <input
- is="iron-input"
- id="itemRevisionInput"
+ <iron-input
placeholder="Revision (Branch or SHA-1)"
bind-value="{{_itemRevision}}">
+ <input
+ is="iron-input"
+ placeholder="Revision (Branch or SHA-1)"
+ bind-value="{{_itemRevision}}">
+ </iron-input>
</section>
- <section class$="[[_computeHideItemClass(itemDetail)]]">
+ <section id="itemAnnotationSection"
+ class$="[[_computeHideItemClass(itemDetail)]]">
<span class="title">Annotation</span>
- <input
- is="iron-input"
- id="itemAnnotationInput"
+ <iron-input
placeholder="Annotation (Optional)"
bind-value="{{_itemAnnotation}}">
+ <input
+ is="iron-input"
+ placeholder="Annotation (Optional)"
+ bind-value="{{_itemAnnotation}}">
+ </iron-input>
</section>
</div>
</div>
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
index 65bb46d7d7..4e9da900ba 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
@@ -24,7 +24,6 @@
Polymer({
is: 'gr-create-pointer-dialog',
- _legacyUndefinedCheck: true,
properties: {
detailType: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html
index 39e200a72c..08e82138b3 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-create-pointer-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-create-pointer-dialog.html">
@@ -49,7 +51,7 @@ limitations under the License.
sandbox.restore();
});
- test('branch created', () => {
+ test('branch created', done => {
sandbox.stub(element.$.restAPI, 'createRepoBranch', () => {
return Promise.resolve({});
});
@@ -59,17 +61,18 @@ limitations under the License.
element._itemName = 'test-branch';
element.itemDetail = 'branches';
- element.$.itemNameInput.bindValue = 'test-branch2';
- element.$.itemRevisionInput.bindValue = 'HEAD';
+ ironInput(element.$.itemNameSection).bindValue = 'test-branch2';
+ ironInput(element.$.itemRevisionSection).bindValue = 'HEAD';
- assert.isTrue(element.hasNewItemName);
-
- assert.equal(element._itemName, 'test-branch2');
-
- assert.equal(element._itemRevision, 'HEAD');
+ setTimeout(() => {
+ assert.isTrue(element.hasNewItemName);
+ assert.equal(element._itemName, 'test-branch2');
+ assert.equal(element._itemRevision, 'HEAD');
+ done();
+ });
});
- test('tag created', () => {
+ test('tag created', done => {
sandbox.stub(element.$.restAPI, 'createRepoTag', () => {
return Promise.resolve({});
});
@@ -79,17 +82,18 @@ limitations under the License.
element._itemName = 'test-tag';
element.itemDetail = 'tags';
- element.$.itemNameInput.bindValue = 'test-tag2';
- element.$.itemRevisionInput.bindValue = 'HEAD';
-
- assert.isTrue(element.hasNewItemName);
-
- assert.equal(element._itemName, 'test-tag2');
+ ironInput(element.$.itemNameSection).bindValue = 'test-tag2';
+ ironInput(element.$.itemRevisionSection).bindValue = 'HEAD';
- assert.equal(element._itemRevision, 'HEAD');
+ setTimeout(() => {
+ assert.isTrue(element.hasNewItemName);
+ assert.equal(element._itemName, 'test-tag2');
+ assert.equal(element._itemRevision, 'HEAD');
+ done();
+ });
});
- test('tag created with annotations', () => {
+ test('tag created with annotations', done => {
sandbox.stub(element.$.restAPI, 'createRepoTag', () => {
return Promise.resolve({});
});
@@ -100,17 +104,17 @@ limitations under the License.
element._itemAnnotation = 'test-message';
element.itemDetail = 'tags';
- element.$.itemNameInput.bindValue = 'test-tag2';
- element.$.itemAnnotationInput.bindValue = 'test-message2';
- element.$.itemRevisionInput.bindValue = 'HEAD';
-
- assert.isTrue(element.hasNewItemName);
-
- assert.equal(element._itemName, 'test-tag2');
+ ironInput(element.$.itemNameSection).bindValue = 'test-tag2';
+ ironInput(element.$.itemAnnotationSection).bindValue = 'test-message2';
+ ironInput(element.$.itemRevisionSection).bindValue = 'HEAD';
- assert.equal(element._itemAnnotation, 'test-message2');
-
- assert.equal(element._itemRevision, 'HEAD');
+ setTimeout(() => {
+ assert.isTrue(element.hasNewItemName);
+ assert.equal(element._itemName, 'test-tag2');
+ assert.equal(element._itemAnnotation, 'test-message2');
+ assert.equal(element._itemRevision, 'HEAD');
+ done();
+ });
});
test('_computeHideItemClass returns hideItem if type is branches', () => {
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.html
index b38fab59f4..d1a2471e4a 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.html
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
@@ -41,10 +41,9 @@ limitations under the License.
border: none;
--gr-autocomplete: {
border: 1px solid var(--border-color);
- border-radius: 2px;
- font-size: var(--font-size-normal);
+ border-radius: var(--border-radius);
height: 2em;
- padding: 0 .15em;
+ padding: 0 var(--spacing-xs);
width: 20em;
}
}
@@ -54,10 +53,13 @@ limitations under the License.
<div id="form">
<section>
<span class="title">Repository name</span>
- <input is="iron-input"
- id="repoNameInput"
- autocomplete="on"
- bind-value="{{_repoConfig.name}}">
+ <iron-input autocomplete="on"
+ bind-value="{{_repoConfig.name}}">
+ <input is="iron-input"
+ id="repoNameInput"
+ autocomplete="on"
+ bind-value="{{_repoConfig.name}}">
+ </iron-input>
</section>
<section>
<span class="title">Rights inherit from</span>
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
index ef7edd4d94..bb2b5f22a6 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-create-repo-dialog',
- _legacyUndefinedCheck: true,
properties: {
params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
index 79079f5c0b..7e32c5ce42 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-create-repo-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-create-repo-dialog.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
index b10c98a309..c15f0916a2 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
@@ -16,12 +16,14 @@ limitations under the License.
-->
<link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/gr-table-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../../shared/gr-account-link/gr-account-link.html">
<dom-module id="gr-group-audit-log">
<template>
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
index 966f3c9eca..8901d4ac36 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-group-audit-log',
- _legacyUndefinedCheck: true,
properties: {
groupId: String,
@@ -33,6 +32,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.ListViewBehavior,
],
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html
index 59a665b9b7..313d465a1a 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-group-audit-log</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-group-audit-log.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
index bcfc9e1649..86f66c43c6 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.html
@@ -16,10 +16,10 @@ limitations under the License.
-->
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/gr-subpage-styles.html">
<link rel="import" href="../../../styles/gr-table-styles.html">
@@ -43,7 +43,6 @@ limitations under the License.
gr-autocomplete {
width: 20em;
--gr-autocomplete: {
- font-size: var(--font-size-normal);
height: 2em;
width: 20em;
}
@@ -91,7 +90,7 @@ limitations under the License.
</span>
<gr-button
id="saveGroupMember"
- on-tap="_handleSavingGroupMember"
+ on-click="_handleSavingGroupMember"
disabled="[[!_groupMemberSearchId]]">
Add
</gr-button>
@@ -111,7 +110,7 @@ limitations under the License.
<td class="deleteColumn">
<gr-button
class="deleteMembersButton"
- on-tap="_handleDeleteMember">
+ on-click="_handleDeleteMember">
Delete
</gr-button>
</td>
@@ -133,7 +132,7 @@ limitations under the License.
</span>
<gr-button
id="saveIncludedGroups"
- on-tap="_handleSavingIncludedGroups"
+ on-click="_handleSavingIncludedGroups"
disabled="[[!_includedGroupSearchId]]">
Add
</gr-button>
@@ -163,7 +162,7 @@ limitations under the License.
<td class="deleteColumn">
<gr-button
class="deleteIncludedGroupButton"
- on-tap="_handleDeleteIncludedGroup">
+ on-click="_handleDeleteIncludedGroup">
Delete
</gr-button>
</td>
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
index b2784e5a5a..7f8e9ac68c 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
@@ -25,7 +25,6 @@
Polymer({
is: 'gr-group-members',
- _legacyUndefinedCheck: true,
properties: {
groupId: Number,
@@ -66,6 +65,7 @@
behaviors: [
Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -205,6 +205,7 @@
this.dispatchEvent(new CustomEvent('show-alert', {
detail: {message: SAVING_ERROR_TEXT},
bubbles: true,
+ composed: true,
}));
return err;
}
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
index 45d5497c3d..bf9113b1d9 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-group-members</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-group-members.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group.html b/polygerrit-ui/app/elements/admin/gr-group/gr-group.html
index b92dc4b46d..7617c19bbe 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.html
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.html
@@ -15,9 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/gr-subpage-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -35,7 +35,7 @@ limitations under the License.
content: ' *';
}
.inputUpdateBtn {
- margin-top: .3em;
+ margin-top: var(--spacing-s);
}
</style>
<style include="gr-form-styles"></style>
@@ -51,7 +51,8 @@ limitations under the License.
<h3 id="groupUUID">Group UUID</h3>
<fieldset>
<gr-copy-clipboard
- text="[[_groupConfig.id]]"></gr-copy-clipboard>
+ id="uuid"
+ text="[[_getGroupUUID(_groupConfig.id)]]"></gr-copy-clipboard>
</fieldset>
<h3 id="groupName" class$="[[_computeHeaderClass(_rename)]]">
Group Name
@@ -66,7 +67,7 @@ limitations under the License.
<span class="value" disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
<gr-button
id="inputUpdateNameBtn"
- on-tap="_handleSaveName"
+ on-click="_handleSaveName"
disabled="[[!_rename]]">
Rename Group</gr-button>
</span>
@@ -86,7 +87,7 @@ limitations under the License.
</span>
<span class="value" disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
<gr-button
- on-tap="_handleSaveOwner"
+ on-click="_handleSaveOwner"
disabled="[[!_owner]]">
Change Owners</gr-button>
</span>
@@ -104,7 +105,7 @@ limitations under the License.
</div>
<span class="value" disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
<gr-button
- on-tap="_handleSaveDescription"
+ on-click="_handleSaveDescription"
disabled="[[!_description]]">
Save Description
</gr-button>
@@ -132,7 +133,7 @@ limitations under the License.
</section>
<span class="value" disabled$="[[_computeGroupDisabled(_groupOwner, _isAdmin, _groupIsInternal)]]">
<gr-button
- on-tap="_handleSaveOptions"
+ on-click="_handleSaveOptions"
disabled="[[!_options]]">
Save Group Options
</gr-button>
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
index 2d7f9cbf18..09953fb74e 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
@@ -32,7 +32,6 @@
Polymer({
is: 'gr-group',
- _legacyUndefinedCheck: true,
/**
* Fired when the group name changes.
@@ -89,6 +88,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
observers: [
'_handleConfigName(_groupConfig.name)',
'_handleConfigOwner(_groupConfig.owner, _groupConfigOwner)',
@@ -233,5 +236,11 @@
_computeGroupDisabled(owner, admin, groupIsInternal) {
return groupIsInternal && (admin || owner) ? false : true;
},
+
+ _getGroupUUID(id) {
+ if (!id) return;
+
+ return id.match(INTERNAL_GROUP_REGEX) ? id : decodeURIComponent(id);
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html
index 226f3ab83b..fc04c02bb9 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-group</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-group.html">
@@ -240,5 +242,19 @@ limitations under the License.
element._loadGroup();
});
+
+ test('uuid', () => {
+ element._groupConfig = {
+ id: '6a1e70e1a88782771a91808c8af9bbb7a9871389',
+ };
+
+ assert.equal(element._groupConfig.id, element.$.uuid.text);
+
+ element._groupConfig = {
+ id: 'user%2Fgroup',
+ };
+
+ assert.equal('user/group', element.$.uuid.text);
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
index a5bb5fd8e8..931e2cda5b 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
@@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
-<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="/bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -31,13 +32,13 @@ limitations under the License.
<style include="shared-styles">
:host {
display: block;
- margin-bottom: .7em;
+ margin-bottom: var(--spacing-m);
}
.header {
align-items: baseline;
display: flex;
justify-content: space-between;
- margin: .3em .7em;
+ margin: var(--spacing-s) var(--spacing-m);
}
.rules {
background: var(--table-header-background-color);
@@ -48,7 +49,7 @@ limitations under the License.
border-bottom: 1px solid var(--border-color);
}
.title {
- margin-bottom: .3em;
+ margin-bottom: var(--spacing-s);
}
#addRule,
#removeBtn {
@@ -60,11 +61,11 @@ limitations under the License.
}
.editing #removeBtn {
display: block;
- margin-left: 1.5em;
+ margin-left: var(--spacing-xl);
}
.editing #addRule {
display: block;
- padding: .7em;
+ padding: var(--spacing-m);
}
#deletedContainer,
.deleted #mainContainer {
@@ -75,7 +76,7 @@ limitations under the License.
border: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
- padding: .7em;
+ padding: var(--spacing-m);
}
#mainContainer {
display: block;
@@ -100,7 +101,7 @@ limitations under the License.
<gr-button
link
id="removeBtn"
- on-tap="_handleRemovePermission">Remove</gr-button>
+ on-click="_handleRemovePermission">Remove</gr-button>
</div>
</div><!-- end header -->
<div class="rules">
@@ -136,7 +137,7 @@ limitations under the License.
<gr-button
link
id="undoRemoveBtn"
- on-tap="_handleUndoRemove">Undo</gr-button>
+ on-click="_handleUndoRemove">Undo</gr-button>
</div><!-- end deletedContainer -->
</section>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
index 58a006da20..75e715b985 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
@@ -38,7 +38,6 @@
Polymer({
is: 'gr-permission',
- _legacyUndefinedCheck: true,
properties: {
labels: Object,
@@ -78,6 +77,11 @@
behaviors: [
Gerrit.AccessBehavior,
+ /**
+ * Unused in this element, but called by other elements in tests
+ * e.g gr-access-section_test.
+ */
+ Gerrit.FireBehavior,
],
observers: [
@@ -138,17 +142,19 @@
_handleValueChange() {
this.permission.value.modified = true;
// Allows overall access page to know a change has been made.
- this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('access-modified', {bubbles: true, composed: true}));
},
_handleRemovePermission() {
if (this.permission.value.added) {
- this.dispatchEvent(new CustomEvent('added-permission-removed',
- {bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'added-permission-removed', {bubbles: true, composed: true}));
}
this._deleted = true;
this.permission.value.deleted = true;
- this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('access-modified', {bubbles: true, composed: true}));
},
_handleRulesChanged(changeRecord) {
@@ -177,7 +183,8 @@
},
_computeLabel(permission, labels) {
- if (!permission.value.label) { return; }
+ if (!labels || !permission ||
+ !permission.value || !permission.value.label) { return; }
const labelName = permission.value.label;
@@ -281,7 +288,8 @@
value.added = true;
// See comment above for why we cannot use "this.set(...)" here.
this.permission.value.rules[groupId] = value;
- this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('access-modified', {bubbles: true, composed: true}));
},
_computeHasRange(name) {
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
index a432ab0b20..0c70f9cfe2 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-permission</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-permission.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.html b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.html
index ca98c500de..276152688a 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.html
@@ -15,10 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -33,22 +33,22 @@ limitations under the License.
.existingItems {
background: var(--table-header-background-color);
border: 1px solid var(--border-color);
- border-radius: 2px;
+ border-radius: var(--border-radius);
}
gr-button {
float: right;
- margin-left: .5em;
+ margin-left: var(--spacing-m);
width: 4.5em;
}
.row {
align-items: center;
display: flex;
justify-content: space-between;
- padding: .5em 0;
+ padding: var(--spacing-m) 0;
width: 100%;
}
.existingItems .row {
- padding: .5em;
+ padding: var(--spacing-m);
}
.existingItems .row:not(:first-of-type) {
border-top: 1px solid var(--border-color);
@@ -61,7 +61,7 @@ limitations under the License.
}
.placeholder {
color: var(--deemphasized-text-color);
- padding-top: .75em;
+ padding-top: var(--spacing-m);
}
</style>
<div class="wrapper gr-form-styles">
@@ -73,8 +73,8 @@ limitations under the License.
<gr-button
link
disabled$="[[disabled]]"
- data-item="[[item]]"
- on-tap="_handleDelete">Delete</gr-button>
+ data-item$="[[item]]"
+ on-click="_handleDelete">Delete</gr-button>
</div>
</template>
</div>
@@ -83,16 +83,20 @@ limitations under the License.
<div class="row placeholder">None configured.</div>
</template>
<div class$="row [[_computeShowInputRow(disabled)]]">
- <input
- is="iron-input"
- id="input"
+ <iron-input
on-keydown="_handleInputKeydown"
- bind-value="{{_newValue}}"/>
+ bind-value="{{_newValue}}">
+ <input
+ is="iron-input"
+ id="input"
+ on-keydown="_handleInputKeydown"
+ bind-value="{{_newValue}}">
+ </iron-input>
<gr-button
id="addButton"
disabled$="[[!_newValue.length]]"
link
- on-tap="_handleAddTap">Add</gr-button>
+ on-click="_handleAddTap">Add</gr-button>
</div>
</div>
</template>
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js
index ab4d286cd7..bb0d501442 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js
@@ -67,7 +67,7 @@
},
_handleDelete(e) {
- const value = Polymer.dom(e).localTarget.dataItem;
+ const value = Polymer.dom(e).localTarget.dataset.item;
this._dispatchChanged(
this.pluginOption.info.values.filter(str => str !== value));
},
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html
index dc3f67ecbf..39e4ddcd39 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-plugin-config-array-editor</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-plugin-config-array-editor.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
index 9be526efb2..ee5dd835ba 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html
@@ -14,8 +14,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
<link rel="import" href="../../../styles/gr-table-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
index d6484d8d0f..1dbfdc807e 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-plugin-list',
- _legacyUndefinedCheck: true,
properties: {
/**
@@ -65,6 +64,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.ListViewBehavior,
],
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
index e42e37476b..24856873bc 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-plugin-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-plugin-list.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
index b6e56de393..ea12908cb2 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
@@ -15,10 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
-<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
+<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
@@ -48,20 +49,20 @@ limitations under the License.
align-items: center;
}
.weblink {
- margin-right: .2em;
+ margin-right: var(--spacing-xs);
}
.weblinks.show,
.referenceContainer {
display: block;
}
.rightsText {
- margin-right: .3rem;
+ margin-right: var(--spacing-s);
}
.editing gr-button,
.admin #editBtn {
display: inline-block;
- margin: 1em 0;
+ margin: var(--spacing-l) 0;
}
.editing #editInheritFromInput {
display: inline-block;
@@ -95,16 +96,16 @@ limitations under the License.
</template>
</div>
<gr-button id="editBtn"
- on-tap="_handleEdit">[[_editOrCancel(_editing)]]</gr-button>
+ on-click="_handleEdit">[[_editOrCancel(_editing)]]</gr-button>
<gr-button id="saveBtn"
primary
class$="[[_computeSaveBtnClass(_ownerOf)]]"
- on-tap="_handleSave"
+ on-click="_handleSave"
disabled$="[[!_modified]]">Save</gr-button>
<gr-button id="saveReviewBtn"
primary
class$="[[_computeSaveReviewBtnClass(_canUpload)]]"
- on-tap="_handleSaveForReview"
+ on-click="_handleSaveForReview"
disabled$="[[!_modified]]">Save for review</gr-button>
<template
is="dom-repeat"
@@ -124,7 +125,7 @@ limitations under the License.
</template>
<div class="referenceContainer">
<gr-button id="addReferenceBtn"
- on-tap="_handleCreateSection">Add Reference</gr-button>
+ on-click="_handleCreateSection">Add Reference</gr-button>
</div>
</div>
</main>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
index 1b8b322c14..1e6a43129b 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
@@ -70,7 +70,6 @@
Polymer({
is: 'gr-repo-access',
- _legacyUndefinedCheck: true,
properties: {
repo: {
@@ -118,6 +117,7 @@
behaviors: [
Gerrit.AccessBehavior,
Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -237,7 +237,7 @@
},
_computeWebLinkClass(weblinks) {
- return weblinks.length ? 'show' : '';
+ return weblinks && weblinks.length ? 'show' : '';
},
_computeShowInherit(inheritsFrom) {
@@ -413,8 +413,11 @@
if (!Object.keys(addRemoveObj.add).length &&
!Object.keys(addRemoveObj.remove).length &&
!addRemoveObj.parent) {
- this.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message: NOTHING_TO_SAVE}, bubbles: true}));
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {message: NOTHING_TO_SAVE},
+ bubbles: true,
+ composed: true,
+ }));
return;
}
const obj = {
@@ -450,12 +453,12 @@
},
_computeSaveBtnClass(ownerOf) {
- return ownerOf.length < 0 ? 'invisible' : '';
+ return ownerOf && ownerOf.length === 0 ? 'invisible' : '';
},
_computeMainClass(ownerOf, canUpload, editing) {
const classList = [];
- if (ownerOf.length > 0 || canUpload) {
+ if (ownerOf && ownerOf.length > 0 || canUpload) {
classList.push('admin');
}
if (editing) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
index a33b4bee2e..1660088447 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-access</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-access.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.html b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.html
index 7db4e4c16f..29bc02d00c 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -23,14 +23,15 @@ limitations under the License.
<style include="shared-styles">
:host {
display: block;
- margin-bottom: 2em;
+ margin-bottom: var(--spacing-xxl);
}
</style>
<h3>[[title]]</h3>
<gr-button
title$="[[tooltip]]"
disabled$="[[disabled]]"
- on-tap="_onCommandTap">
+ on-click
+ ="_onCommandTap">
[[title]]
</gr-button>
</template>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
index e0becaf68c..026c9908ef 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-repo-command',
- _legacyUndefinedCheck: true,
properties: {
title: String,
@@ -34,7 +33,8 @@
*/
_onCommandTap() {
- this.dispatchEvent(new CustomEvent('command-tap', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('command-tap', {bubbles: true, composed: true}));
},
});
})();
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html
index 9f9ac922ea..49d8765159 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-command</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-command.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html
index dba01aacfc..5089f346b3 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.html
@@ -15,9 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/gr-subpage-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
index 09c902621d..3b4811e0ee 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
@@ -28,7 +28,6 @@
Polymer({
is: 'gr-repo-commands',
- _legacyUndefinedCheck: true,
properties: {
params: Object,
@@ -42,6 +41,10 @@
_canCreate: Boolean,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
attached() {
this._loadRepo();
@@ -75,8 +78,9 @@
_handleRunningGC() {
return this.$.restAPI.runRepoGC(this.repo).then(response => {
if (response.status === 200) {
- this.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message: GC_MESSAGE}, bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'show-alert',
+ {detail: {message: GC_MESSAGE}, bubbles: true, composed: true}));
}
});
},
@@ -101,7 +105,7 @@
CREATE_CHANGE_SUCCEEDED_MESSAGE :
CREATE_CHANGE_FAILED_MESSAGE;
this.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message}, bubbles: true}));
+ {detail: {message}, bubbles: true, composed: true}));
if (!change) { return; }
Gerrit.Nav.navigateToRelativeUrl(Gerrit.Nav.getEditUrlForDiff(
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html
index 76c65e8b32..2976923805 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-commands</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-commands.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
index 1d49db9a86..8af3a92b40 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.html
@@ -14,7 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -24,7 +25,7 @@ limitations under the License.
<style include="shared-styles">
:host {
display: block;
- margin-bottom: 2em;
+ margin-bottom: var(--spacing-xxl);
}
.loading #dashboards,
#loadingContainer {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
index f72e986384..71cc5713fd 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-repo-dashboards',
- _legacyUndefinedCheck: true,
properties: {
repo: {
@@ -33,6 +32,10 @@
_dashboards: Array,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
_repoChanged(repo) {
this._loading = true;
if (!repo) { return Promise.resolve(); }
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
index 94bf5e09d5..4f76983480 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-dashboards</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-dashboards.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html
index fccfa6a0c8..2f244f83f4 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.html
@@ -17,8 +17,9 @@ limitations under the License.
<link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/gr-table-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -64,15 +65,14 @@ limitations under the License.
display: none;
}
.revisionEdit gr-button {
- margin-left: .6em;
+ margin-left: var(--spacing-m);
}
.editBtn {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
.canEdit .revisionEdit{
align-items: center;
display: flex;
- line-height: 1;
}
.deleteButton:not(.show) {
display: none;
@@ -120,23 +120,26 @@ limitations under the License.
</span>
<gr-button
link
- on-tap="_handleEditRevision"
+ on-click="_handleEditRevision"
class="editBtn">
edit
</gr-button>
- <input
- is=iron-input
+ <iron-input
bind-value="{{_revisedRef}}"
class="editItem">
+ <input
+ is="iron-input"
+ bind-value="{{_revisedRef}}">
+ </iron-input>
<gr-button
link
- on-tap="_handleCancelRevision"
+ on-click="_handleCancelRevision"
class="cancelBtn editItem">
Cancel
</gr-button>
<gr-button
link
- on-tap="_handleSaveRevision"
+ on-click="_handleSaveRevision"
class="saveBtn editItem"
disabled="[[!_revisedRef]]">
Save
@@ -172,7 +175,7 @@ limitations under the License.
<gr-button
link
class$="deleteButton [[_computeHideDeleteClass(_isOwner, item.can_delete)]]"
- on-tap="_handleDeleteItem">
+ on-click="_handleDeleteItem">
Delete
</gr-button>
</td>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
index e4000e064c..905232223e 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
@@ -26,7 +26,6 @@
Polymer({
is: 'gr-repo-detail-list',
- _legacyUndefinedCheck: true,
properties: {
/**
@@ -85,6 +84,7 @@
behaviors: [
Gerrit.ListViewBehavior,
+ Gerrit.FireBehavior,
Gerrit.URLEncodingBehavior,
],
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html
index 427a78ab5a..44d9b271f8 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-detail-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-detail-list.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.html b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.html
index 0490db2486..5e82c1e0cf 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.html
@@ -14,10 +14,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/gr-table-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
@@ -30,6 +29,20 @@ limitations under the License.
<template>
<style include="shared-styles"></style>
<style include="gr-table-styles"></style>
+ <style>
+ .genericList tr td:last-of-type {
+ text-align: left;
+ }
+ .genericList tr th:last-of-type {
+ text-align: left;
+ }
+ .readOnly {
+ text-align: center;
+ }
+ .changesLink, .name, .repositoryBrowser, .readOnly {
+ white-space:nowrap;
+ }
+ </style>
<gr-list-view
create-new=[[_createNewCapability]]
filter="[[_filter]]"
@@ -42,10 +55,10 @@ limitations under the License.
<table id="list" class="genericList">
<tr class="headerRow">
<th class="name topHeader">Repository Name</th>
- <th class="description topHeader">Repository Description</th>
- <th class="changesLink topHeader">Changes</th>
<th class="repositoryBrowser topHeader">Repository Browser</th>
- <th class="readOnly topHeader">Read only</th>
+ <th class="changesLink topHeader">Changes</th>
+ <th class="topHeader readOnly">Read only</th>
+ <th class="description topHeader">Repository Description</th>
</tr>
<tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
<td>Loading...</td>
@@ -56,8 +69,6 @@ limitations under the License.
<td class="name">
<a href$="[[_computeRepoUrl(item.name)]]">[[item.name]]</a>
</td>
- <td class="description">[[item.description]]</td>
- <td class="changesLink"><a href$="[[_computeChangesLink(item.name)]]">(view all)</a></td>
<td class="repositoryBrowser">
<template is="dom-repeat"
items="[[_computeWeblink(item)]]" as="link">
@@ -65,11 +76,13 @@ limitations under the License.
class="webLink"
rel="noopener"
target="_blank">
- ([[link.name]])
+ [[link.name]]
</a>
</template>
</td>
+ <td class="changesLink"><a href$="[[_computeChangesLink(item.name)]]">view all</a></td>
<td class="readOnly">[[_readOnly(item)]]</td>
+ <td class="description">[[item.description]]</td>
</tr>
</template>
</tbody>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
index d4eb29bf15..0eaa496c08 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-repo-list',
- _legacyUndefinedCheck: true,
properties: {
/**
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html
index 4bc023ffda..c77592cb78 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-list.html">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html
index 7f2cbe7a25..d2093e4378 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html
@@ -15,9 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../../../behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
@@ -35,14 +35,14 @@ limitations under the License.
<style include="gr-subpage-styles">
.inherited {
color: var(--deemphasized-text-color);
- margin-left: .5em;
+ margin-left: var(--spacing-m);
}
section.section:not(.ARRAY) .title {
align-items: center;
display: flex;
}
section.section.ARRAY .title {
- padding-top: .75em;
+ padding-top: var(--spacing-m);
}
</style>
<div class="gr-form-styles">
@@ -87,12 +87,18 @@ limitations under the License.
</gr-select>
</template>
<template is="dom-if" if="[[_isString(option.info.type)]]">
- <input
- is="iron-input"
- value="[[option.info.value]]"
+ <iron-input
+ bind-value="[[option.info.value]]"
on-input="_handleStringChange"
data-option-key$="[[option._key]]"
- disabled$="[[_computeDisabled(option.info.editable)]]"></input>
+ disabled$="[[_computeDisabled(option.info.editable)]]">
+ <input
+ is="iron-input"
+ value="[[option.info.value]]"
+ on-input="_handleStringChange"
+ data-option-key$="[[option._key]]"
+ disabled$="[[_computeDisabled(option.info.editable)]]">
+ </iron-input>
</template>
<template is="dom-if" if="[[option.info.inherited_value]]">
<span class="inherited">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
index 6d7677e4bf..bfa8832a99 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
@@ -71,7 +71,10 @@
return editable === 'false';
},
- _computeChecked(value) {
+ /**
+ * @param {string} value - fallback to 'false' if undefined
+ */
+ _computeChecked(value = 'false') {
return JSON.parse(value);
},
@@ -123,8 +126,8 @@
notifyPath: `${name}.${notifyPath}`,
};
- this.dispatchEvent(new CustomEvent(this.PLUGIN_CONFIG_CHANGED,
- {detail, bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ this.PLUGIN_CONFIG_CHANGED, {detail, bubbles: true, composed: true}));
},
});
})();
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
index ba0c8769ba..0a6846fc08 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-plugin-config</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-plugin-config.html">
@@ -151,7 +153,8 @@ limitations under the License.
const select = element.$$('select');
assert.ok(select);
select.value = 'newTest';
- select.dispatchEvent(new Event('change', {bubbles: true}));
+ select.dispatchEvent(new Event(
+ 'change', {bubbles: true, composed: true}));
flushAsynchronousOperations();
assert.isTrue(buildStub.called);
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html
index 8604ad8743..5de77b9dda 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html
@@ -15,11 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
+<link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
<link rel="import" href="../../shared/gr-download-commands/gr-download-commands.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-select/gr-select.html">
@@ -270,12 +272,18 @@ limitations under the License.
<section>
<span class="title">Maximum Git object size limit</span>
<span class="value">
- <input
- id="maxGitObjSizeInput"
+ <iron-input
+ id="maxGitObjSizeIronInput"
bind-value="{{_repoConfig.max_object_size_limit.configured_value}}"
- is="iron-input"
type="text"
disabled$="[[_readOnly]]">
+ <input
+ id="maxGitObjSizeInput"
+ bind-value="{{_repoConfig.max_object_size_limit.configured_value}}"
+ is="iron-input"
+ type="text"
+ disabled$="[[_readOnly]]">
+ </iron-input>
<template is="dom-if" if="[[_repoConfig.max_object_size_limit.value]]">
effective: [[_repoConfig.max_object_size_limit.value]] bytes
</template>
@@ -356,7 +364,7 @@ limitations under the License.
</template>
</div>
<gr-button
- on-tap="_handleSaveRepoConfig"
+ on-click="_handleSaveRepoConfig"
disabled$="[[_computeButtonDisabled(_readOnly, _configChanged)]]">Save changes</gr-button>
</fieldset>
<gr-endpoint-decorator name="repo-config">
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
index ff630c49eb..153a13799c 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
@@ -53,7 +53,6 @@
Polymer({
is: 'gr-repo',
- _legacyUndefinedCheck: true,
properties: {
params: Object,
@@ -109,6 +108,10 @@
_schemesObj: Object,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
observers: [
'_handleConfigChanged(_repoConfig.*)',
],
@@ -309,6 +312,9 @@
},
_computeCommands(repo, schemesObj, _selectedScheme) {
+ if (!schemesObj || !repo || !_selectedScheme) {
+ return [];
+ }
const commands = [];
let commandObj;
if (schemesObj.hasOwnProperty(_selectedScheme)) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
index 60645bb667..4e81565ec9 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo.html">
@@ -100,10 +102,12 @@ limitations under the License.
const SCHEMES = {http: {}, repo: {}, ssh: {}};
function getFormFields() {
- const selects = Polymer.dom(element.root).querySelectorAll('select');
- const textareas =
- Polymer.dom(element.root).querySelectorAll('iron-autogrow-textarea');
- const inputs = Polymer.dom(element.root).querySelectorAll('input');
+ const selects = Array.from(
+ Polymer.dom(element.root).querySelectorAll('select'));
+ const textareas = Array.from(
+ Polymer.dom(element.root).querySelectorAll('iron-autogrow-textarea'));
+ const inputs = Array.from(
+ Polymer.dom(element.root).querySelectorAll('input'));
return inputs.concat(textareas).concat(selects);
}
@@ -362,8 +366,9 @@ limitations under the License.
configInputObj.private_by_default;
element.$.matchAuthoredDateWithCommitterDateSelect.bindValue =
configInputObj.match_author_to_committer_date;
- element.$.maxGitObjSizeInput.bindValue =
- configInputObj.max_object_size_limit;
+ const inputElement = Polymer.Element ?
+ element.$.maxGitObjSizeIronInput : element.$.maxGitObjSizeInput;
+ inputElement.bindValue = configInputObj.max_object_size_limit;
element.$.contributorAgreementSelect.bindValue =
configInputObj.use_contributor_agreements;
element.$.useSignedOffBySelect.bindValue =
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html
index c8ae650fec..9820e31235 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.html
@@ -15,22 +15,25 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-select/gr-select.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-rule-editor">
<template>
<style include="shared-styles">
:host {
border-bottom: 1px solid var(--border-color);
- padding: .7em;
+ padding: var(--spacing-m);
display: block;
}
#removeBtn {
@@ -44,7 +47,7 @@ limitations under the License.
display: flex;
}
#options > * {
- margin-right: .5em;
+ margin-right: var(--spacing-m);
}
#mainContainer {
align-items: baseline;
@@ -146,14 +149,14 @@ limitations under the License.
<gr-button
link
id="removeBtn"
- on-tap="_handleRemoveRule">Remove</gr-button>
+ on-click="_handleRemoveRule">Remove</gr-button>
</div>
<div
id="deletedContainer"
class$="gr-form-styles [[_computeSectionClass(editing, _deleted)]]">
[[groupName]] was deleted
<gr-button link
- id="undoRemoveBtn" on-tap="_handleUndoRemove">Undo</gr-button>
+ id="undoRemoveBtn" on-click="_handleUndoRemove">Undo</gr-button>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
index 84c61016d6..8d94a90541 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
@@ -66,7 +66,6 @@
Polymer({
is: 'gr-rule-editor',
- _legacyUndefinedCheck: true,
properties: {
hasRange: Boolean,
@@ -97,6 +96,11 @@
behaviors: [
Gerrit.AccessBehavior,
Gerrit.BaseUrlBehavior,
+ /**
+ * Unused in this element, but called by other elements in tests
+ * e.g gr-permission_test.
+ */
+ Gerrit.FireBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -115,11 +119,20 @@
this._setupValues(this.rule);
},
+ attached() {
+ if (!this.rule) { return; } // Check needed for test purposes.
+ if (!this._originalRuleValues) {
+ // Observer _handleValueChange is called after the ready()
+ // method finishes. Original values must be set later to
+ // avoid set .modified flag to true
+ this._setOriginalRuleValues(this.rule.value);
+ }
+ },
+
_setupValues(rule) {
if (!rule.value) {
this._setDefaultRuleValues();
}
- this._setOriginalRuleValues(rule.value);
},
_computeForce(permission, action) {
@@ -211,12 +224,13 @@
_handleRemoveRule() {
if (this.rule.value.added) {
- this.dispatchEvent(new CustomEvent('added-rule-removed',
- {bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'added-rule-removed', {bubbles: true, composed: true}));
}
this._deleted = true;
this.rule.value.deleted = true;
- this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('access-modified', {bubbles: true, composed: true}));
},
_handleUndoRemove() {
@@ -238,7 +252,8 @@
if (!this._originalRuleValues) { return; }
this.rule.value.modified = true;
// Allows overall access page to know a change has been made.
- this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('access-modified', {bubbles: true, composed: true}));
},
_setOriginalRuleValues(value) {
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
index 7f5bf6abab..6d533afcdd 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-rule-editor</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-rule-editor.html">
@@ -200,7 +202,7 @@ limitations under the License.
});
suite('already existing generic rule', () => {
- setup(() => {
+ setup(done => {
element.group = 'Group Name';
element.permission = 'submit';
element.rule = {
@@ -216,6 +218,10 @@ limitations under the License.
// by the parent element.
element._setupValues(element.rule);
flushAsynchronousOperations();
+ flush(() => {
+ element.attached();
+ done();
+ });
});
test('_ruleValues and _originalRuleValues are set correctly', () => {
@@ -311,7 +317,7 @@ limitations under the License.
});
suite('new edit rule', () => {
- setup(() => {
+ setup(done => {
element.group = 'Group Name';
element.permission = 'editTopicName';
element.rule = {
@@ -321,6 +327,10 @@ limitations under the License.
element._setupValues(element.rule);
flushAsynchronousOperations();
element.rule.value.added = true;
+ flush(() => {
+ element.attached();
+ done();
+ });
});
test('_ruleValues and _originalRuleValues are set correctly', () => {
@@ -360,7 +370,7 @@ limitations under the License.
});
suite('already existing rule with labels', () => {
- setup(() => {
+ setup(done => {
element.label = {values: [
{value: -2, text: 'This shall not be merged'},
{value: -1, text: 'I would prefer this is not merged as is'},
@@ -382,6 +392,10 @@ limitations under the License.
element.section = 'refs/*';
element._setupValues(element.rule);
flushAsynchronousOperations();
+ flush(() => {
+ element.attached();
+ done();
+ });
});
test('_ruleValues and _originalRuleValues are set correctly', () => {
@@ -414,7 +428,7 @@ limitations under the License.
});
suite('new rule with labels', () => {
- setup(() => {
+ setup(done => {
sandbox.spy(element, '_setDefaultRuleValues');
element.label = {values: [
{value: -2, text: 'This shall not be merged'},
@@ -432,6 +446,10 @@ limitations under the License.
element._setupValues(element.rule);
flushAsynchronousOperations();
element.rule.value.added = true;
+ flush(() => {
+ element.attached();
+ done();
+ });
});
test('_ruleValues and _originalRuleValues are set correctly', () => {
@@ -472,7 +490,7 @@ limitations under the License.
});
suite('already existing push rule', () => {
- setup(() => {
+ setup(done => {
element.group = 'Group Name';
element.permission = 'push';
element.rule = {
@@ -485,6 +503,10 @@ limitations under the License.
element.section = 'refs/*';
element._setupValues(element.rule);
flushAsynchronousOperations();
+ flush(() => {
+ element.attached();
+ done();
+ });
});
test('_ruleValues and _originalRuleValues are set correctly', () => {
@@ -513,7 +535,7 @@ limitations under the License.
});
suite('new push rule', () => {
- setup(() => {
+ setup(done => {
element.group = 'Group Name';
element.permission = 'push';
element.rule = {
@@ -523,6 +545,10 @@ limitations under the License.
element._setupValues(element.rule);
flushAsynchronousOperations();
element.rule.value.added = true;
+ flush(() => {
+ element.attached();
+ done();
+ });
});
test('_ruleValues and _originalRuleValues are set correctly', () => {
@@ -553,7 +579,7 @@ limitations under the License.
});
suite('already existing edit rule', () => {
- setup(() => {
+ setup(done => {
element.group = 'Group Name';
element.permission = 'editTopicName';
element.rule = {
@@ -566,6 +592,10 @@ limitations under the License.
element.section = 'refs/*';
element._setupValues(element.rule);
flushAsynchronousOperations();
+ flush(() => {
+ element.attached();
+ done();
+ });
});
test('_ruleValues and _originalRuleValues are set correctly', () => {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
index a6dde7a3d4..a6c86bba27 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
@@ -19,7 +19,7 @@ limitations under the License.
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/gr-change-list-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-account-link/gr-account-link.html">
@@ -29,6 +29,8 @@ limitations under the License.
<link rel="import" href="../../shared/gr-limited-text/gr-limited-text.html">
<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
+<link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
<dom-module id="gr-change-list-item">
<template>
@@ -76,7 +78,7 @@ limitations under the License.
display: inline-flex;
}
.status .comma {
- padding-right: .2rem;
+ padding-right: var(--spacing-xs);
}
/* Used to hide the leading separator comma for statuses. */
.status .comma:first-of-type {
@@ -85,7 +87,7 @@ limitations under the License.
.size gr-tooltip-content {
margin: -.4rem -.6rem;
max-width: 2.5rem;
- padding: .4rem .6rem;
+ padding: var(--spacing-m) var(--spacing-l);
}
a {
color: inherit;
@@ -97,6 +99,8 @@ limitations under the License.
}
.u-monospace {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
}
.u-green {
color: var(--vote-text-color-recommended);
@@ -104,10 +108,6 @@ limitations under the License.
.u-red {
color: var(--vote-text-color-disliked);
}
- .label.u-green:not(.u-monospace),
- .label.u-red:not(.u-monospace) {
- font-size: 1.2rem;
- }
.u-gray-background {
background-color: var(--table-header-background-color);
}
@@ -159,16 +159,14 @@ limitations under the License.
<td class="cell owner"
hidden$="[[isColumnHidden('Owner', visibleChangeTableColumns)]]">
<gr-account-link
- account="[[change.owner]]"
- additional-text="[[_computeAccountStatusString(change.owner)]]"></gr-account-link>
+ account="[[change.owner]]"></gr-account-link>
</td>
<td class="cell assignee"
hidden$="[[isColumnHidden('Assignee', visibleChangeTableColumns)]]">
<template is="dom-if" if="[[change.assignee]]">
<gr-account-link
id="assigneeAccountLink"
- account="[[change.assignee]]"
- additional-text="[[_computeAccountStatusString(change.assignee)]]"></gr-account-link>
+ account="[[change.assignee]]"></gr-account-link>
</template>
<template is="dom-if" if="[[!change.assignee]]">
<span class="placeholder">--</span>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
index ecc7532a89..8eb3989114 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
@@ -26,7 +26,6 @@
Polymer({
is: 'gr-change-list-item',
- _legacyUndefinedCheck: true,
properties: {
visibleChangeTableColumns: Array,
@@ -40,11 +39,6 @@
type: String,
computed: '_computeChangeURL(change)',
},
- needsReview: {
- type: Boolean,
- reflectToAttribute: true,
- computed: '_computeItemNeedsReview(change.reviewed)',
- },
statuses: {
type: Array,
computed: 'changeStatuses(change)',
@@ -78,10 +72,6 @@
});
},
- _computeItemNeedsReview(reviewed) {
- return !reviewed;
- },
-
_computeChangeURL(change) {
return Gerrit.Nav.getUrlForChange(change);
},
@@ -174,10 +164,6 @@
return str;
},
- _computeAccountStatusString(account) {
- return account && account.status ? `(${account.status})` : '';
- },
-
_computeSizeTooltip(change) {
if (change.insertions + change.deletions === 0 ||
isNaN(change.insertions + change.deletions)) {
@@ -214,6 +200,7 @@
this.set('change.reviewed', newVal);
this.dispatchEvent(new CustomEvent('toggle-reviewed', {
bubbles: true,
+ composed: true,
detail: {change: this.change, reviewed: newVal},
}));
},
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
index 35f81bcd47..afae619e62 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-list-item</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
@@ -187,14 +189,6 @@ limitations under the License.
};
flushAsynchronousOperations();
assert.isOk(element.$$('.assignee gr-account-link'));
- assert.equal(Polymer.dom(element.root)
- .querySelector('#assigneeAccountLink').additionalText, '(test)');
- });
-
- test('_computeAccountStatusString', () => {
- assert.equal(element._computeAccountStatusString({}), '');
- assert.equal(element._computeAccountStatusString({status: 'Working'}),
- '(Working)');
});
test('TShirt sizing tooltip', () => {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
index 48d5075d2f..3ac1d5f92e 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
@@ -17,7 +17,8 @@ limitations under the License.
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-icons/gr-icons.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -35,7 +36,7 @@ limitations under the License.
}
.loading {
color: var(--deemphasized-text-color);
- padding: 1em var(--default-horizontal-margin);
+ padding: var(--spacing-l);
}
gr-change-list {
width: 100%;
@@ -67,7 +68,7 @@ limitations under the License.
@media only screen and (max-width: 50em) {
.loading,
.error {
- padding: 0 var(--default-horizontal-margin);
+ padding: 0 var(--spacing-l);
}
}
</style>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
index 6d051329db..cf6da73a07 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
@@ -18,7 +18,7 @@
'use strict';
const LookupQueryPatterns = {
- CHANGE_ID: /^\s*i?[0-9a-f]{8,40}\s*$/i,
+ CHANGE_ID: /^\s*i?[0-9a-f]{7,40}\s*$/i,
CHANGE_NUM: /^\s*[1-9][0-9]*\s*$/g,
};
@@ -31,7 +31,6 @@
Polymer({
is: 'gr-change-list-view',
- _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -41,6 +40,7 @@
behaviors: [
Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -162,8 +162,7 @@
for (const query in LookupQueryPatterns) {
if (LookupQueryPatterns.hasOwnProperty(query) &&
this._query.match(LookupQueryPatterns[query])) {
- this._replaceCurrentLocation(
- Gerrit.Nav.getUrlForChange(changes[0]));
+ Gerrit.Nav.navigateToChange(changes[0]);
return;
}
}
@@ -185,10 +184,6 @@
});
},
- _replaceCurrentLocation(url) {
- window.location.replace(url);
- },
-
_getChanges() {
return this.$.restAPI.getChanges(this._changesPerPage, this._query,
this._offset);
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
index 39113646cd..54885ccaf2 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-list-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-change-list-view.html">
@@ -194,7 +196,6 @@ limitations under the License.
suite('query based navigation', () => {
setup(() => {
- sandbox.stub(Gerrit.Nav, 'getUrlForChange', () => '/r/c/1');
});
teardown(done => {
@@ -205,10 +206,11 @@ limitations under the License.
});
test('Searching for a change ID redirects to change', done => {
+ const change = {_number: 1};
sandbox.stub(element, '_getChanges')
- .returns(Promise.resolve([{_number: 1}]));
- sandbox.stub(element, '_replaceCurrentLocation', url => {
- assert.equal(url, '/r/c/1');
+ .returns(Promise.resolve([change]));
+ sandbox.stub(Gerrit.Nav, 'navigateToChange', url => {
+ assert.equal(url, change);
done();
});
@@ -216,10 +218,11 @@ limitations under the License.
});
test('Searching for a change num redirects to change', done => {
+ const change = {_number: 1};
sandbox.stub(element, '_getChanges')
- .returns(Promise.resolve([{_number: 1}]));
- sandbox.stub(element, '_replaceCurrentLocation', url => {
- assert.equal(url, '/r/c/1');
+ .returns(Promise.resolve([change]));
+ sandbox.stub(Gerrit.Nav, 'navigateToChange', url => {
+ assert.equal(url, change);
done();
});
@@ -227,10 +230,11 @@ limitations under the License.
});
test('Commit hash redirects to change', done => {
+ const change = {_number: 1};
sandbox.stub(element, '_getChanges')
- .returns(Promise.resolve([{_number: 1}]));
- sandbox.stub(element, '_replaceCurrentLocation', url => {
- assert.equal(url, '/r/c/1');
+ .returns(Promise.resolve([change]));
+ sandbox.stub(Gerrit.Nav, 'navigateToChange', url => {
+ assert.equal(url, change);
done();
});
@@ -240,7 +244,7 @@ limitations under the License.
test('Searching for an invalid change ID searches', () => {
sandbox.stub(element, '_getChanges')
.returns(Promise.resolve([]));
- const stub = sandbox.stub(element, '_replaceCurrentLocation');
+ const stub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
element.params = {view: Gerrit.Nav.View.SEARCH, query: CHANGE_ID};
flushAsynchronousOperations();
@@ -251,7 +255,7 @@ limitations under the License.
test('Change ID with multiple search results searches', () => {
sandbox.stub(element, '_getChanges')
.returns(Promise.resolve([{}, {}]));
- const stub = sandbox.stub(element, '_replaceCurrentLocation');
+ const stub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
element.params = {view: Gerrit.Nav.View.SEARCH, query: CHANGE_ID};
flushAsynchronousOperations();
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
index ef17baad32..699f07a32d 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
@@ -20,12 +20,14 @@ limitations under the License.
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/gr-change-list-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
<link rel="import" href="../gr-change-list-item/gr-change-list-item.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
<dom-module id="gr-change-list">
<template>
@@ -35,6 +37,18 @@ limitations under the License.
border-collapse: collapse;
width: 100%;
}
+ .section-count-label {
+ color: var(--deemphasized-text-color);
+ }
+ a.section-title:hover {
+ text-decoration: none;
+ }
+ a.section-title:hover .section-count-label {
+ text-decoration: none;
+ }
+ a.section-title:hover .section-name {
+ text-decoration: underline;
+ }
</style>
<table id="changeList">
<tr class="topHeader">
@@ -68,8 +82,9 @@ limitations under the License.
<td class="star" hidden$="[[!showStar]]" hidden></td>
<td class="cell"
colspan$="[[_computeColspan(changeTableColumns, labelNames)]]">
- <a href$="[[_sectionHref(changeSection.query)]]">
- [[changeSection.name]]
+ <a href$="[[_sectionHref(changeSection.query)]]" class="section-title">
+ <span class="section-name">[[changeSection.name]]</span>
+ <span class="section-count-label">[[changeSection.countLabel]]</span>
</a>
</td>
</tr>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index 587719df55..5006f1e5e8 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -24,7 +24,6 @@
Polymer({
is: 'gr-change-list',
- _legacyUndefinedCheck: true,
/**
* Fired when next page key shortcut was pressed.
@@ -105,6 +104,7 @@
behaviors: [
Gerrit.BaseUrlBehavior,
Gerrit.ChangeTableBehavior,
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.RESTClientBehavior,
Gerrit.URLEncodingBehavior,
@@ -158,6 +158,11 @@
},
_computePreferences(account, preferences) {
+ // Polymer 2: check for undefined
+ if ([account, preferences].some(arg => arg === undefined)) {
+ return;
+ }
+
this.changeTableColumns = this.columnNames;
if (account) {
@@ -173,6 +178,7 @@
},
_computeColspan(changeTableColumns, labelNames) {
+ if (!changeTableColumns || !labelNames) return;
return changeTableColumns.length + labelNames.length +
NUMBER_FIXED_COLUMNS;
},
@@ -210,8 +216,19 @@
this.sections = changes ? [{results: changes}] : [];
},
+ _processQuery(query) {
+ let tokens = query.split(' ');
+ const invalidTokens = ['limit:', 'age:', '-age:'];
+ tokens = tokens.filter(token => {
+ return !invalidTokens.some(invalidToken => {
+ return token.startsWith(invalidToken);
+ });
+ });
+ return tokens.join(' ');
+ },
+
_sectionHref(query) {
- return Gerrit.Nav.getUrlForSearchQuery(query);
+ return Gerrit.Nav.getUrlForSearchQuery(this._processQuery(query));
},
/**
@@ -238,13 +255,13 @@
_computeItemNeedsReview(account, change, showReviewedState) {
return showReviewedState && !change.reviewed &&
!change.work_in_progress &&
- this.changeIsOpen(change.status) &&
+ this.changeIsOpen(change) &&
(!account || account._account_id != change.owner._account_id);
},
_computeItemHighlight(account, change) {
// Do not show the assignee highlight if the change is not open.
- if (!change.assignee ||
+ if (!change ||!change.assignee ||
!account ||
CLOSED_STATUS.indexOf(change.status) !== -1) {
return false;
@@ -352,16 +369,16 @@
},
_getListItems() {
- // Polymer2: querySelectorAll returns NodeList instead of Array.
return Array.from(
Polymer.dom(this.root).querySelectorAll('gr-change-list-item'));
},
_sectionsChanged() {
// Flush DOM operations so that the list item elements will be loaded.
- Polymer.dom.flush();
- this.$.cursor.stops = this._getListItems();
- this.$.cursor.moveToStart();
+ Polymer.RenderStatus.afterNextRender(this, () => {
+ this.$.cursor.stops = this._getListItems();
+ this.$.cursor.moveToStart();
+ });
},
_isOutgoing(section) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
index 99741a88db..817de6f128 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<link rel="import" href="gr-change-list.html">
@@ -170,11 +172,11 @@ limitations under the License.
{_number: 2},
];
flushAsynchronousOperations();
- const elementItems = Polymer.dom(element.root).querySelectorAll(
- 'gr-change-list-item');
- assert.equal(elementItems.length, 3);
+ Polymer.RenderStatus.afterNextRender(element, () => {
+ const elementItems = Polymer.dom(element.root).querySelectorAll(
+ 'gr-change-list-item');
+ assert.equal(elementItems.length, 3);
- flush(() => {
assert.isTrue(elementItems[0].hasAttribute('selected'));
MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
assert.equal(element.selectedIndex, 1);
@@ -431,6 +433,59 @@ limitations under the License.
});
});
+ suite('dashboard queries', () => {
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ });
+
+ teardown(() => { sandbox.restore(); });
+
+ test('query without age and limit unchanged', () => {
+ const query = 'status:closed owner:me';
+ assert.deepEqual(element._processQuery(query), query);
+ });
+
+ test('query with age and limit', () => {
+ const query = 'status:closed age:1week limit:10 owner:me';
+ const expectedQuery = 'status:closed owner:me';
+ assert.deepEqual(element._processQuery(query), expectedQuery);
+ });
+
+ test('query with age', () => {
+ const query = 'status:closed age:1week owner:me';
+ const expectedQuery = 'status:closed owner:me';
+ assert.deepEqual(element._processQuery(query), expectedQuery);
+ });
+
+ test('query with limit', () => {
+ const query = 'status:closed limit:10 owner:me';
+ const expectedQuery = 'status:closed owner:me';
+ assert.deepEqual(element._processQuery(query), expectedQuery);
+ });
+
+ test('query with age as value and not key', () => {
+ const query = 'status:closed random:age';
+ const expectedQuery = 'status:closed random:age';
+ assert.deepEqual(element._processQuery(query), expectedQuery);
+ });
+
+ test('query with limit as value and not key', () => {
+ const query = 'status:closed random:limit';
+ const expectedQuery = 'status:closed random:limit';
+ assert.deepEqual(element._processQuery(query), expectedQuery);
+ });
+
+ test('query with -age key', () => {
+ const query = 'status:closed -age:1week';
+ const expectedQuery = 'status:closed';
+ assert.deepEqual(element._processQuery(query), expectedQuery);
+ });
+ });
+
suite('gr-change-list sections', () => {
let element;
let sandbox;
@@ -442,7 +497,7 @@ limitations under the License.
teardown(() => { sandbox.restore(); });
- test('keyboard shortcuts', () => {
+ test('keyboard shortcuts', done => {
element.selectedIndex = 0;
element.sections = [
{
@@ -468,42 +523,45 @@ limitations under the License.
},
];
flushAsynchronousOperations();
- const elementItems = Polymer.dom(element.root).querySelectorAll(
- 'gr-change-list-item');
- assert.equal(elementItems.length, 9);
-
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- assert.equal(element.selectedIndex, 1);
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
-
- const navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
- assert.equal(element.selectedIndex, 2);
-
- MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
- assert.deepEqual(navStub.lastCall.args[0], {_number: 2},
- 'Should navigate to /c/2/');
-
- MockInteractions.pressAndReleaseKeyOn(element, 75); // 'k'
- assert.equal(element.selectedIndex, 1);
- MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
- assert.deepEqual(navStub.lastCall.args[0], {_number: 1},
- 'Should navigate to /c/1/');
-
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
- assert.equal(element.selectedIndex, 4);
- MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
- assert.deepEqual(navStub.lastCall.args[0], {_number: 4},
- 'Should navigate to /c/4/');
-
- MockInteractions.pressAndReleaseKeyOn(element, 82); // 'r'
- const change = element._changeForIndex(element.selectedIndex);
- assert.equal(change.reviewed, true,
- 'Should mark change as reviewed');
- MockInteractions.pressAndReleaseKeyOn(element, 82); // 'r'
- assert.equal(change.reviewed, false,
- 'Should mark change as unreviewed');
+ Polymer.RenderStatus.afterNextRender(element, () => {
+ const elementItems = Polymer.dom(element.root).querySelectorAll(
+ 'gr-change-list-item');
+ assert.equal(elementItems.length, 9);
+
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ assert.equal(element.selectedIndex, 1);
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+
+ const navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
+ assert.equal(element.selectedIndex, 2);
+
+ MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
+ assert.deepEqual(navStub.lastCall.args[0], {_number: 2},
+ 'Should navigate to /c/2/');
+
+ MockInteractions.pressAndReleaseKeyOn(element, 75); // 'k'
+ assert.equal(element.selectedIndex, 1);
+ MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
+ assert.deepEqual(navStub.lastCall.args[0], {_number: 1},
+ 'Should navigate to /c/1/');
+
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ MockInteractions.pressAndReleaseKeyOn(element, 74); // 'j'
+ assert.equal(element.selectedIndex, 4);
+ MockInteractions.pressAndReleaseKeyOn(element, 13); // 'enter'
+ assert.deepEqual(navStub.lastCall.args[0], {_number: 4},
+ 'Should navigate to /c/4/');
+
+ MockInteractions.pressAndReleaseKeyOn(element, 82); // 'r'
+ const change = element._changeForIndex(element.selectedIndex);
+ assert.equal(change.reviewed, true,
+ 'Should mark change as reviewed');
+ MockInteractions.pressAndReleaseKeyOn(element, 82); // 'r'
+ assert.equal(change.reviewed, false,
+ 'Should mark change as unreviewed');
+ done();
+ });
});
test('highlight attribute is updated correctly', () => {
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.html b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.html
index ecbd67e543..e88368dc58 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.html
+++ b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -30,7 +30,7 @@ limitations under the License.
#graphic,
#help {
display: inline-block;
- margin: .5em;
+ margin: var(--spacing-m);
}
#graphic #circle {
align-items: center;
@@ -51,14 +51,14 @@ limitations under the License.
text-align: center;
}
#help {
- padding-top: 1.35em;
+ padding-top: var(--spacing-xl);
vertical-align: top;
}
#help h1 {
- font-size: var(--font-size-large);
+ font-size: var(--font-size-h3);
}
#help p {
- margin-bottom: .6em;
+ margin-bottom: var(--spacing-m);
max-width: 35em;
}
@media only screen and (max-width: 50em) {
@@ -82,7 +82,7 @@ limitations under the License.
other git code review tools. Click on the `Create Change' button
and follow the step by step instructions.
</p>
- <gr-button on-tap="_handleCreateTap">Create Change</gr-button>
+ <gr-button on-click="_handleCreateTap">Create Change</gr-button>
</div>
</template>
<script src="gr-create-change-help.js"></script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
index 42d7bd768d..19e7a254a6 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-create-change-help',
- _legacyUndefinedCheck: true,
/**
* Fired when the "Create change" button is tapped.
@@ -29,7 +28,8 @@
_handleCreateTap(e) {
e.preventDefault();
- this.dispatchEvent(new CustomEvent('create-tap', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('create-tap', {bubbles: true, composed: true}));
},
});
})();
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html
index 09d95fd3e5..c43d62a188 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-create-change-help</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.html b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.html
index e6d123c7cd..9e86058e03 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.html
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<link rel="import" href="../../shared/gr-shell-command/gr-shell-command.html">
@@ -25,10 +25,10 @@ limitations under the License.
<style include="shared-styles">
ol {
list-style: decimal;
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
p {
- margin-bottom: .75em;
+ margin-bottom: var(--spacing-m);
}
#commandsDialog {
max-width: 40em;
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
index e4958c52c6..5abb257b09 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
@@ -25,7 +25,7 @@
Polymer({
is: 'gr-create-commands-dialog',
- _legacyUndefinedCheck: true,
+
properties: {
branch: String,
_createNewCommitCommand: {
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html
index e00037db35..89ad5732c7 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-create-commands-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-create-commands-dialog.html">
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.html b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.html
index d12d84b940..def5228743 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.html
+++ b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<link rel="import" href="../../shared/gr-repo-branch-picker/gr-repo-branch-picker.html">
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
index ed87e9ea7e..c2bfbf5e29 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
@@ -26,7 +26,7 @@
Polymer({
is: 'gr-create-destination-dialog',
- _legacyUndefinedCheck: true,
+
properties: {
_repo: String,
_branch: String,
@@ -46,9 +46,13 @@
this.$.createOverlay.close();
},
- _pickerConfirm() {
+ _pickerConfirm(e) {
this.$.createOverlay.close();
const detail = {repo: this._repo, branch: this._branch};
+ // e is a 'confirm' event from gr-dialog. We want to fire a more detailed
+ // 'confirm' event here, so let's stop propagation of the bare event.
+ e.preventDefault();
+ e.stopPropagation();
this.dispatchEvent(new CustomEvent('confirm', {detail, bubbles: false}));
},
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
index b0ba8a2b92..41475a040f 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../change-list/gr-change-list/gr-change-list.html">
@@ -38,7 +39,7 @@ limitations under the License.
}
.loading {
color: var(--deemphasized-text-color);
- padding: 1em var(--default-horizontal-margin);
+ padding: var(--spacing-l);
}
gr-change-list {
width: 100%;
@@ -52,7 +53,7 @@ limitations under the License.
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
- padding: .25em var(--default-horizontal-margin);
+ padding: var(--spacing-xs) var(--spacing-l);
}
.banner gr-button {
--gr-button: {
@@ -67,7 +68,7 @@ limitations under the License.
}
@media only screen and (max-width: 50em) {
.loading {
- padding: 0 var(--default-horizontal-margin);
+ padding: 0 var(--spacing-l);
}
}
</style>
@@ -80,7 +81,7 @@ limitations under the License.
<gr-button
class="delete"
link
- on-tap="_handleOpenDeleteDialog">Delete All</gr-button>
+ on-click="_handleOpenDeleteDialog">Delete All</gr-button>
</div>
</div>
<div class="loading" hidden$="[[!_loading]]">Loading...</div>
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index 5faef085d5..b762ab3f01 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-dashboard-view',
- _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -76,6 +75,7 @@
],
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.RESTClientBehavior,
],
@@ -144,12 +144,6 @@
return Promise.resolve();
}
- const user = params.user || 'self';
-
- // NOTE: This method may be called before attachment. Fire title-change
- // in an async so that attachment to the DOM can take place first.
- const title = params.title || this._computeTitle(user);
- this.async(() => this.fire('title-change', {title}));
return this._reload();
},
@@ -170,11 +164,19 @@
const checkForNewUser = !project && user === 'self';
return dashboardPromise
- .then(res => this._fetchDashboardChanges(res, checkForNewUser))
+ .then(res => {
+ if (res && res.title) {
+ this.fire('title-change', {title: res.title});
+ }
+ return this._fetchDashboardChanges(res, checkForNewUser);
+ })
.then(() => {
this._maybeShowDraftsBanner();
this.$.reporting.dashboardDisplayed();
}).catch(err => {
+ this.fire('title-change', {
+ title: title || this._computeTitle(user),
+ });
console.warn(err);
}).then(() => { this._loading = false; });
},
@@ -196,7 +198,7 @@
section.query);
if (checkForNewUser) {
- queries.push('owner:self');
+ queries.push('owner:self limit:1');
}
return this.$.restAPI.getChanges(null, queries, null, this.options)
@@ -208,6 +210,7 @@
}
this._results = changes.map((results, i) => ({
name: res.sections[i].name,
+ countLabel: this._computeSectionCountLabel(results),
query: res.sections[i].query,
results,
isOutgoing: res.sections[i].isOutgoing,
@@ -217,6 +220,16 @@
});
},
+ _computeSectionCountLabel(changes) {
+ if (!changes || !changes.length || changes.length == 0) {
+ return '';
+ }
+ const more = changes[changes.length - 1]._more_changes;
+ const numChanges = changes.length;
+ const andMore = more ? ' and more' : '';
+ return `(${numChanges}${andMore})`;
+ },
+
_computeUserHeaderClass(params) {
if (!params || !!params.project || !params.user
|| params.user === 'self') {
@@ -248,7 +261,7 @@
if (!draftSection || !draftSection.results.length) { return; }
const closedChanges = draftSection.results
- .filter(change => !this.changeIsOpen(change.status));
+ .filter(change => !this.changeIsOpen(change));
if (!closedChanges.length) { return; }
this._showDraftsBanner = true;
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
index 54c8ea988d..41d4192be9 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-dashboard-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-dashboard-view.html">
@@ -147,6 +149,27 @@ limitations under the License.
assert.equal(element._computeTitle('not self'), 'Dashboard for not self');
});
+ suite('_computeSectionCountLabel', () => {
+ test('empty changes dont count label', () => {
+ assert.equal('', element._computeSectionCountLabel([]));
+ });
+
+ test('1 change', () => {
+ assert.equal('(1)',
+ element._computeSectionCountLabel(['1']));
+ });
+
+ test('2 changes', () => {
+ assert.equal('(2)',
+ element._computeSectionCountLabel(['1', '2']));
+ });
+
+ test('1 change and more', () => {
+ assert.equal('(1 and more)',
+ element._computeSectionCountLabel([{_more_changes: true}]));
+ });
+ });
+
suite('_isViewActive', () => {
test('nothing happens when user param is falsy', () => {
element.params = {};
@@ -182,7 +205,7 @@ limitations under the License.
return paramsChangedPromise.then(() => {
assert.isTrue(
getChangesStub.calledWith(
- null, ['1', '2', 'owner:self'], null, element.options));
+ null, ['1', '2', 'owner:self limit:1'], null, element.options));
});
});
diff --git a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.html b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.html
index 2394e24b7b..d445185e95 100644
--- a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.html
+++ b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../change-list/gr-change-list/gr-change-list.html">
<link rel="import" href="../gr-create-change-help/gr-create-change-help.html">
diff --git a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
index 14d0cb01e9..acc4295bcd 100644
--- a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
+++ b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-embed-dashboard',
- _legacyUndefinedCheck: true,
+
properties: {
account: Object,
sections: Array,
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.html b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.html
index 23287254d1..0b4459cd45 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.html
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/dashboard-header-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
index 67fbd979d4..7ae4dab5ed 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-repo-header',
- _legacyUndefinedCheck: true,
+
properties: {
/** @type {?String} */
repo: {
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html
index a561e09c75..266818ed13 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-header</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-header.html">
@@ -44,30 +46,13 @@ limitations under the License.
teardown(() => { sandbox.restore(); });
- test('loads and clears account info', done => {
- sandbox.stub(element.$.restAPI, 'getAccountDetails')
- .returns(Promise.resolve({
- name: 'foo',
- email: 'bar',
- registered_on: '2015-03-12 18:32:08.000000000',
- }));
- sandbox.stub(element.$.restAPI, 'getAccountStatus')
- .returns(Promise.resolve('baz'));
-
- element.userId = 'foo.bar@baz';
- flush(() => {
- assert.isOk(element._accountDetails);
- assert.isOk(element._status);
-
- element.userId = null;
- flush(() => {
- flushAsynchronousOperations();
- assert.isNull(element._accountDetails);
- assert.isNull(element._status);
-
- done();
- });
- });
+ test('repoUrl reset once repo changed', () => {
+ sandbox.stub(Gerrit.Nav, 'getUrlForRepo',
+ repoName => `http://test.com/${repoName}`
+ );
+ assert.equal(element._repoUrl, undefined);
+ element.repo = 'test';
+ assert.equal(element._repoUrl, 'http://test.com/test');
});
});
</script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html
index 6accdfc8ad..fed1c121a3 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
index c7fda2ce62..6afc169000 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-user-header',
- _legacyUndefinedCheck: true,
+
properties: {
/** @type {?String} */
userId: {
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html
index c33be3b440..e837a5b13d 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-user-header</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-user-header.html">
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
deleted file mode 100644
index c53c111624..0000000000
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
+++ /dev/null
@@ -1,185 +0,0 @@
-/**
- * @license
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-(function() {
- 'use strict';
-
- Polymer({
- is: 'gr-account-entry',
- _legacyUndefinedCheck: true,
-
- /**
- * Fired when an account is entered.
- *
- * @event add
- */
-
- /**
- * When allowAnyInput is true, account-text-changed is fired when input text
- * changed. This is needed so that the reply dialog's save button can be
- * enabled for arbitrary cc's, which don't need a 'commit'.
- *
- * @event account-text-changed
- */
- properties: {
- allowAnyInput: Boolean,
- borderless: Boolean,
- change: Object,
- filter: Function,
- placeholder: String,
- /**
- * When true, account-entry uses the account suggest API endpoint, which
- * suggests any account in that Gerrit instance (and does not suggest
- * groups).
- *
- * When false/undefined, account-entry uses the suggest_reviewers API
- * endpoint, which suggests any account or group in that Gerrit instance
- * that is not already a reviewer (or is not CCed) on that change.
- */
- allowAnyUser: Boolean,
-
- // suggestFrom = 0 to enable default suggestions.
- suggestFrom: {
- type: Number,
- value: 0,
- },
-
- query: {
- type: Function,
- value() {
- return this._getReviewerSuggestions.bind(this);
- },
- },
-
- _config: Object,
- /** The value of the autocomplete entry. */
- _inputText: {
- type: String,
- observer: '_inputTextChanged',
- },
-
- _loggedIn: Boolean,
- },
-
- behaviors: [
- Gerrit.AnonymousNameBehavior,
- ],
-
- attached() {
- this.$.restAPI.getConfig().then(cfg => {
- this._config = cfg;
- });
- this.$.restAPI.getLoggedIn().then(loggedIn => {
- this._loggedIn = loggedIn;
- });
- },
-
- get focusStart() {
- return this.$.input.focusStart;
- },
-
- focus() {
- this.$.input.focus();
- },
-
- clear() {
- this.$.input.clear();
- },
-
- setText(text) {
- this.$.input.setText(text);
- },
-
- getText() {
- return this.$.input.text;
- },
-
- _handleInputCommit(e) {
- this.fire('add', {value: e.detail.value});
- this.$.input.focus();
- },
-
- _accountOrAnon(reviewer) {
- return this.getUserName(this._config, reviewer, false);
- },
-
- _inputTextChanged(text) {
- if (text.length && this.allowAnyInput) {
- this.dispatchEvent(new CustomEvent('account-text-changed',
- {bubbles: true}));
- }
- },
-
- _makeSuggestion(reviewer) {
- let name;
- let value;
- const generateStatusStr = function(account) {
- return account.status ? '(' + account.status + ')' : '';
- };
- if (reviewer.account) {
- // Reviewer is an account suggestion from getChangeSuggestedReviewers.
- const reviewerName = this._accountOrAnon(reviewer.account);
- const reviewerEmail = this._reviewerEmail(reviewer.account.email);
- const reviewerStatus = generateStatusStr(reviewer.account);
- name = [reviewerName, reviewerEmail, reviewerStatus]
- .filter(p => p.length > 0).join(' ');
- value = reviewer;
- } else if (reviewer.group) {
- // Reviewer is a group suggestion from getChangeSuggestedReviewers.
- name = reviewer.group.name + ' (group)';
- value = reviewer;
- } else if (reviewer._account_id) {
- // Reviewer is an account suggestion from getSuggestedAccounts.
- const reviewerName = this._accountOrAnon(reviewer);
- const reviewerEmail = this._reviewerEmail(reviewer.email);
- const reviewerStatus = generateStatusStr(reviewer);
- name = [reviewerName, reviewerEmail, reviewerStatus]
- .filter(p => p.length > 0).join(' ');
- value = {account: reviewer, count: 1};
- }
- return {name, value};
- },
-
- _getReviewerSuggestions(input) {
- if (!this.change || !this.change._number || !this._loggedIn) {
- return Promise.resolve([]);
- }
-
- const api = this.$.restAPI;
- const xhr = this.allowAnyUser ?
- api.getSuggestedAccounts(`cansee:${this.change._number} ${input}`) :
- api.getChangeSuggestedReviewers(this.change._number, input);
-
- return xhr.then(reviewers => {
- if (!reviewers) { return []; }
- if (!this.filter) {
- return reviewers.map(this._makeSuggestion.bind(this));
- }
- return reviewers
- .filter(this.filter)
- .map(this._makeSuggestion.bind(this));
- });
- },
-
- _reviewerEmail(email) {
- if (typeof email !== 'undefined') {
- return '<' + email + '>';
- }
-
- return '';
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html
deleted file mode 100644
index 724f7dac0b..0000000000
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html
+++ /dev/null
@@ -1,274 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2016 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-account-entry</title>
-
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
-<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../scripts/util.js"></script>
-
-<link rel="import" href="gr-account-entry.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
- <template>
- <gr-account-entry></gr-account-entry>
- </template>
-</test-fixture>
-
-<script>
- suite('gr-account-entry tests', () => {
- let sandbox;
- let _nextAccountId = 0;
- const makeAccount = function(opt_status) {
- const accountId = ++_nextAccountId;
- return {
- _account_id: accountId,
- name: 'name ' + accountId,
- email: 'email ' + accountId,
- status: opt_status,
- };
- };
- let _nextAccountId2 = 0;
- const makeAccount2 = function(opt_status) {
- const accountId2 = ++_nextAccountId2;
- return {
- _account_id: accountId2,
- email: 'email ' + accountId2,
- status: opt_status,
- };
- };
- let _nextAccountId3 = 0;
- const makeAccount3 = function(opt_status) {
- const accountId3 = ++_nextAccountId3;
- return {
- _account_id: accountId3,
- name: 'name ' + accountId3,
- status: opt_status,
- };
- };
-
- let owner;
- let existingReviewer1;
- let existingReviewer2;
- let suggestion1;
- let suggestion2;
- let suggestion3;
- let element;
-
- setup(done => {
- owner = makeAccount();
- existingReviewer1 = makeAccount();
- existingReviewer2 = makeAccount();
- suggestion1 = {account: makeAccount()};
- suggestion2 = {account: makeAccount()};
- suggestion3 = {
- group: {
- id: 'suggested group id',
- name: 'suggested group',
- },
- };
-
- stub('gr-rest-api-interface', {
- getLoggedIn() { return Promise.resolve(true); },
- });
-
- element = fixture('basic');
- element.change = {
- _number: 42,
- owner,
- reviewers: {
- CC: [existingReviewer1],
- REVIEWER: [existingReviewer2],
- },
- };
- sandbox = sinon.sandbox.create();
- return flush(done);
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- suite('stubbed values for _getReviewerSuggestions', () => {
- setup(() => {
- stub('gr-rest-api-interface', {
- getChangeSuggestedReviewers() {
- const redundantSuggestion1 = {account: existingReviewer1};
- const redundantSuggestion2 = {account: existingReviewer2};
- const redundantSuggestion3 = {account: owner};
- return Promise.resolve([redundantSuggestion1, redundantSuggestion2,
- redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
- },
- });
- });
-
- test('_makeSuggestion formats account or group accordingly', () => {
- let account = makeAccount();
- const account2 = makeAccount2();
- const account3 = makeAccount3();
- let suggestion = element._makeSuggestion({account});
- assert.deepEqual(suggestion, {
- name: account.name + ' <' + account.email + '>',
- value: {account},
- });
-
- const group = {name: 'test'};
- suggestion = element._makeSuggestion({group});
- assert.deepEqual(suggestion, {
- name: group.name + ' (group)',
- value: {group},
- });
-
- suggestion = element._makeSuggestion(account);
- assert.deepEqual(suggestion, {
- name: account.name + ' <' + account.email + '>',
- value: {account, count: 1},
- });
-
- element._config = {
- user: {
- anonymous_coward_name: 'Anonymous Coward',
- },
- };
- assert.deepEqual(element._accountOrAnon(account2), 'Anonymous');
-
- account = makeAccount('OOO');
-
- suggestion = element._makeSuggestion({account});
- assert.deepEqual(suggestion, {
- name: account.name + ' <' + account.email + '> (OOO)',
- value: {account},
- });
-
- suggestion = element._makeSuggestion(account);
- assert.deepEqual(suggestion, {
- name: account.name + ' <' + account.email + '> (OOO)',
- value: {account, count: 1},
- });
-
- sandbox.stub(element, '_reviewerEmail',
- () => { return ''; });
-
- suggestion = element._makeSuggestion(account3);
- assert.deepEqual(suggestion, {
- name: account3.name,
- value: {account: account3, count: 1},
- });
- });
-
- test('_reviewerEmail', () => {
- assert.equal(
- element._reviewerEmail('email@gerritreview.com'),
- '<email@gerritreview.com>');
- assert.equal(element._reviewerEmail(undefined), '');
- });
-
- test('_getReviewerSuggestions excludes owner+reviewers', done => {
- element._getReviewerSuggestions().then(reviewers => {
- // Default is no filtering.
- assert.equal(reviewers.length, 6);
-
- // Set up filter that only accepts suggestion1.
- const accountId = suggestion1.account._account_id;
- element.filter = function(suggestion) {
- return suggestion.account &&
- suggestion.account._account_id === accountId;
- };
-
- element._getReviewerSuggestions().then(reviewers => {
- assert.deepEqual(reviewers, [element._makeSuggestion(suggestion1)]);
- }).then(done);
- });
- });
-
- test('_getReviewerSuggestions short circuits when logged out', () => {
- // API call is already stubbed.
- const xhrSpy = element.$.restAPI.getChangeSuggestedReviewers;
- element._loggedIn = false;
- return element._getReviewerSuggestions('').then(() => {
- assert.isFalse(xhrSpy.called);
- element._loggedIn = true;
- return element._getReviewerSuggestions('').then(() => {
- assert.isTrue(xhrSpy.called);
- });
- });
- });
- });
-
- test('allowAnyUser', done => {
- const suggestReviewerStub =
- sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
- .returns(Promise.resolve([]));
- const suggestAccountStub =
- sandbox.stub(element.$.restAPI, 'getSuggestedAccounts')
- .returns(Promise.resolve([]));
-
- element._getReviewerSuggestions('').then(() => {
- assert.isTrue(suggestReviewerStub.calledOnce);
- assert.isTrue(suggestReviewerStub.calledWith(42, ''));
- assert.isFalse(suggestAccountStub.called);
- element.allowAnyUser = true;
-
- element._getReviewerSuggestions('').then(() => {
- assert.isTrue(suggestReviewerStub.calledOnce);
- assert.isTrue(suggestAccountStub.calledOnce);
- assert.isTrue(suggestAccountStub.calledWith('cansee:42 '));
- done();
- });
- });
- });
- test('account-text-changed fired when input text changed and allowAnyInput',
- () => {
- // Spy on query, as that is called when _updateSuggestions proceeds.
- const changeStub = sandbox.stub();
- element.allowAnyInput = true;
- sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
- .returns(Promise.resolve([]));
- element.addEventListener('account-text-changed', changeStub);
- element.$.input.text = 'a';
- assert.isTrue(changeStub.calledOnce);
- element.$.input.text = 'ab';
- assert.isTrue(changeStub.calledTwice);
- });
-
- test('account-text-changed not fired when input text changed without ' +
- 'allowAnyUser', () => {
- // Spy on query, as that is called when _updateSuggestions proceeds.
- const changeStub = sandbox.stub();
- sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
- .returns(Promise.resolve([]));
- element.addEventListener('account-text-changed', changeStub);
- element.$.input.text = 'a';
- assert.isFalse(changeStub.called);
- });
-
- test('setText', () => {
- // Spy on query, as that is called when _updateSuggestions proceeds.
- const suggestSpy = sandbox.spy(element.$.input, 'query');
- element.setText('test text');
- flushAsynchronousOperations();
-
- assert.equal(element.$.input.$.input.value, 'test text');
- assert.isFalse(suggestSpy.called);
- });
- });
-</script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index 278875e7e4..e12f10def9 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
<link rel="import" href="../../admin/gr-create-change-dialog/gr-create-change-dialog.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
@@ -62,13 +62,13 @@ limitations under the License.
color: var(--deemphasized-text-color);
}
#confirmSubmitDialog .changeSubject {
- margin: 1em;
+ margin: var(--spacing-l);
text-align: center;
}
iron-icon {
color: inherit;
height: 1.2rem;
- margin-right: .2rem;
+ margin-right: var(--spacing-xs);
width: 1.2rem;
}
gr-button {
@@ -92,7 +92,7 @@ limitations under the License.
}
gr-button {
--gr-button: {
- padding: .5em;
+ padding: var(--spacing-m);
white-space: nowrap;
}
}
@@ -101,7 +101,7 @@ limitations under the License.
margin: 0;
}
#actionLoadingMessage {
- margin: .5em;
+ margin: var(--spacing-m);
text-align: center;
}
#moreMessage {
@@ -124,11 +124,12 @@ limitations under the License.
link
title$="[[action.title]]"
has-tooltip="[[_computeHasTooltip(action.title)]]"
+ position-below="true"
data-action-key$="[[action.__key]]"
data-action-type$="[[action.__type]]"
data-label$="[[action.label]]"
disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
- on-tap="_handleActionTap">
+ on-click="_handleActionTap">
<iron-icon class$="[[_computeHasIcon(action)]]" icon$="gr-icons:[[action.icon]]"></iron-icon>
[[action.label]]
</gr-button>
@@ -144,11 +145,12 @@ limitations under the License.
link
title$="[[action.title]]"
has-tooltip="[[_computeHasTooltip(action.title)]]"
+ position-below="true"
data-action-key$="[[action.__key]]"
data-action-type$="[[action.__type]]"
data-label$="[[action.label]]"
disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
- on-tap="_handleActionTap">
+ on-click="_handleActionTap">
<iron-icon class$="[[_computeHasIcon(action)]]" icon$="gr-icons:[[action.icon]]"></iron-icon>
[[action.label]]
</gr-button>
@@ -177,7 +179,7 @@ limitations under the License.
on-cancel="_handleConfirmDialogCancel"
branch="[[change.branch]]"
has-parent="[[hasParent]]"
- rebase-on-current="[[revisionActions.rebase.rebaseOnCurrent]]"
+ rebase-on-current="[[_computeRebaseOnCurrent(_revisionRebaseAction)]]"
hidden></gr-confirm-rebase-dialog>
<gr-confirm-cherrypick-dialog id="confirmCherrypick"
class="confirmDialog"
@@ -217,7 +219,7 @@ limitations under the License.
id="confirmSubmitDialog"
class="confirmDialog"
change="[[change]]"
- action="[[revisionActions.submit]]"
+ action="[[_revisionSubmitAction]]"
on-cancel="_handleConfirmDialogCancel"
on-confirm="_handleSubmitConfirm" hidden></gr-confirm-submit-dialog>
<gr-dialog id="createFollowUpDialog"
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 9182bbceb6..9c0c38fc2d 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -50,7 +50,6 @@
OPTIONAL: 'OPTIONAL',
};
- // TODO(davido): Add the rest of the change actions.
const ChangeActions = {
ABANDON: 'abandon',
DELETE: '/',
@@ -72,7 +71,6 @@
WIP: 'wip',
};
- // TODO(andybons): Add the rest of the revision actions.
const RevisionActions = {
CHERRYPICK: 'cherrypick',
REBASE: 'rebase',
@@ -193,7 +191,6 @@
Polymer({
is: 'gr-change-actions',
- _legacyUndefinedCheck: true,
/**
* Fired when the change should be reloaded.
@@ -269,8 +266,23 @@
/** @type {?} */
revisionActions: {
type: Object,
+ notify: true,
value() { return {}; },
},
+ // If property binds directly to [[revisionActions.submit]] it is not
+ // updated when revisionActions doesn't contain submit action.
+ /** @type {?} */
+ _revisionSubmitAction: {
+ type: Object,
+ computed: '_getSubmitAction(revisionActions)',
+ },
+ // If property binds directly to [[revisionActions.rebase]] it is not
+ // updated when revisionActions doesn't contain rebase action.
+ /** @type {?} */
+ _revisionRebaseAction: {
+ type: Object,
+ computed: '_getRebaseAction(revisionActions)',
+ },
privateByDefault: String,
_loading: {
@@ -394,6 +406,7 @@
RevisionActions,
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.PatchSetBehavior,
Gerrit.RESTClientBehavior,
],
@@ -415,6 +428,26 @@
this._handleLoadingComplete();
},
+ _getSubmitAction(revisionActions) {
+ return this._getRevisionAction(revisionActions, 'submit', null);
+ },
+
+ _getRebaseAction(revisionActions) {
+ return this._getRevisionAction(revisionActions, 'rebase', null);
+ },
+
+ _getRevisionAction(revisionActions, actionName, emptyActionValue) {
+ if (!revisionActions) {
+ return undefined;
+ }
+ if (revisionActions[actionName] === undefined) {
+ // Return null to fire an event when reveisionActions was loaded
+ // but doesn't contain actionName. undefined doesn't fire an event
+ return emptyActionValue;
+ }
+ return revisionActions[actionName];
+ },
+
reload() {
if (!this.changeNum || !this.latestPatchNum) {
return Promise.resolve();
@@ -425,6 +458,10 @@
if (!revisionActions) { return; }
this.revisionActions = revisionActions;
+ this._sendShowRevisionActions({
+ change: this.change,
+ revisionActions,
+ });
this._handleLoadingComplete();
}).catch(err => {
this.fire('show-alert', {message: ERR_REVISION_ACTIONS});
@@ -437,6 +474,13 @@
Gerrit.awaitPluginsLoaded().then(() => this._loading = false);
},
+ _sendShowRevisionActions(detail) {
+ this.$.jsAPI.handleEvent(
+ this.$.jsAPI.EventType.SHOW_REVISION_ACTIONS,
+ detail
+ );
+ },
+
_changeChanged() {
this.reload();
},
@@ -553,6 +597,15 @@
_actionsChanged(actionsChangeRecord, revisionActionsChangeRecord,
additionalActionsChangeRecord) {
+ // Polymer 2: check for undefined
+ if ([
+ actionsChangeRecord,
+ revisionActionsChangeRecord,
+ additionalActionsChangeRecord,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
const additionalActions = (additionalActionsChangeRecord &&
additionalActionsChangeRecord.base) || [];
this.hidden = this._keyCount(actionsChangeRecord) === 0 &&
@@ -573,14 +626,25 @@
* @param {string=} actionName
*/
_deleteAndNotify(actionName) {
- if (this.actions[actionName]) {
+ if (this.actions && this.actions[actionName]) {
delete this.actions[actionName];
- this.notifyPath('actions.' + actionName);
+ // We assign a fake value of 'false' to support Polymer 2
+ // see https://github.com/Polymer/polymer/issues/2631
+ this.notifyPath('actions.' + actionName, false);
}
},
_editStatusChanged(editMode, editPatchsetLoaded,
editBasedOnCurrentPatchSet, disableEdit) {
+ // Polymer 2: check for undefined
+ if ([
+ editMode,
+ editBasedOnCurrentPatchSet,
+ disableEdit,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
if (disableEdit) {
this._deleteAndNotify('publishEdit');
this._deleteAndNotify('rebaseEdit');
@@ -589,10 +653,10 @@
this._deleteAndNotify('edit');
return;
}
- if (editPatchsetLoaded) {
+ if (this.actions && editPatchsetLoaded) {
// Only show actions that mutate an edit if an actual edit patch set
// is loaded.
- if (this.changeIsOpen(this.change.status)) {
+ if (this.changeIsOpen(this.change)) {
if (editBasedOnCurrentPatchSet) {
if (!this.actions.publishEdit) {
this.set('actions.publishEdit', PUBLISH_EDIT);
@@ -614,7 +678,7 @@
this._deleteAndNotify('deleteEdit');
}
- if (this.changeIsOpen(this.change.status)) {
+ if (this.actions && this.changeIsOpen(this.change)) {
// Only show edit button if there is no edit patchset loaded and the
// file list is not in edit mode.
if (editPatchsetLoaded || editMode) {
@@ -843,7 +907,7 @@
_handleActionTap(e) {
e.preventDefault();
let el = Polymer.dom(e).localTarget;
- while (el.is !== 'gr-button') {
+ while (el.tagName.toLowerCase() !== 'gr-button') {
if (!el.parentElement) { return; }
el = el.parentElement;
}
@@ -967,8 +1031,9 @@
},
_calculateDisabled(action, hasKnownChainState) {
- if (action.__key === 'rebase' && hasKnownChainState === false) {
- return true;
+ if (action.__key === 'rebase') {
+ // Rebase button is only disabled when change has no parent(s).
+ return hasKnownChainState === false;
}
return !action.enabled;
},
@@ -1003,7 +1068,6 @@
_handleCherryPickRestApi(conflicts) {
const el = this.$.confirmCherrypick;
if (!el.branch) {
- // TODO(davido): Fix error handling
this.fire('show-alert', {message: ERR_BRANCH_EMPTY});
return;
}
@@ -1307,6 +1371,17 @@
*/
_computeAllActions(changeActionsRecord, revisionActionsRecord,
primariesRecord, additionalActionsRecord, change) {
+ // Polymer 2: check for undefined
+ if ([
+ changeActionsRecord,
+ revisionActionsRecord,
+ primariesRecord,
+ additionalActionsRecord,
+ change,
+ ].some(arg => arg === undefined)) {
+ return [];
+ }
+
const revisionActionValues = this._getActionValues(revisionActionsRecord,
primariesRecord, additionalActionsRecord, ActionType.REVISION);
const changeActionValues = this._getActionValues(changeActionsRecord,
@@ -1395,6 +1470,13 @@
});
},
+ _computeRebaseOnCurrent(revisionRebaseAction) {
+ if (revisionRebaseAction) {
+ return !!revisionRebaseAction.enabled;
+ }
+ return null;
+ },
+
/**
* Occasionally, a change created by a change action is not yet knwon to the
* API for a brief time. Wait for the given change number to be recognized.
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index 9803fabcf3..74d262ab6c 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-actions</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
@@ -112,6 +114,15 @@ limitations under the License.
sandbox.restore();
});
+ test('show-revision-actions event should fire', done => {
+ const spy = sinon.spy(element, '_sendShowRevisionActions');
+ element.reload();
+ flush(() => {
+ assert.isTrue(spy.called);
+ done();
+ });
+ });
+
test('primary and secondary actions split properly', () => {
// Submit should be the only primary action.
assert.equal(element._topLevelPrimaryActions.length, 1);
@@ -352,7 +363,7 @@ limitations under the License.
action.enabled = false;
assert.equal(
- element._calculateDisabled(action, hasKnownChainState), true);
+ element._calculateDisabled(action, hasKnownChainState), false);
});
test('rebase change', done => {
@@ -1511,4 +1522,69 @@ limitations under the License.
assert.equal(reportStub.lastCall.args[0], 'type-key');
});
});
+
+ suite('getChangeRevisionActions returns only some actions', () => {
+ let element;
+ let sandbox;
+ let changeRevisionActions;
+
+ setup(() => {
+ stub('gr-rest-api-interface', {
+ getChangeRevisionActions() {
+ return Promise.resolve(changeRevisionActions);
+ },
+ send(method, url, payload) {
+ return Promise.reject(new Error('error'));
+ },
+ getProjectConfig() { return Promise.resolve({}); },
+ });
+
+ sandbox = sinon.sandbox.create();
+ sandbox.stub(Gerrit, 'awaitPluginsLoaded').returns(Promise.resolve());
+
+ element = fixture('basic');
+ // getChangeRevisionActions is not called without
+ // set the following properies
+ element.change = {};
+ element.changeNum = '42';
+ element.latestPatchNum = '2';
+
+
+ sandbox.stub(element.$.confirmCherrypick.$.restAPI,
+ 'getRepoBranches').returns(Promise.resolve([]));
+ sandbox.stub(element.$.confirmMove.$.restAPI,
+ 'getRepoBranches').returns(Promise.resolve([]));
+ return element.reload();
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('confirmSubmitDialog and confirmRebase properties are changed', () => {
+ changeRevisionActions = {};
+ element.reload();
+ assert.strictEqual(element.$.confirmSubmitDialog.action, null);
+ assert.strictEqual(element.$.confirmRebase.rebaseOnCurrent, null);
+ });
+
+ test('_computeRebaseOnCurrent', () => {
+ const rebaseAction = {
+ enabled: true,
+ label: 'Rebase',
+ method: 'POST',
+ title: 'Rebase onto tip of branch or parent change',
+ };
+
+ // When rebase is enabled initially, rebaseOnCurrent should be set to
+ // true.
+ assert.isTrue(element._computeRebaseOnCurrent(rebaseAction));
+
+ delete rebaseAction.enabled;
+
+ // When rebase is not enabled initially, rebaseOnCurrent should be set to
+ // false.
+ assert.isFalse(element._computeRebaseOnCurrent(rebaseAction));
+ });
+ });
</script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
index f1d3cd3faf..b8bea9ca36 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-metadata</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../plugins/gr-plugin-host/gr-plugin-host.html">
<link rel="import" href="gr-change-metadata.html">
@@ -87,6 +89,7 @@ limitations under the License.
teardown(() => {
sandbox.restore();
+ Gerrit._testOnly_resetPlugins();
});
suite('by default', () => {
@@ -104,7 +107,7 @@ limitations under the License.
suite('with plugin style', () => {
setup(done => {
- Gerrit._resetPlugins();
+ Gerrit._testOnly_resetPlugins();
const pluginHost = fixture('plugin-host');
pluginHost.config = {
plugin: {
@@ -139,7 +142,7 @@ limitations under the License.
new URL('test/plugin.html?' + Math.random(),
window.location.href).toString());
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
- Gerrit._setPluginsPending([]);
+ Gerrit._loadPlugins([]);
element = createElement();
});
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
index a09b730157..6a92d96ecd 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
@@ -15,9 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../../styles/gr-change-metadata-shared-styles.html">
+<link rel="import" href="../../../styles/gr-change-view-integration-shared-styles.html">
<link rel="import" href="../../../styles/gr-voting-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
@@ -35,38 +37,17 @@ limitations under the License.
<link rel="import" href="../gr-change-requirements/gr-change-requirements.html">
<link rel="import" href="../gr-commit-info/gr-commit-info.html">
<link rel="import" href="../gr-reviewer-list/gr-reviewer-list.html">
+<link rel="import" href="../../shared/gr-account-list/gr-account-list.html">
+<script src="../../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js"></script>
<dom-module id="gr-change-metadata">
<template>
+ <style include="gr-change-metadata-shared-styles"></style>
<style include="shared-styles">
:host {
display: table;
}
- section {
- display: table-row;
- }
- section:not(:first-of-type) .title,
- section:not(:first-of-type) .value {
- padding-top: .5em;
- }
- section:not(:first-of-type) {
- margin-top: 1em;
- }
- .title,
- .value {
- display: table-cell;
- }
- .title {
- color: var(--deemphasized-text-color);
- font-weight: var(--font-weight-bold);
- max-width: 20em;
- padding-left: var(--metadata-horizontal-padding);
- padding-right: .5em;
- word-break: break-word;
- }
- .value {
- padding-right: var(--metadata-horizontal-padding);
- }
gr-change-requirements {
--requirements-horizontal-padding: var(--metadata-horizontal-padding);
}
@@ -74,7 +55,7 @@ limitations under the License.
max-width: 20ch;
overflow: hidden;
text-overflow: ellipsis;
- vertical-align: middle;
+ vertical-align: top;
white-space: nowrap;
}
gr-editable-label {
@@ -99,14 +80,14 @@ limitations under the License.
pointer-events: none;
}
.hashtagChip {
- margin-bottom: .5em;
+ margin-bottom: var(--spacing-m);
}
#externalStyle {
display: block;
}
.parentList.merge {
list-style-type: decimal;
- padding-left: 1em;
+ padding-left: var(--spacing-l);
}
.parentList gr-commit-info {
display: inline-block;
@@ -116,7 +97,7 @@ limitations under the License.
display: none;
}
.icon {
- margin: -.25em 0;
+ margin: -3px 0;
}
.icon.help,
.icon.notTrusted {
@@ -133,9 +114,12 @@ limitations under the License.
display: inline-block;
}
.separatedSection {
- border-top: 1px solid var(--border-color);
- margin-top: .5em;
- padding: .5em 0;
+ margin-top: var(--spacing-l);
+ padding: var(--spacing-m) 0;
+ }
+ .hashtag gr-linked-chip,
+ .topic gr-linked-chip {
+ --linked-chip-text-color: var(--link-color);
}
</style>
<gr-external-style id="externalStyle" name="change-metadata">
@@ -195,9 +179,9 @@ limitations under the License.
id="assigneeValue"
placeholder="Set assignee..."
accounts="{{_assignee}}"
- change="[[change]]"
readonly="[[_computeAssigneeReadOnly(_mutable, change)]]"
- allow-any-user></gr-account-list>
+ suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
+ </gr-account-list>
</span>
</section>
<section>
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index 94b85fdf59..4fe5ddc70c 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -17,17 +17,6 @@
(function() {
'use strict';
- const Defs = {};
-
- /**
- * @typedef {{
- * message: string,
- * icon: string,
- * class: string,
- * }}
- */
- Defs.PushCertificateValidation;
-
const HASHTAG_ADD_MESSAGE = 'Add Hashtag';
const SubmitTypeLabel = {
@@ -61,7 +50,6 @@
Polymer({
is: 'gr-change-metadata',
- _legacyUndefinedCheck: true,
/**
* Fired when the change topic is changed.
@@ -101,7 +89,7 @@
computed: '_computeHashtagReadOnly(_mutable, change)',
},
/**
- * @type {Defs.PushCertificateValidation}
+ * @type {Gerrit.PushCertificateValidation}
*/
_pushCertificateValidation: {
type: Object,
@@ -177,7 +165,7 @@
},
_computeHideStrategy(change) {
- return !this.changeIsOpen(change.status);
+ return !this.changeIsOpen(change);
},
/**
@@ -215,19 +203,21 @@
this._settingTopic = false;
this.set(['change', 'topic'], newTopic);
if (newTopic !== lastTopic) {
- this.dispatchEvent(
- new CustomEvent('topic-changed', {bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'topic-changed', {bubbles: true, composed: true}));
}
});
},
_showAddTopic(changeRecord, settingTopic) {
- const hasTopic = !!changeRecord && !!changeRecord.base.topic;
+ const hasTopic = !!changeRecord &&
+ !!changeRecord.base && !!changeRecord.base.topic;
return !hasTopic && !settingTopic;
},
_showTopicChip(changeRecord, settingTopic) {
- const hasTopic = !!changeRecord && !!changeRecord.base.topic;
+ const hasTopic = !!changeRecord &&
+ !!changeRecord.base && !!changeRecord.base.topic;
return hasTopic && !settingTopic;
},
@@ -241,13 +231,15 @@
this.set(['change', 'hashtags'], newHashtag);
if (newHashtag !== lastHashtag) {
this.dispatchEvent(
- new CustomEvent('hashtag-changed', {bubbles: true}));
+ new CustomEvent('hashtag-changed', {
+ bubbles: true, composed: true}));
}
});
},
_computeTopicReadOnly(mutable, change) {
return !mutable ||
+ !change ||
!change.actions ||
!change.actions.topic ||
!change.actions.topic.enabled;
@@ -255,6 +247,7 @@
_computeHashtagReadOnly(mutable, change) {
return !mutable ||
+ !change ||
!change.actions ||
!change.actions.hashtags ||
!change.actions.hashtags.enabled;
@@ -262,6 +255,7 @@
_computeAssigneeReadOnly(mutable, change) {
return !mutable ||
+ !change ||
!change.actions ||
!change.actions.assignee ||
!change.actions.assignee.enabled;
@@ -291,11 +285,11 @@
},
/**
- * @return {?Defs.PushCertificateValidation} object representing data for
+ * @return {?Gerrit.PushCertificateValidation} object representing data for
* the push validation.
*/
_computePushCertificateValidation(serverConfig, change) {
- if (!serverConfig || !serverConfig.receive ||
+ if (!change || !serverConfig || !serverConfig.receive ||
!serverConfig.receive.enable_signed_push) {
return null;
}
@@ -348,6 +342,7 @@
},
_computeBranchURL(project, branch) {
+ if (!this.change || !this.change.status) return '';
return Gerrit.Nav.getUrlForBranch(branch, project,
this.change.status == this.ChangeStatus.NEW ? 'open' :
this.change.status.toLowerCase());
@@ -368,7 +363,7 @@
target.disabled = false;
this.set(['change', 'topic'], '');
this.dispatchEvent(
- new CustomEvent('topic-changed', {bubbles: true}));
+ new CustomEvent('topic-changed', {bubbles: true, composed: true}));
}).catch(err => {
target.disabled = false;
return;
@@ -407,7 +402,7 @@
* @return {Object|null} either an accound or null.
*/
_getNonOwnerRole(change, role) {
- if (!change.current_revision ||
+ if (!change || !change.current_revision ||
!change.revisions[change.current_revision]) {
return null;
}
@@ -446,13 +441,18 @@
},
_computeParentsLabel(parents) {
- return parents.length > 1 ? 'Parents' : 'Parent';
+ return parents && parents.length > 1 ? 'Parents' : 'Parent';
},
_computeParentListClass(parents, parentIsCurrent) {
+ // Undefined check for polymer 2
+ if (parents === undefined || parentIsCurrent === undefined) {
+ return '';
+ }
+
return [
'parentList',
- parents.length > 1 ? 'merge' : 'nonMerge',
+ parents && parents.length > 1 ? 'merge' : 'nonMerge',
parentIsCurrent ? 'current' : 'notCurrent',
].join(' ');
},
@@ -467,5 +467,12 @@
// dom-if.
this.$$('.topicEditableLabel').open();
},
+
+ _getReviewerSuggestionsProvider(change) {
+ const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
+ change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY);
+ provider.init();
+ return provider;
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
index ce69d45eb6..d60847b077 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-metadata</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../core/gr-router/gr-router.html">
<link rel="import" href="gr-change-metadata.html">
@@ -747,7 +749,7 @@ limitations under the License.
},
'0.1',
'http://some/plugins/url.html');
- Gerrit._setPluginsCount(0);
+ Gerrit._loadPlugins([]);
flush(() => {
assert.strictEqual(hookEl.plugin, plugin);
assert.strictEqual(hookEl.change, element.change);
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/test/plugin.html b/polygerrit-ui/app/elements/change/gr-change-metadata/test/plugin.html
index d0ed4a1c1e..b3aa98f0ea 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/test/plugin.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/test/plugin.html
@@ -7,20 +7,22 @@
</dom-module>
<dom-module id="my-plugin-style">
- <style>
- html {
- --change-metadata-assignee: {
- display: none;
+ <template>
+ <style>
+ html {
+ --change-metadata-assignee: {
+ display: none;
+ }
+ --change-metadata-label-status: {
+ display: none;
+ }
+ --change-metadata-strategy: {
+ display: none;
+ }
+ --change-metadata-topic: {
+ display: none;
+ }
}
- --change-metadata-label-status: {
- display: none;
- }
- --change-metadata-strategy: {
- display: none;
- }
- --change-metadata-topic: {
- display: none;
- }
- }
- </style>
+ </style>
+ </template>
</dom-module>
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.html b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.html
index e79bce16a0..47ff7f78d8 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.html
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -34,8 +34,10 @@ limitations under the License.
.status {
color: #FFA62F;
display: inline-block;
- font-family: var(--monospace-font-family);
text-align: center;
+ font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
}
.approved.status {
color: var(--vote-text-color-recommended);
@@ -46,8 +48,8 @@ limitations under the License.
iron-icon {
color: inherit;
}
- .name {
- font-weight: var(--font-weight-bold);
+ .status iron-icon {
+ vertical-align: top;
}
section {
display: table-row;
@@ -57,16 +59,15 @@ limitations under the License.
}
.title {
min-width: 10em;
- padding: .75em .5em 0 var(--requirements-horizontal-padding);
- vertical-align: top;
+ padding: var(--spacing-s) var(--spacing-m) 0 var(--requirements-horizontal-padding);
}
.value {
- padding: .6em .5em 0 0;
- vertical-align: middle;
+ padding: var(--spacing-s) 0 0 0;
}
.title,
.value {
display: table-cell;
+ vertical-align: top;
}
.hidden {
display: none;
@@ -75,12 +76,10 @@ limitations under the License.
cursor: pointer;
}
.showHide .title {
- border-top: 1px solid var(--border-color);
- padding-bottom: .5em;
- padding-top: .5em;
+ padding-bottom: var(--spacing-m);
+ padding-top: var(--spacing-l);
}
.showHide .value {
- border-top: 1px solid var(--border-color);
padding-top: 0;
vertical-align: middle;
}
@@ -89,7 +88,7 @@ limitations under the License.
float: right;
}
.spacer {
- height: .5em;
+ height: var(--spacing-m);
}
</style>
<template
@@ -128,7 +127,7 @@ limitations under the License.
<section class$="spacer [[_computeShowOptional(_optionalLabels.*)]]"></section>
<section
show-bottom-border$="[[_showOptionalLabels]]"
- on-tap="_handleShowHide"
+ on-click="_handleShowHide"
class$="showHide [[_computeShowOptional(_optionalLabels.*)]]">
<div class="title">Other labels</div>
<div class="value">
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
index 4717ff9624..dfdcd59ea5 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-change-requirements',
- _legacyUndefinedCheck: true,
properties: {
/** @type {?} */
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html
index 3f35158acb..2ceac39606 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-requirements</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-change-requirements.html">
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 857976223e..29f2d83c50 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -15,11 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../../bower_components/paper-tabs/paper-tabs.html">
+<link rel="import" href="/bower_components/paper-tabs/paper-tabs.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
@@ -42,6 +43,7 @@ limitations under the License.
<link rel="import" href="../../shared/revision-info/revision-info.html">
<link rel="import" href="../gr-change-actions/gr-change-actions.html">
<link rel="import" href="../gr-change-metadata/gr-change-metadata.html">
+<link rel="import" href="../../shared/gr-icons/gr-icons.html">
<link rel="import" href="../gr-commit-info/gr-commit-info.html">
<link rel="import" href="../gr-download-dialog/gr-download-dialog.html">
<link rel="import" href="../gr-file-list-header/gr-file-list-header.html">
@@ -61,25 +63,25 @@ limitations under the License.
}
.container.loading {
color: var(--deemphasized-text-color);
- padding: 1em var(--default-horizontal-margin);
+ padding: var(--spacing-l);
}
.header {
align-items: center;
background-color: var(--table-header-background-color);
border-bottom: 1px solid var(--border-color);
display: flex;
- padding: .55em var(--default-horizontal-margin);
+ padding: var(--spacing-s) var(--spacing-l);
z-index: 99; /* Less than gr-overlay's backdrop */
}
.header.editMode {
background-color: var(--edit-mode-background-color);
}
.header .download {
- margin-right: 1em;
+ margin-right: var(--spacing-l);
}
gr-change-status {
display: initial;
- margin: .1em .1em .1em .4em;
+ margin: var(--spacing-xxs) var(--spacing-xxs) var(--spacing-xxs) var(--spacing-s);
}
gr-change-status:first-child {
margin-left: 0;
@@ -88,17 +90,16 @@ limitations under the License.
align-items: center;
display: flex;
flex: 1;
- font-size: 1.2rem;
+ font-size: var(--font-size-h3);
}
.headerTitle .headerSubject {
font-weight: var(--font-weight-bold);
}
#replyBtn {
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
gr-change-star {
- font-size: var(--font-size-normal);
- margin-right: .25em;
+ margin-right: var(--spacing-xs);
}
gr-reply-dialog {
width: 60em;
@@ -106,6 +107,9 @@ limitations under the License.
.changeStatus {
text-transform: capitalize;
}
+ .changeInfo {
+ background-color: var(--table-header-background-color);
+ }
/* Strong specificity here is needed due to
https://github.com/Polymer/polymer/issues/2531 */
.container section.changeInfo {
@@ -114,32 +118,33 @@ limitations under the License.
.changeId {
color: var(--deemphasized-text-color);
font-family: var(--font-family);
- margin-top: 1em;
+ margin-top: var(--spacing-l);
}
.changeMetadata {
- border-right: 1px solid var(--border-color);
- }
- /* Prevent plugin text from overflowing. */
- #change_plugins {
- word-break: break-word;
+ /* Limit meta section to half of the screen at max */
+ max-width: 50%;
}
.commitMessage {
font-family: var(--monospace-font-family);
- margin-right: 1em;
- margin-bottom: 1em;
- max-width: var(--commit-message-max-width, 72ch);;
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
+ margin-right: var(--spacing-l);
+ margin-bottom: var(--spacing-l);
+ /* Account for border and padding and rounding errors. */
+ max-width: calc(72ch + 2px + 2*var(--spacing-m) + 0.4px);
}
.commitMessage gr-linked-text {
word-break: break-word;
}
#commitMessageEditor {
- min-width: 72ch;
+ /* Account for border and padding and rounding errors. */
+ min-width: calc(72ch + 2px + 2*var(--spacing-m) + 0.4px);
}
.editCommitMessage {
- margin-top: 1em;
+ margin-top: var(--spacing-l);
+
--gr-button: {
- padding-left: 0;
- padding-right: 0;
+ padding: 5px 0px;
}
}
.changeStatuses,
@@ -166,7 +171,7 @@ limitations under the License.
.relatedChanges {
flex: 1 1 auto;
overflow: hidden;
- padding: 1em 0;
+ padding: var(--spacing-l) 0;
}
.mobile {
display: none;
@@ -178,7 +183,7 @@ limitations under the License.
border: 0;
border-top: 1px solid var(--border-color);
height: 0;
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
#commitMessage.collapsed {
max-height: 36em;
@@ -187,7 +192,7 @@ limitations under the License.
#relatedChanges {
}
#relatedChanges.collapsed {
- margin-bottom: 1.1em;
+ margin-bottom: var(--spacing-l);
max-height: var(--relation-chain-max-height, 2em);
overflow: hidden;
}
@@ -195,8 +200,8 @@ limitations under the License.
display: flex;
flex-direction: column;
flex-shrink: 0;
- margin: 1em 0;
- padding: 0 1em;
+ margin: var(--spacing-l) 0;
+ padding: 0 var(--spacing-l);
}
.collapseToggleContainer {
display: flex;
@@ -212,7 +217,7 @@ limitations under the License.
display: block;
}
#relatedChangesToggle {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
padding-top: var(--related-change-btn-top-padding, 0);
}
.showOnEdit {
@@ -231,12 +236,12 @@ limitations under the License.
paper-tabs {
background-color: var(--table-header-background-color);
border-top: 1px solid var(--border-color);
- height: 3rem;
+ height: calc(var(--line-height-normal) + 2*var(--spacing-m));
--paper-tabs-selection-bar-color: var(--link-color);
}
paper-tab {
box-sizing: border-box;
- max-width: 15rem;
+ max-width: 12em;
--paper-tab-ink: var(--link-color);
}
gr-thread-list,
@@ -249,14 +254,9 @@ limitations under the License.
#uploadHelpOverlay {
width: 50em;
}
- @media screen and (min-width: 80em) {
- .commitMessage {
- max-width: var(--commit-message-max-width, 100ch);
- }
- }
#metadata {
- --metadata-horizontal-padding: 1em;
- padding-top: 1em;
+ --metadata-horizontal-padding: var(--spacing-l);
+ padding-top: var(--spacing-l);
width: 100%;
}
/* NOTE: If you update this breakpoint, also update the
@@ -266,8 +266,7 @@ limitations under the License.
padding: 0;
}
#relatedChanges {
- border-top: 1px solid var(--border-color);
- padding-top: 1em;
+ padding-top: var(--spacing-l);
}
#commitAndRelated {
flex-direction: column;
@@ -293,14 +292,14 @@ limitations under the License.
align-items: flex-start;
flex-direction: column;
flex: 1;
- padding: .5em var(--default-horizontal-margin);
+ padding: var(--spacing-s) var(--spacing-l);
}
gr-change-star {
vertical-align: middle;
}
.headerTitle {
flex-wrap: wrap;
- font-size: 1.1rem;
+ font-size: var(--font-size-h3);
}
.desktop {
display: none;
@@ -322,16 +321,10 @@ limitations under the License.
}
.commitContainer {
margin: 0;
- padding: 1em;
- }
- .relatedChanges,
- .changeMetadata {
- font-size: var(--font-size-normal);
+ padding: var(--spacing-l);
}
.changeMetadata {
- border-bottom: 1px solid var(--border-color);
- border-right: none;
- margin-top: .25em;
+ margin-top: var(--spacing-xs);
max-width: none;
}
#metadata,
@@ -340,7 +333,7 @@ limitations under the License.
}
.commitActions {
display: block;
- margin-top: 1em;
+ margin-top: var(--spacing-l);
width: 100%;
}
.commitMessage {
@@ -366,6 +359,7 @@ limitations under the License.
<div
id="mainContent"
class="container"
+ on-show-checks-table="_handleShowTab"
hidden$="{{_loading}}">
<div class$="[[_computeHeaderClass(_editMode)]]">
<div class="headerTitle">
@@ -405,7 +399,7 @@ limitations under the License.
disable-edit="[[disableEdit]]"
has-parent="[[hasParent]]"
actions="[[_change.actions]]"
- revision-actions="[[_currentRevisionActions]]"
+ revision-actions="{{_currentRevisionActions}}"
change-num="[[_changeNum]]"
change-status="[[_change.status]]"
commit-num="[[_commitInfo.commit]]"
@@ -435,10 +429,6 @@ limitations under the License.
parent-is-current="[[_parentIsCurrent]]"
on-show-reply-dialog="_handleShowReplyDialog">
</gr-change-metadata>
- <!-- Plugins insert content into following container.
- Stop-gap until PolyGerrit plugins interface is ready.
- This will not work with Shadow DOM. -->
- <div id="change_plugins"></div>
</div>
<div id="mainChangeInfo" class="changeInfo-column mainChangeInfo">
<div id="commitAndRelated" class="hideOnMobileOverlay">
@@ -450,7 +440,7 @@ limitations under the License.
hidden$="[[!_loggedIn]]"
primary
disabled="[[_replyDisabled]]"
- on-tap="_handleReplyTap">[[_replyButtonLabel]]</gr-button>
+ on-click="_handleReplyTap">[[_replyButtonLabel]]</gr-button>
</div>
<div
id="commitMessage"
@@ -467,7 +457,7 @@ limitations under the License.
</gr-editable-content>
<gr-button link
class="editCommitMessage"
- on-tap="_handleEditCommitMessage"
+ on-click="_handleEditCommitMessage"
hidden$="[[_hideEditCommitMessage]]">Edit</gr-button>
<div class="changeId" hidden$="[[!_changeIdCommitMessageError]]">
<hr>
@@ -487,7 +477,7 @@ limitations under the License.
link
id="commitCollapseToggleButton"
class="collapseToggleButton"
- on-tap="_toggleCommitCollapsed">
+ on-click="_toggleCommitCollapsed">
[[_computeCollapseText(_commitCollapsed)]]
</gr-button>
</div>
@@ -515,7 +505,7 @@ limitations under the License.
link
id="relatedChangesToggleButton"
class="collapseToggleButton"
- on-tap="_toggleRelatedChangesCollapsed">
+ on-click="_toggleRelatedChangesCollapsed">
[[_computeCollapseText(_relatedChangesCollapsed)]]
</gr-button>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 4788999235..02cf861afa 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -60,11 +60,11 @@
};
const CHANGE_DATA_TIMING_LABEL = 'ChangeDataLoaded';
+ const CHANGE_RELOAD_TIMING_LABEL = 'ChangeReloaded';
const SEND_REPLY_TIMING_LABEL = 'SendReply';
Polymer({
is: 'gr-change-view',
- _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -203,7 +203,6 @@
_loading: Boolean,
/** @type {?} */
_projectConfig: Object,
- _rebaseOnCurrent: Boolean,
_replyButtonLabel: {
type: String,
value: 'Reply',
@@ -226,7 +225,8 @@
},
_changeStatuses: {
type: String,
- computed: '_computeChangeStatusChips(_change, _mergeable)',
+ computed:
+ '_computeChangeStatusChips(_change, _mergeable, _submitEnabled)',
},
_commitCollapsed: {
type: Boolean,
@@ -247,8 +247,14 @@
value: false,
observer: '_updateToggleContainerClass',
},
- _parentIsCurrent: Boolean,
- _submitEnabled: Boolean,
+ _parentIsCurrent: {
+ type: Boolean,
+ computed: '_isParentCurrent(_currentRevisionActions)',
+ },
+ _submitEnabled: {
+ type: Boolean,
+ computed: '_isSubmitEnabled(_currentRevisionActions)',
+ },
/** @type {?} */
_mergeable: {
@@ -281,6 +287,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PatchSetBehavior,
Gerrit.RESTClientBehavior,
@@ -307,6 +314,7 @@
keyboardShortcuts() {
return {
[this.Shortcut.SEND_REPLY]: null, // DOC_ONLY binding
+ [this.Shortcut.EMOJI_DROPDOWN]: null, // DOC_ONLY binding
[this.Shortcut.REFRESH_CHANGE]: '_handleRefreshChange',
[this.Shortcut.OPEN_REPLY_DIALOG]: '_handleOpenReplyDialog',
[this.Shortcut.OPEN_DOWNLOAD_DIALOG]:
@@ -346,7 +354,7 @@
!== this._dynamicTabHeaderEndpoints.length) {
console.warn('Different number of tab headers and tab content.');
}
- });
+ }).then(() => this._setPrimaryTab());
this.addEventListener('comment-save', this._handleCommentSave.bind(this));
this.addEventListener('comment-refresh', this._reloadDrafts.bind(this));
@@ -410,12 +418,31 @@
this._showMessagesView = this.$.commentTabs.selected === 0;
},
- _handleFileTabChange() {
+ _handleFileTabChange(e) {
const selectedIndex = this.$$('#primaryTabs').selected;
this._showFileTabContent = selectedIndex === 0;
// Initial tab is the static files list.
- this._selectedFilesTabPluginEndpoint =
+ const newSelectedTab =
this._dynamicTabContentEndpoints[selectedIndex - 1];
+ if (newSelectedTab !== this._selectedFilesTabPluginEndpoint) {
+ this._selectedFilesTabPluginEndpoint = newSelectedTab;
+
+ const tabName = this._selectedFilesTabPluginEndpoint || 'files';
+ const source = e && e.type ? e.type : '';
+ this.$.reporting.reportInteraction('tab-changed',
+ `tabname: ${tabName}, source: ${source}`);
+ }
+ },
+
+ _handleShowTab(e) {
+ const idx = this._dynamicTabContentEndpoints.indexOf(e.detail.tab);
+ if (idx === -1) {
+ console.warn(e.detail.tab + ' tab not found');
+ return;
+ }
+ this.$$('#primaryTabs').selected = idx + 1;
+ this.$$('#primaryTabs').scrollIntoView();
+ this.$.reporting.reportInteraction('show-tab', e.detail.tab);
},
_handleEditCommitMessage(e) {
@@ -452,20 +479,33 @@
this._editingCommitMessage = false;
},
- _computeChangeStatusChips(change, mergeable) {
+ _computeChangeStatusChips(change, mergeable, submitEnabled) {
+ // Polymer 2: check for undefined
+ if ([
+ change,
+ mergeable,
+ ].some(arg => arg === undefined)) {
+ // To keep consistent with Polymer 1, we are returning undefined
+ // if not all dependencies are defined
+ return undefined;
+ }
+
// Show no chips until mergeability is loaded.
- if (mergeable === null || mergeable === undefined) { return []; }
+ if (mergeable === null) {
+ return [];
+ }
const options = {
includeDerived: true,
mergeable: !!mergeable,
- submitEnabled: this._submitEnabled,
+ submitEnabled: !!submitEnabled,
};
return this.changeStatuses(change, options);
},
_computeHideEditCommitMessage(loggedIn, editing, change, editMode) {
- if (!loggedIn || editing || change.status === this.ChangeStatus.MERGED ||
+ if (!loggedIn || editing ||
+ (change && change.status === this.ChangeStatus.MERGED) ||
editMode) {
return true;
}
@@ -495,6 +535,7 @@
},
_computeTotalCommentCounts(unresolvedCount, changeComments) {
+ if (!changeComments) return undefined;
const draftCount = changeComments.computeDraftCount();
const unresolvedString = GrCountStringFormatter.computeString(
unresolvedCount, 'unresolved');
@@ -736,17 +777,21 @@
});
},
+ _setPrimaryTab() {
+ // Selected has to be set after the paper-tabs are visible because
+ // the selected underline depends on calculations made by the browser.
+ this.$.commentTabs.selected = 0;
+ const primaryTabs = this.$$('#primaryTabs');
+ if (primaryTabs) primaryTabs.selected = 0;
+ },
+
_performPostLoadTasks() {
this._maybeShowReplyDialog();
this._maybeShowRevertDialog();
this._sendShowChangeEvent();
- // Selected has to be set after the paper-tabs are visible because
- // the selected underline depends on calculations made by the browser.
- this.$.commentTabs.selected = 0;
- const primaryTabs = this.$$('#primaryTabs');
- if (primaryTabs) primaryTabs.selected = 0;
+ this._setPrimaryTab();
this.async(() => {
if (this.viewState.scrollTop) {
@@ -759,7 +804,12 @@
});
},
- _paramsAndChangeChanged(value) {
+ _paramsAndChangeChanged(value, change) {
+ // Polymer 2: check for undefined
+ if ([value, change].some(arg => arg === undefined)) {
+ return;
+ }
+
// If the change number or patch range is different, then reset the
// selected file index.
const patchRangeState = this.viewState.patchRange;
@@ -814,7 +864,8 @@
Gerrit.awaitPluginsLoaded()
.then(this._getLoggedIn.bind(this))
.then(loggedIn => {
- if (!loggedIn || this._change.status !== this.ChangeStatus.MERGED) {
+ if (!loggedIn || !this._change ||
+ this._change.status !== this.ChangeStatus.MERGED) {
// Do not display dialog if not logged-in or the change is not
// merged.
return;
@@ -856,19 +907,22 @@
_changeChanged(change) {
if (!change || !this._patchRange || !this._allPatchSets) { return; }
+ // We get the parent first so we keep the original value for basePatchNum
+ // and not the updated value.
const parent = this._getBasePatchNum(change, this._patchRange);
- this.set('_patchRange.basePatchNum', parent);
this.set('_patchRange.patchNum', this._patchRange.patchNum ||
this.computeLatestPatchNum(this._allPatchSets));
+ this.set('_patchRange.basePatchNum', parent);
+
const title = change.subject + ' (' + change.change_id.substr(0, 9) + ')';
this.fire('title-change', {title});
},
/**
* Gets base patch number, if it is a parent try and decide from
- * preference weather to default to `auto merge`, `Parent 1` or `PARENT`.
+ * preference whether to default to `auto merge`, `Parent 1` or `PARENT`.
*
* @param {Object} change
* @param {Object} patchRange
@@ -899,8 +953,8 @@
return 'PARENT';
},
- _computeShowPrimaryTabs(dynamicTabContentEndpoints) {
- return dynamicTabContentEndpoints.length > 0;
+ _computeShowPrimaryTabs(dynamicTabHeaderEndpoints) {
+ return dynamicTabHeaderEndpoints && dynamicTabHeaderEndpoints.length > 0;
},
_computeChangeUrl(change) {
@@ -933,6 +987,11 @@
},
_computeChangeIdCommitMessageError(commitMessage, change) {
+ // Polymer 2: check for undefined
+ if ([commitMessage, change].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
if (!commitMessage) { return CHANGE_ID_ERROR.MISSING; }
// Find the last match in the commit message:
@@ -987,6 +1046,11 @@
},
_computeReplyButtonLabel(changeRecord, canStartReview) {
+ // Polymer 2: check for undefined
+ if ([changeRecord, canStartReview].some(arg => arg === undefined)) {
+ return 'Reply';
+ }
+
if (canStartReview) {
return 'Start review';
}
@@ -1152,6 +1216,7 @@
},
_getProjectConfig() {
+ if (!this._change) return;
return this.$.restAPI.getProjectConfig(this._change.project).then(
config => {
this._projectConfig = config;
@@ -1162,18 +1227,6 @@
return this.$.restAPI.getPreferences();
},
- _updateRebaseAction(revisionActions) {
- if (revisionActions && revisionActions.rebase) {
- revisionActions.rebase.rebaseOnCurrent =
- !!revisionActions.rebase.enabled;
- this._parentIsCurrent = !revisionActions.rebase.enabled;
- revisionActions.rebase.enabled = true;
- } else {
- this._parentIsCurrent = true;
- }
- return revisionActions;
- },
-
_prepareCommitMsgForLinkify(msg) {
// TODO(wyatta) switch linkify sequence, see issue 5526.
// This is a zero-with space. It is added to prevent the linkify library
@@ -1201,7 +1254,7 @@
if (!this._patchRange.patchNum &&
change.current_revision === edit.base_revision) {
change.current_revision = edit.commit.commit;
- this._patchRange.patchNum = this.EDIT_NAME;
+ this.set('_patchRange.patchNum', this.EDIT_NAME);
// Because edits are fibbed as revisions and added to the revisions
// array, and revision actions are always derived from the 'latest'
// patch set, we must copy over actions from the patch set base.
@@ -1239,8 +1292,6 @@
this._latestCommitMessage = null;
}
- // Update the submit enabled based on current revision.
- this._submitEnabled = this._isSubmitEnabled(currentRevision);
const lineHeight = getComputedStyle(this).lineHeight;
@@ -1257,8 +1308,6 @@
currentRevision.commit.commit = latestRevisionSha;
}
this._commitInfo = currentRevision.commit;
- this._currentRevisionActions =
- this._updateRebaseAction(currentRevision.actions);
this._selectedRevision = currentRevision;
// TODO: Fetch and process files.
} else {
@@ -1270,9 +1319,17 @@
});
},
- _isSubmitEnabled(currentRevision) {
- return !!(currentRevision.actions && currentRevision.actions.submit &&
- currentRevision.actions.submit.enabled);
+ _isSubmitEnabled(revisionActions) {
+ return !!(revisionActions && revisionActions.submit &&
+ revisionActions.submit.enabled);
+ },
+
+ _isParentCurrent(revisionActions) {
+ if (revisionActions && revisionActions.rebase) {
+ return !revisionActions.rebase.enabled;
+ } else {
+ return true;
+ }
},
_getEdit() {
@@ -1282,6 +1339,7 @@
_getLatestCommitMessage() {
return this.$.restAPI.getChangeCommitInfo(this._changeNum,
this.computeLatestPatchNum(this._allPatchSets)).then(commitInfo => {
+ if (!commitInfo) return Promise.resolve();
this._latestCommitMessage =
this._prepareCommitMsgForLinkify(commitInfo.message);
});
@@ -1349,15 +1407,17 @@
/**
* Reload the change.
*
- * @param {boolean=} opt_reloadRelatedChanges Reloads the related chanegs
- * when true.
+ * @param {boolean=} opt_isLocationChange Reloads the related changes
+ * when true and ends reporting events that started on location change.
* @return {Promise} A promise that resolves when the core data has loaded.
* Some non-core data loading may still be in-flight when the core data
* promise resolves.
*/
- _reload(opt_reloadRelatedChanges) {
+ _reload(opt_isLocationChange) {
this._loading = true;
this._relatedChangesCollapsed = true;
+ this.$.reporting.time(CHANGE_RELOAD_TIMING_LABEL);
+ this.$.reporting.time(CHANGE_DATA_TIMING_LABEL);
// Array to house all promises related to data requests.
const allDataPromises = [];
@@ -1370,7 +1430,13 @@
// Resolves when the loading flag is set to false, meaning that some
// change content may start appearing.
const loadingFlagSet = detailCompletes
- .then(() => { this._loading = false; });
+ .then(() => { this._loading = false; })
+ .then(() => {
+ this.$.reporting.timeEnd(CHANGE_RELOAD_TIMING_LABEL);
+ if (opt_isLocationChange) {
+ this.$.reporting.changeDisplayed();
+ }
+ });
// Resolves when the project config has loaded.
const projectConfigLoaded = detailCompletes
@@ -1430,20 +1496,20 @@
coreDataPromise = mergeabilityLoaded;
}
- if (opt_reloadRelatedChanges) {
+ if (opt_isLocationChange) {
const relatedChangesLoaded = coreDataPromise
.then(() => this.$.relatedChanges.reload());
allDataPromises.push(relatedChangesLoaded);
}
- this.$.reporting.time(CHANGE_DATA_TIMING_LABEL);
Promise.all(allDataPromises).then(() => {
this.$.reporting.timeEnd(CHANGE_DATA_TIMING_LABEL);
- this.$.reporting.changeFullyLoaded();
+ if (opt_isLocationChange) {
+ this.$.reporting.changeFullyLoaded();
+ }
});
- return coreDataPromise
- .then(() => { this.$.reporting.changeDisplayed(); });
+ return coreDataPromise;
},
/**
@@ -1458,6 +1524,10 @@
},
_getMergeability() {
+ if (!this._change) {
+ this._mergeable = null;
+ return Promise.resolve();
+ }
// If the change is closed, it is not mergeable. Note: already merged
// changes are obviously not mergeable, but the mergeability API will not
// answer for abandoned changes.
@@ -1595,8 +1665,7 @@
_computeShowRelatedToggle() {
// Make sure the max height has been applied, since there is now content
// to populate.
- // TODO update to polymer 2.x syntax
- if (!this.getComputedStyleValue('--relation-chain-max-height')) {
+ if (!util.getComputedStyleValue('--relation-chain-max-height', this)) {
this._updateRelatedChangeMaxHeight();
}
// Prevents showMore from showing when click on related change, since the
@@ -1690,6 +1759,10 @@
},
_computeEditMode(patchRangeRecord, paramsRecord) {
+ if ([patchRangeRecord, paramsRecord].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
if (paramsRecord.base && paramsRecord.base.edit) { return true; }
const patchRange = patchRangeRecord.base || {};
@@ -1775,7 +1848,7 @@
},
_computeCurrentRevision(currentRevision, revisions) {
- return revisions && revisions[currentRevision];
+ return currentRevision && revisions && revisions[currentRevision];
},
_computeDiffPrefsDisabled(disableDiffPrefs, loggedIn) {
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index f74160b503..e00bab5860 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<link rel="import" href="../../edit/gr-edit-constants.html">
<link rel="import" href="gr-change-view.html">
@@ -79,7 +81,7 @@ limitations under the License.
});
element = fixture('basic');
sandbox.stub(element.$.actions, 'reload').returns(Promise.resolve());
- Gerrit._setPluginsCount(0);
+ Gerrit._loadPlugins([]);
});
teardown(done => {
@@ -90,9 +92,7 @@ limitations under the License.
});
getCustomCssValue = cssParam => {
- // TODO: Update to be compatible with 2.x when we upgrade from
- // 1.x to 2.x.
- return element.getComputedStyleValue(cssParam);
+ return util.getComputedStyleValue(cssParam, element);
};
test('_handleMessageAnchorTap', () => {
@@ -345,8 +345,10 @@ limitations under the License.
],
};
setup(() => {
+ // Fake computeDraftCount as its required for ChangeComments,
+ // see gr-comment-api#reloadDrafts.
reloadStub = sandbox.stub(element.$.commentAPI, 'reloadDrafts')
- .returns(Promise.resolve({drafts}));
+ .returns(Promise.resolve({drafts, computeDraftCount: () => 1}));
});
test('drafts are reloaded when reload-drafts fired', done => {
@@ -530,65 +532,11 @@ limitations under the License.
assert.equal(result, 'CC=\u200Btest@google.com');
}),
- test('_updateRebaseAction', () => {
- const currentRevisionActions = {
- cherrypick: {
- enabled: true,
- label: 'Cherry Pick',
- method: 'POST',
- title: 'cherrypick',
- },
- rebase: {
- enabled: true,
- label: 'Rebase',
- method: 'POST',
- title: 'Rebase onto tip of branch or parent change',
- },
- };
- element._parentIsCurrent = undefined;
-
- // Rebase enabled should always end up true.
- // When rebase is enabled initially, rebaseOnCurrent should be set to
- // true.
- assert.equal(element._updateRebaseAction(currentRevisionActions),
- currentRevisionActions);
-
- assert.isTrue(currentRevisionActions.rebase.enabled);
- assert.isTrue(currentRevisionActions.rebase.rebaseOnCurrent);
- assert.isFalse(element._parentIsCurrent);
-
- delete currentRevisionActions.rebase.enabled;
-
- // When rebase is not enabled initially, rebaseOnCurrent should be set to
- // false.
- assert.equal(element._updateRebaseAction(currentRevisionActions),
- currentRevisionActions);
-
- assert.isTrue(currentRevisionActions.rebase.enabled);
- assert.isFalse(currentRevisionActions.rebase.rebaseOnCurrent);
- assert.isTrue(element._parentIsCurrent);
- });
-
test('_isSubmitEnabled', () => {
assert.isFalse(element._isSubmitEnabled({}));
- assert.isFalse(element._isSubmitEnabled({actions: {}}));
- assert.isFalse(element._isSubmitEnabled({actions: {submit: {}}}));
+ assert.isFalse(element._isSubmitEnabled({submit: {}}));
assert.isTrue(element._isSubmitEnabled(
- {actions: {submit: {enabled: true}}}));
- });
-
- test('_updateRebaseAction sets _parentIsCurrent on no rebase', () => {
- const currentRevisionActions = {
- cherrypick: {
- enabled: true,
- label: 'Cherry Pick',
- method: 'POST',
- title: 'cherrypick',
- },
- };
- element._parentIsCurrent = undefined;
- element._updateRebaseAction(currentRevisionActions);
- assert.isTrue(element._parentIsCurrent);
+ {submit: {enabled: true}}));
});
test('_reload is called when an approved label is removed', () => {
@@ -1599,32 +1547,44 @@ limitations under the License.
sandbox.stub(Gerrit.Nav, 'navigateToRelativeUrl');
// Delete
- fileList.dispatchEvent(new CustomEvent('file-action-tap',
- {detail: {action: Actions.DELETE.id, path: 'foo'}, bubbles: true}));
+ fileList.dispatchEvent(new CustomEvent('file-action-tap', {
+ detail: {action: Actions.DELETE.id, path: 'foo'},
+ bubbles: true,
+ composed: true,
+ }));
flushAsynchronousOperations();
assert.isTrue(controls.openDeleteDialog.called);
assert.equal(controls.openDeleteDialog.lastCall.args[0], 'foo');
// Restore
- fileList.dispatchEvent(new CustomEvent('file-action-tap',
- {detail: {action: Actions.RESTORE.id, path: 'foo'}, bubbles: true}));
+ fileList.dispatchEvent(new CustomEvent('file-action-tap', {
+ detail: {action: Actions.RESTORE.id, path: 'foo'},
+ bubbles: true,
+ composed: true,
+ }));
flushAsynchronousOperations();
assert.isTrue(controls.openRestoreDialog.called);
assert.equal(controls.openRestoreDialog.lastCall.args[0], 'foo');
// Rename
- fileList.dispatchEvent(new CustomEvent('file-action-tap',
- {detail: {action: Actions.RENAME.id, path: 'foo'}, bubbles: true}));
+ fileList.dispatchEvent(new CustomEvent('file-action-tap', {
+ detail: {action: Actions.RENAME.id, path: 'foo'},
+ bubbles: true,
+ composed: true,
+ }));
flushAsynchronousOperations();
assert.isTrue(controls.openRenameDialog.called);
assert.equal(controls.openRenameDialog.lastCall.args[0], 'foo');
// Open
- fileList.dispatchEvent(new CustomEvent('file-action-tap',
- {detail: {action: Actions.OPEN.id, path: 'foo'}, bubbles: true}));
+ fileList.dispatchEvent(new CustomEvent('file-action-tap', {
+ detail: {action: Actions.OPEN.id, path: 'foo'},
+ bubbles: true,
+ composed: true,
+ }));
flushAsynchronousOperations();
assert.isTrue(Gerrit.Nav.getEditUrlForDiff.called);
@@ -1830,5 +1790,50 @@ limitations under the License.
MockInteractions.tap(element.$.changeStar.$$('button'));
assert.isTrue(stub.called);
});
+
+ suite('gr-reporting tests', () => {
+ setup(() => {
+ element._patchRange = {
+ basePatchNum: 'PARENT',
+ patchNum: 1,
+ };
+ sandbox.stub(element, '_getChangeDetail').returns(Promise.resolve());
+ sandbox.stub(element, '_getProjectConfig').returns(Promise.resolve());
+ sandbox.stub(element, '_reloadComments').returns(Promise.resolve());
+ sandbox.stub(element, '_getMergeability').returns(Promise.resolve());
+ sandbox.stub(element, '_getLatestCommitMessage')
+ .returns(Promise.resolve());
+ });
+
+ test('don\'t report changedDisplayed on reply', done => {
+ const changeDisplayStub =
+ sandbox.stub(element.$.reporting, 'changeDisplayed');
+ const changeFullyLoadedStub =
+ sandbox.stub(element.$.reporting, 'changeFullyLoaded');
+ element._handleReplySent();
+ flush(() => {
+ assert.isFalse(changeDisplayStub.called);
+ assert.isFalse(changeFullyLoadedStub.called);
+ done();
+ });
+ });
+
+ test('report changedDisplayed on _paramsChanged', done => {
+ const changeDisplayStub =
+ sandbox.stub(element.$.reporting, 'changeDisplayed');
+ const changeFullyLoadedStub =
+ sandbox.stub(element.$.reporting, 'changeFullyLoaded');
+ element._paramsChanged({
+ view: Gerrit.Nav.View.CHANGE,
+ changeNum: 101,
+ project: 'test-project',
+ });
+ flush(() => {
+ assert.isTrue(changeDisplayStub.called);
+ assert.isTrue(changeFullyLoadedStub.called);
+ done();
+ });
+ });
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
index e4183df6ff..a7e65a300b 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
@@ -18,7 +18,7 @@ limitations under the License.
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-formatted-text/gr-formatted-text.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -43,10 +43,10 @@ limitations under the License.
}
.container {
display: flex;
- margin: .5em 0;
+ margin: var(--spacing-m) 0;
}
.lineNum {
- margin-right: .5em;
+ margin-right: var(--spacing-m);
min-width: 10em;
text-align: right;
}
@@ -57,7 +57,7 @@ limitations under the License.
@media screen and (max-width: 50em) {
.container {
flex-direction: column;
- margin: 0 0 .5em .5em;
+ margin: 0 0 var(--spacing-m) var(--spacing-m);
}
.lineNum {
min-width: initial;
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
index b19ca62dce..42cb976ff3 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
@@ -18,7 +18,6 @@
'use strict';
Polymer({
is: 'gr-comment-list',
- _legacyUndefinedCheck: true,
behaviors: [
Gerrit.BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
index 9996abce14..c18ae8d9f1 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-comment-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-comment-list.html">
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.html b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.html
index b6c8fcc6c2..902bf41095 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.html
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-copy-clipboard/gr-copy-clipboard.html">
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
index ddab319bfe..e2fcdffb2c 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-commit-info',
- _legacyUndefinedCheck: true,
properties: {
change: Object,
@@ -47,11 +46,21 @@
},
_computeShowWebLink(change, commitInfo, serverConfig) {
+ // Polymer 2: check for undefined
+ if ([change, commitInfo, serverConfig].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const weblink = this._getWeblink(change, commitInfo, serverConfig);
return !!weblink && !!weblink.url;
},
_computeWebLink(change, commitInfo, serverConfig) {
+ // Polymer 2: check for undefined
+ if ([change, commitInfo, serverConfig].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const {url} = this._getWeblink(change, commitInfo, serverConfig) || {};
return url;
},
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
index 438a3f300e..f271a706e5 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-commit-info</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../core/gr-router/gr-router.html">
<link rel="import" href="gr-commit-info.html">
@@ -119,6 +121,7 @@ limitations under the License.
},
],
};
+ element.serverConfig = {};
assert.isOk(element._computeShowWebLink(element.change,
element.commitInfo, element.serverConfig));
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
index 8803eb3e62..05a2bb2575 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.html
@@ -15,8 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -42,6 +43,8 @@ limitations under the License.
}
iron-autogrow-textarea {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
padding: 0;
width: 73ch; /* Add a char to account for the border. */
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
index a371e13eb9..524876e21f 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-confirm-abandon-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -38,6 +37,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
],
@@ -55,6 +55,7 @@
_handleConfirmTap(e) {
e.preventDefault();
+ e.stopPropagation();
this._confirm();
},
@@ -64,6 +65,7 @@
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('cancel', null, {bubbles: false});
},
});
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html
index 95d53740ec..cc4b80ea6a 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-confirm-abandon-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-confirm-abandon-dialog.html">
@@ -49,19 +51,26 @@ limitations under the License.
test('_handleConfirmTap', () => {
const confirmHandler = sandbox.stub();
element.addEventListener('confirm', confirmHandler);
- sandbox.stub(element, '_confirm');
+ sandbox.spy(element, '_handleConfirmTap');
+ sandbox.spy(element, '_confirm');
element.$$('gr-dialog').fire('confirm');
assert.isTrue(confirmHandler.called);
+ assert.isTrue(confirmHandler.calledOnce);
+ assert.isTrue(element._handleConfirmTap.called);
assert.isTrue(element._confirm.called);
+ assert.isTrue(element._confirm.called);
+ assert.isTrue(element._confirm.calledOnce);
});
test('_handleCancelTap', () => {
const cancelHandler = sandbox.stub();
element.addEventListener('cancel', cancelHandler);
- sandbox.stub(element, '_handleCancelTap');
+ sandbox.spy(element, '_handleCancelTap');
element.$$('gr-dialog').fire('cancel');
assert.isTrue(cancelHandler.called);
+ assert.isTrue(cancelHandler.calledOnce);
assert.isTrue(element._handleCancelTap.called);
+ assert.isTrue(element._handleCancelTap.calledOnce);
});
});
</script>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html
index cd196eceae..b9e91559a6 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
index 2e8af0e140..a0da331fa3 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
@@ -19,7 +19,10 @@
Polymer({
is: 'gr-confirm-cherrypick-conflict-dialog',
- _legacyUndefinedCheck: true,
+
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
/**
* Fired when the confirm button is pressed.
@@ -35,11 +38,13 @@
_handleConfirmTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('confirm', null, {bubbles: false});
},
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('cancel', null, {bubbles: false});
},
});
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html
index 77b102ca2c..f411de45b5 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-confirm-cherrypick-conflict-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-confirm-cherrypick-conflict-dialog.html">
@@ -47,19 +49,23 @@ limitations under the License.
test('_handleConfirmTap', () => {
const confirmHandler = sandbox.stub();
element.addEventListener('confirm', confirmHandler);
- sandbox.stub(element, '_handleConfirmTap');
+ sandbox.spy(element, '_handleConfirmTap');
element.$$('gr-dialog').fire('confirm');
assert.isTrue(confirmHandler.called);
+ assert.isTrue(confirmHandler.calledOnce);
assert.isTrue(element._handleConfirmTap.called);
+ assert.isTrue(element._handleConfirmTap.calledOnce);
});
test('_handleCancelTap', () => {
const cancelHandler = sandbox.stub();
element.addEventListener('cancel', cancelHandler);
- sandbox.stub(element, '_handleCancelTap');
+ sandbox.spy(element, '_handleCancelTap');
element.$$('gr-dialog').fire('cancel');
assert.isTrue(cancelHandler.called);
+ assert.isTrue(cancelHandler.calledOnce);
assert.isTrue(element._handleCancelTap.called);
+ assert.isTrue(element._handleCancelTap.calledOnce);
});
});
</script>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
index 84558ac427..8ddcd83a9a 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.html
@@ -15,8 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
@@ -43,16 +45,16 @@ limitations under the License.
.main label,
.main input[type="text"] {
display: block;
- font: inherit;
width: 100%;
}
.main .message {
- border: groove;
width: 100%;
}
iron-autogrow-textarea {
+ font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
padding: 0;
-
--iron-autogrow-textarea: {
font-family: var(--monospace-font-family);
width: 72ch;
@@ -77,12 +79,17 @@ limitations under the License.
<label for="baseInput">
Provide base commit sha1 for cherry-pick
</label>
- <input
- is="iron-input"
- id="baseCommitInput"
+ <iron-input
maxlength="40"
placeholder="(optional)"
bind-value="{{baseCommit}}">
+ <input
+ is="iron-input"
+ id="baseCommitInput"
+ maxlength="40"
+ placeholder="(optional)"
+ bind-value="{{baseCommit}}">
+ </iron-input>
<label for="messageInput">
Cherry Pick Commit Message
</label>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
index bdc0bb2722..5278540d15 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-confirm-cherrypick-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -51,11 +50,24 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
observers: [
'_computeMessage(changeStatus, commitNum, commitMessage)',
],
_computeMessage(changeStatus, commitNum, commitMessage) {
+ // Polymer 2: check for undefined
+ if ([
+ changeStatus,
+ commitNum,
+ commitMessage,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
let newMessage = commitMessage;
if (changeStatus === 'MERGED') {
@@ -66,11 +78,13 @@
_handleConfirmTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('confirm', null, {bubbles: false});
},
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('cancel', null, {bubbles: false});
},
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
index 5c51fe0bb5..22a2abab3a 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-confirm-cherrypick-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-confirm-cherrypick-dialog.html">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html
index 621ef0a942..24c1132887 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.html
@@ -15,8 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
@@ -47,11 +48,9 @@ limitations under the License.
.main label,
.main input[type="text"] {
display: block;
- font: inherit;
width: 100%;
}
.main .message {
- border: groove;
width: 100%;
}
.warning {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
index 91600ff541..c6b0adfd83 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-confirm-move-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -47,13 +46,19 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
_handleConfirmTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('confirm', null, {bubbles: false});
},
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('cancel', null, {bubbles: false});
},
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html
index e619425225..8d6e029d54 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-confirm-move-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-confirm-move-dialog.html">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html
index 912bbfa6a2..cf2721ac78 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -41,14 +41,13 @@ limitations under the License.
.parentRevisionContainer label,
.parentRevisionContainer input[type="text"] {
display: block;
- font: inherit;
width: 100%;
}
.parentRevisionContainer label {
- margin-bottom: .2em;
+ margin-bottom: var(--spacing-xs);
}
.rebaseOption {
- margin: .5em 0;
+ margin: var(--spacing-m) 0;
}
</style>
<gr-dialog
@@ -63,7 +62,7 @@ limitations under the License.
<input id="rebaseOnParentInput"
name="rebaseOptions"
type="radio"
- on-tap="_handleRebaseOnParent">
+ on-click="_handleRebaseOnParent">
<label id="rebaseOnParentLabel" for="rebaseOnParentInput">
Rebase on parent change
</label>
@@ -78,7 +77,7 @@ limitations under the License.
name="rebaseOptions"
type="radio"
disabled$="[[!_displayTipOption(rebaseOnCurrent, hasParent)]]"
- on-tap="_handleRebaseOnTip">
+ on-click="_handleRebaseOnTip">
<label id="rebaseOnTipLabel" for="rebaseOnTipInput">
Rebase on top of the [[branch]]
branch<span hidden$="[[!hasParent]]">
@@ -94,7 +93,7 @@ limitations under the License.
<input id="rebaseOnOtherInput"
name="rebaseOptions"
type="radio"
- on-tap="_handleRebaseOnOther">
+ on-click="_handleRebaseOnOther">
<label id="rebaseOnOtherLabel" for="rebaseOnOtherInput">
Rebase on a specific change, ref, or commit <span hidden$="[[!hasParent]]">
(breaks relation chain)
@@ -107,7 +106,7 @@ limitations under the License.
query="[[_query]]"
no-debounce
text="{{_text}}"
- on-tap="_handleEnterChangeNumberTap"
+ on-click="_handleEnterChangeNumberClick"
allow-non-suggested-values
placeholder="Change number, ref, or commit hash">
</gr-autocomplete>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
index 77bc798f7b..54ce271b5b 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-confirm-rebase-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -120,6 +119,7 @@
_handleConfirmTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.dispatchEvent(new CustomEvent('confirm',
{detail: {base: this._getSelectedBase()}}));
this._text = '';
@@ -127,6 +127,7 @@
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.dispatchEvent(new CustomEvent('cancel'));
this._text = '';
},
@@ -135,7 +136,7 @@
this.$.parentInput.focus();
},
- _handleEnterChangeNumberTap() {
+ _handleEnterChangeNumberClick() {
this.$.rebaseOnOtherInput.checked = true;
},
@@ -144,6 +145,11 @@
* the corresponding value to be submitted.
*/
_updateSelectedOption(rebaseOnCurrent, hasParent) {
+ // Polymer 2: check for undefined
+ if ([rebaseOnCurrent, hasParent].some(arg => arg === undefined)) {
+ return;
+ }
+
if (this._displayParentOption(rebaseOnCurrent, hasParent)) {
this.$.rebaseOnParentInput.checked = true;
} else if (this._displayTipOption(rebaseOnCurrent, hasParent)) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
index c6e9ec4804..cd5b13087c 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-confirm-rebase-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-confirm-rebase-dialog.html">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
index 9e5f1de4ef..2e1e6ae2c9 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.html
@@ -15,10 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
<dom-module id="gr-confirm-revert-dialog">
<template>
@@ -37,6 +39,8 @@ limitations under the License.
}
iron-autogrow-textarea {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
padding: 0;
width: 73ch; /* Add a char to account for the border. */
@@ -53,15 +57,17 @@ limitations under the License.
on-cancel="_handleCancelTap">
<div class="header" slot="header">Revert Merged Change</div>
<div class="main" slot="main">
- <label for="messageInput">
- Revert Commit Message
- </label>
- <iron-autogrow-textarea
- id="messageInput"
- class="message"
- autocomplete="on"
- max-rows="15"
- bind-value="{{message}}"></iron-autogrow-textarea>
+ <gr-endpoint-decorator name="confirm-revert-change">
+ <label for="messageInput">
+ Revert Commit Message
+ </label>
+ <iron-autogrow-textarea
+ id="messageInput"
+ class="message"
+ autocomplete="on"
+ max-rows="15"
+ bind-value="{{message}}"></iron-autogrow-textarea>
+ </gr-endpoint-decorator>
</div>
</gr-dialog>
</template>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
index 93e21c77c8..662b64c349 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-confirm-revert-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -40,6 +39,10 @@
message: String,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
populateRevertMessage(message, commitHash) {
// Figure out what the revert title should be.
const originalTitle = message.split('\n')[0];
@@ -56,11 +59,13 @@
_handleConfirmTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('confirm', null, {bubbles: false});
},
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('cancel', null, {bubbles: false});
},
});
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
index c5a1bde748..6e41555051 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-confirm-revert-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-confirm-revert-dialog.html">
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html
index 1036b7fbcc..1a1276dc38 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html
@@ -15,11 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
+<link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-confirm-submit-dialog">
@@ -29,7 +31,7 @@ limitations under the License.
min-width: 40em;
}
p {
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
@media screen and (max-width: 50em) {
#dialog {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
index 93d38df33d..e86e21c5d1 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-confirm-submit-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -56,11 +55,13 @@
_handleConfirmTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.dispatchEvent(new CustomEvent('confirm', {bubbles: false}));
},
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.dispatchEvent(new CustomEvent('cancel', {bubbles: false}));
},
});
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html
index 86c15f6a6e..40fa29aa47 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-confirm-submit-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<link rel="import" href="gr-confirm-submit-dialog.html">
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
index 28d25d27da..e20bbd76a7 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
@@ -15,8 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -31,7 +32,7 @@ limitations under the License.
}
section {
display: flex;
- padding: .5em 1.5em;
+ padding: var(--spacing-m) var(--spacing-xl);
}
section:not(:first-of-type) {
border-top: 1px solid var(--border-color);
@@ -39,7 +40,7 @@ limitations under the License.
.flexContainer {
display: flex;
justify-content: space-between;
- padding-top: .75em;
+ padding-top: var(--spacing-m);
}
.footer {
justify-content: flex-end;
@@ -52,15 +53,15 @@ limitations under the License.
}
.patchFiles,
.archivesContainer {
- padding-bottom: .5em;
+ padding-bottom: var(--spacing-m);
}
.patchFiles {
- margin-right: 2em;
+ margin-right: var(--spacing-xxl);
}
.patchFiles a,
.archives a {
display: inline-block;
- margin-right: 1em;
+ margin-right: var(--spacing-l);
}
.patchFiles a:last-of-type,
.archives a:last-of-type {
@@ -120,7 +121,7 @@ limitations under the License.
<span class="closeButtonContainer">
<gr-button id="closeButton"
link
- on-tap="_handleCloseTap">Close</gr-button>
+ on-click="_handleCloseTap">Close</gr-button>
</span>
</section>
</template>
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
index b6c155e2ad..b297a14a31 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-download-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the user presses the close button.
@@ -48,6 +47,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.PatchSetBehavior,
Gerrit.RESTClientBehavior,
],
@@ -70,16 +70,17 @@
_computeDownloadCommands(change, patchNum, _selectedScheme) {
let commandObj;
+ if (!change) return [];
for (const rev of Object.values(change.revisions || {})) {
if (this.patchNumEquals(rev._number, patchNum) &&
- rev.fetch.hasOwnProperty(_selectedScheme)) {
+ rev && rev.fetch && rev.fetch.hasOwnProperty(_selectedScheme)) {
commandObj = rev.fetch[_selectedScheme].commands;
break;
}
}
const commands = [];
for (const title in commandObj) {
- if (!commandObj.hasOwnProperty(title)) { continue; }
+ if (!commandObj || !commandObj.hasOwnProperty(title)) { continue; }
commands.push({
title,
command: commandObj[title],
@@ -116,6 +117,10 @@
* @return {string} Not sure why there was a mismatch
*/
_computeDownloadLink(change, patchNum, opt_zip) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum].some(arg => arg === undefined)) {
+ return '';
+ }
return this.changeBaseURL(change.project, change._number, patchNum) +
'/patch?' + (opt_zip ? 'zip' : 'download');
},
@@ -129,6 +134,11 @@
* @return {string}
*/
_computeDownloadFilename(change, patchNum, opt_zip) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum].some(arg => arg === undefined)) {
+ return '';
+ }
+
let shortRev = '';
for (const rev in change.revisions) {
if (this.patchNumEquals(change.revisions[rev]._number, patchNum)) {
@@ -140,6 +150,10 @@
},
_computeHidePatchFile(change, patchNum) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum].some(arg => arg === undefined)) {
+ return false;
+ }
for (const rev of Object.values(change.revisions || {})) {
if (this.patchNumEquals(rev._number, patchNum)) {
const parentLength = rev.commit && rev.commit.parents ?
@@ -151,11 +165,20 @@
},
_computeArchiveDownloadLink(change, patchNum, format) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum, format].some(arg => arg === undefined)) {
+ return '';
+ }
return this.changeBaseURL(change.project, change._number, patchNum) +
'/archive?format=' + format;
},
_computeSchemes(change, patchNum) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum].some(arg => arg === undefined)) {
+ return [];
+ }
+
for (const rev of Object.values(change.revisions || {})) {
if (this.patchNumEquals(rev._number, patchNum)) {
const fetch = rev.fetch;
@@ -175,6 +198,7 @@
_handleCloseTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('close', null, {bubbles: false});
},
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
index 214363b1cf..82574808b4 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-download-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-download-dialog.html">
@@ -144,7 +146,8 @@ limitations under the License.
});
test('anchors use download attribute', () => {
- const anchors = Polymer.dom(element.root).querySelectorAll('a');
+ const anchors = Array.from(
+ Polymer.dom(element.root).querySelectorAll('a'));
assert.isTrue(!anchors.some(a => !a.hasAttribute('download')));
});
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
index f7d90eb472..9146125d4e 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
@@ -29,6 +30,7 @@ limitations under the License.
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-icons/gr-icons.html">
<link rel="import" href="../gr-file-list-constants.html">
+<link rel="import" href="../gr-commit-info/gr-commit-info.html">
<dom-module id="gr-file-list-header">
<template>
@@ -47,7 +49,7 @@ limitations under the License.
background-color: var(--table-header-background-color);
border-top: 1px solid var(--border-color);
display: flex;
- padding: 6px var(--default-horizontal-margin);
+ padding: var(--spacing-s) var(--spacing-l);
}
.patchInfo-left {
align-items: baseline;
@@ -143,7 +145,7 @@ limitations under the License.
margin-right: -5px;
}
.fileViewActionsLabel {
- margin-right: .2rem;
+ margin-right: var(--spacing-xs);
}
@media screen and (max-width: 50em) {
.patchInfo-header .desktop {
@@ -216,28 +218,28 @@ limitations under the License.
<span class$="[[_computeUploadHelpContainerClass(change, account)]]">
<gr-button link
class="upload"
- on-tap="_handleUploadTap">Update Change</gr-button>
+ on-click="_handleUploadTap">Update Change</gr-button>
</span>
<span class="downloadContainer desktop">
<gr-button link
class="download"
- on-tap="_handleDownloadTap">Download</gr-button>
+ on-click="_handleDownloadTap">Download</gr-button>
</span>
<span class$="includedInContainer [[_hideIncludedIn(change)]] desktop">
<gr-button link
class="includedIn"
- on-tap="_handleIncludedInTap">Included In</gr-button>
+ on-click="_handleIncludedInTap">Included In</gr-button>
</span>
<template is="dom-if"
if="[[_fileListActionsVisible(shownFileCount, _maxFilesForBulkActions)]]">
<gr-button
id="expandBtn"
link
- on-tap="_expandAllDiffs">Expand All</gr-button>
+ on-click="_expandAllDiffs">Expand All</gr-button>
<gr-button
id="collapseBtn"
link
- on-tap="_collapseAllDiffs">Collapse All</gr-button>
+ on-click="_collapseAllDiffs">Collapse All</gr-button>
</template>
<template is="dom-if"
if="[[!_fileListActionsVisible(shownFileCount, _maxFilesForBulkActions)]]">
@@ -261,7 +263,7 @@ limitations under the License.
has-tooltip
title="Diff preferences"
class="prefsButton desktop"
- on-tap="_handlePrefsTap"><iron-icon icon="gr-icons:settings"></iron-icon></gr-button>
+ on-click="_handlePrefsTap"><iron-icon icon="gr-icons:settings"></iron-icon></gr-button>
</span>
</div>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
index 8321879ee0..d6fbfc4e18 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
@@ -23,7 +23,6 @@
Polymer({
is: 'gr-file-list-header',
- _legacyUndefinedCheck: true,
/**
* @event expand-diffs
@@ -94,6 +93,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.PatchSetBehavior,
],
@@ -132,10 +132,20 @@
},
_computeDescriptionReadOnly(loggedIn, change, account) {
+ // Polymer 2: check for undefined
+ if ([loggedIn, change, account].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
return !(loggedIn && (account._account_id === change.owner._account_id));
},
_computePatchSetDescription(change, patchNum) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum].some(arg => arg === undefined)) {
+ return;
+ }
+
const rev = this.getRevisionByPatchNum(change.revisions, patchNum);
this._patchsetDescription = (rev && rev.description) ?
rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
@@ -217,6 +227,7 @@
_handleDownloadTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.dispatchEvent(
new CustomEvent('open-download-dialog', {bubbles: false}));
},
@@ -239,6 +250,7 @@
_handleUploadTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.dispatchEvent(
new CustomEvent('open-upload-help-dialog', {bubbles: false}));
},
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html
index adfeeb4c30..ac626ab61a 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-file-list-header</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<link rel="import" href="gr-file-list-header.html">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index f9ee54fca9..649aa5311b 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/async-foreach-behavior/async-foreach-behavior.html">
<link rel="import" href="../../../behaviors/dom-util-behavior/dom-util-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -47,8 +48,8 @@ limitations under the License.
align-items: center;
border-top: 1px solid var(--border-color);
display: flex;
- min-height: 2.25em;
- padding: .2em var(--default-horizontal-margin) .2em calc(var(--default-horizontal-margin) - .35rem);
+ min-height: calc(var(--line-height-normal) + 2*var(--spacing-s));
+ padding: var(--spacing-xs) var(--spacing-l) var(--spacing-xs) calc(var(--spacing-l) - .35rem);
}
:host(.loading) .row {
opacity: .5;
@@ -115,16 +116,13 @@ limitations under the License.
cursor: pointer;
flex: 1;
text-decoration: none;
- white-space: nowrap;
+ /* Wrap it into multiple lines if too long. */
+ white-space: normal;
+ word-break: break-word;
}
.path:hover :first-child {
text-decoration: underline;
}
- .path,
- .path div {
- overflow: hidden;
- text-overflow: ellipsis;
- }
.oldPath {
color: var(--deemphasized-text-color);
}
@@ -137,16 +135,18 @@ limitations under the License.
min-width: 7.5em;
}
.comments {
- padding-left: 2em;
- min-width: 20em;
+ padding-left: var(--spacing-l);
+ min-width: 7.5em;
}
.row:not(.header-row) .stats,
.total-stats {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
display: flex;
}
.sizeBars {
- margin-left: .5em;
+ margin-left: var(--spacing-m);
min-width: 7em;
text-align: center;
}
@@ -165,18 +165,18 @@ limitations under the License.
color: var(--vote-text-color-disliked);
text-align: left;
min-width: 4em;
- padding-left: 0.5em;
+ padding-left: var(--spacing-s);
}
.drafts {
color: #C62828;
font-weight: var(--font-weight-bold);
}
.show-hide {
- margin-left: .35em;
+ margin-left: var(--spacing-s);
width: 1.9em;
}
.fileListButton {
- margin: .5em;
+ margin: var(--spacing-m);
}
.totalChanges {
justify-content: flex-end;
@@ -200,15 +200,11 @@ limitations under the License.
.truncatedFileName {
display: none;
}
- .expanded .fullFileName {
- white-space: normal;
- word-wrap: break-word;
- }
.mobile {
display: none;
}
.reviewed {
- margin-left: 2em;
+ margin-left: var(--spacing-xxl);
width: 15em;
}
.reviewed label {
@@ -234,7 +230,7 @@ limitations under the License.
}
.reviewedLabel {
color: var(--deemphasized-text-color);
- margin-right: 1em;
+ margin-right: var(--spacing-l);
opacity: 0;
}
.reviewedLabel.isReviewed {
@@ -247,10 +243,12 @@ limitations under the License.
.markReviewed,
.pathLink {
display: inline-block;
- margin: -.2em 0;
- padding: .4em 0;
+ margin: -2px 0;
+ padding: var(--spacing-s) 0;
}
- @media screen and (max-width: 50em) {
+
+ /** small screen breakpoint: 768px */
+ @media screen and (max-width: 55em) {
.desktop {
display: none;
}
@@ -285,7 +283,7 @@ limitations under the License.
</style>
<div
id="container"
- on-tap="_handleFileListTap">
+ on-click="_handleFileListClick">
<div class="header-row row">
<div class="status"></div>
<div class="path">File</div>
@@ -498,7 +496,7 @@ limitations under the License.
<gr-button
class="fileListButton"
id="incrementButton"
- link on-tap="_incrementNumFilesShown">
+ link on-click="_incrementNumFilesShown">
[[_computeIncrementText(numFilesShown, _files)]]
</gr-button>
<gr-tooltip-content
@@ -508,7 +506,7 @@ limitations under the License.
<gr-button
class="fileListButton"
id="showAllButton"
- link on-tap="_showAllFiles">
+ link on-click="_showAllFiles">
[[_computeShowAllText(_files)]]
</gr-button><!--
--></gr-tooltip-content>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 8dba99d8fb..8d9b9058a2 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -41,29 +41,8 @@
U: 'Unchanged',
};
- const Defs = {};
-
- /**
- * Object containing layout values to be used in rendering size-bars.
- * `max{Inserted,Deleted}` represent the largest values of the
- * `lines_inserted` and `lines_deleted` fields of the files respectively. The
- * `max{Addition,Deletion}Width` represent the width of the graphic allocated
- * to the insertion or deletion side respectively. Finally, the
- * `deletionOffset` value represents the x-position for the deletion bar.
- *
- * @typedef {{
- * maxInserted: number,
- * maxDeleted: number,
- * maxAdditionWidth: number,
- * maxDeletionWidth: number,
- * deletionOffset: number,
- * }}
- */
- Defs.LayoutStats;
-
Polymer({
is: 'gr-file-list',
- _legacyUndefinedCheck: true,
/**
* Fired when a draft refresh should get triggered
@@ -148,7 +127,7 @@
_shownFiles: {
type: Array,
- computed: '_computeFilesShown(numFilesShown, _files.*)',
+ computed: '_computeFilesShown(numFilesShown, _files)',
},
/**
@@ -166,7 +145,7 @@
type: Boolean,
observer: '_loadingChanged',
},
- /** @type {Defs.LayoutStats|undefined} */
+ /** @type {Gerrit.LayoutStats|undefined} */
_sizeBarLayout: {
type: Object,
computed: '_computeSizeBarLayout(_shownFiles.*)',
@@ -203,6 +182,7 @@
behaviors: [
Gerrit.AsyncForeachBehavior,
Gerrit.DomUtilBehavior,
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PatchSetBehavior,
Gerrit.PathListBehavior,
@@ -332,7 +312,6 @@
},
get diffs() {
- // Polymer2: querySelectorAll returns NodeList instead of Array.
return Array.from(
Polymer.dom(this.root).querySelectorAll('gr-diff-host'));
},
@@ -512,7 +491,7 @@
this.set(['_files', index, 'isReviewed'], reviewed);
if (index < this._shownFiles.length) {
- this.set(['_shownFiles', index, 'isReviewed'], reviewed);
+ this.notifyPath(`_shownFiles.${index}.isReviewed`);
}
this._saveReviewedState(path, reviewed);
@@ -562,7 +541,7 @@
* Handle all events from the file list dom-repeat so event handleers don't
* have to get registered for potentially very long lists.
*/
- _handleFileListTap(e) {
+ _handleFileListClick(e) {
// Traverse upwards to find the row element if the target is not the row.
let row = e.target;
while (!row.classList.contains('row') && row.parentElement) {
@@ -800,6 +779,11 @@
},
_computeDiffURL(change, patchNum, basePatchNum, path, editMode) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum, basePatchNum, path, editMode]
+ .some(arg => arg === undefined)) {
+ return;
+ }
// TODO(kaspern): Fix editing for commit messages and merge lists.
if (editMode && path !== this.COMMIT_MESSAGE_PATH &&
path !== this.MERGE_LIST_PATH) {
@@ -860,8 +844,19 @@
},
_computeFiles(filesByPath, changeComments, patchRange, reviewed, loading) {
+ // Polymer 2: check for undefined
+ if ([
+ filesByPath,
+ changeComments,
+ patchRange,
+ reviewed,
+ loading,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
// Await all promises resolving from reload. @See Issue 9057
- if (loading) { return; }
+ if (loading || !changeComments) { return; }
const commentedPaths = changeComments.getPaths(patchRange);
const files = Object.assign({}, filesByPath);
@@ -879,10 +874,15 @@
},
_computeFilesShown(numFilesShown, files) {
+ // Polymer 2: check for undefined
+ if ([numFilesShown, files].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const previousNumFilesShown = this._shownFiles ?
this._shownFiles.length : 0;
- const filesShown = files.base.slice(0, numFilesShown);
+ const filesShown = files.slice(0, numFilesShown);
this.fire('files-shown-changed', {length: filesShown.length});
// Start the timer for the rendering work hwere because this is where the
@@ -904,12 +904,13 @@
},
_filesChanged() {
- Polymer.dom.flush();
- // Polymer2: querySelectorAll returns NodeList instead of Array.
- const files = Array.from(
- Polymer.dom(this.root).querySelectorAll('.file-row'));
- this.$.fileCursor.stops = files;
- this.$.fileCursor.setCursorAtIndex(this.selectedIndex, true);
+ if (this._files && this._files.length > 0) {
+ Polymer.dom.flush();
+ const files = Array.from(
+ Polymer.dom(this.root).querySelectorAll('.file-row'));
+ this.$.fileCursor.stops = files;
+ this.$.fileCursor.setCursorAtIndex(this.selectedIndex, true);
+ }
},
_incrementNumFilesShown() {
@@ -947,6 +948,11 @@
},
_computePatchSetDescription(revisions, patchNum) {
+ // Polymer 2: check for undefined
+ if ([revisions, patchNum].some(arg => arg === undefined)) {
+ return '';
+ }
+
const rev = this.getRevisionByPatchNum(revisions, patchNum);
return (rev && rev.description) ?
rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
@@ -1178,7 +1184,8 @@
/**
* Compute size bar layout values from the file list.
*
- * @return {Defs.LayoutStats|undefined}
+ * @return {Gerrit.LayoutStats|undefined}
+ *
*/
_computeSizeBarLayout(shownFilesRecord) {
if (!shownFilesRecord || !shownFilesRecord.base) { return undefined; }
@@ -1214,7 +1221,7 @@
* Get the width of the addition bar for a file.
*
* @param {Object} file
- * @param {Defs.LayoutStats} stats
+ * @param {Gerrit.LayoutStats} stats
* @return {number}
*/
_computeBarAdditionWidth(file, stats) {
@@ -1232,7 +1239,7 @@
* Get the x-offset of the addition bar for a file.
*
* @param {Object} file
- * @param {Defs.LayoutStats} stats
+ * @param {Gerrit.LayoutStats} stats
* @return {number}
*/
_computeBarAdditionX(file, stats) {
@@ -1244,7 +1251,7 @@
* Get the width of the deletion bar for a file.
*
* @param {Object} file
- * @param {Defs.LayoutStats} stats
+ * @param {Gerrit.LayoutStats} stats
* @return {number}
*/
_computeBarDeletionWidth(file, stats) {
@@ -1261,7 +1268,8 @@
/**
* Get the x-offset of the deletion bar for a file.
*
- * @param {Defs.LayoutStats} stats
+ * @param {Gerrit.LayoutStats} stats
+ *
* @return {number}
*/
_computeBarDeletionX(stats) {
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
index fffac8e2e0..4c102a2c07 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-file-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<link rel="import" href="../../diff/gr-comment-api/gr-comment-api.html">
<script src="../../../scripts/util.js"></script>
@@ -815,18 +817,18 @@ limitations under the License.
assert.isTrue(commitReviewLabel.classList.contains('isReviewed'));
assert.equal(markReviewLabel.textContent, 'MARK UNREVIEWED');
- const tapSpy = sandbox.spy(element, '_handleFileListTap');
+ const clickSpy = sandbox.spy(element, '_handleFileListClick');
MockInteractions.tap(markReviewLabel);
assert.isTrue(saveStub.lastCall.calledWithExactly('/COMMIT_MSG', false));
assert.isFalse(commitReviewLabel.classList.contains('isReviewed'));
assert.equal(markReviewLabel.textContent, 'MARK REVIEWED');
- assert.isTrue(tapSpy.lastCall.args[0].defaultPrevented);
+ assert.isTrue(clickSpy.lastCall.args[0].defaultPrevented);
MockInteractions.tap(markReviewLabel);
assert.isTrue(saveStub.lastCall.calledWithExactly('/COMMIT_MSG', true));
assert.isTrue(commitReviewLabel.classList.contains('isReviewed'));
assert.equal(markReviewLabel.textContent, 'MARK UNREVIEWED');
- assert.isTrue(tapSpy.lastCall.args[0].defaultPrevented);
+ assert.isTrue(clickSpy.lastCall.args[0].defaultPrevented);
});
test('_computeFileStatusLabel', () => {
@@ -834,7 +836,7 @@ limitations under the License.
assert.equal(element._computeFileStatusLabel('M'), 'Modified');
});
- test('_handleFileListTap', () => {
+ test('_handleFileListClick', () => {
element._filesByPath = {
'/COMMIT_MSG': {},
'f1.txt': {},
@@ -846,7 +848,7 @@ limitations under the License.
patchNum: '2',
};
- const tapSpy = sandbox.spy(element, '_handleFileListTap');
+ const clickSpy = sandbox.spy(element, '_handleFileListClick');
const reviewStub = sandbox.stub(element, '_reviewFile');
const toggleExpandSpy = sandbox.spy(element, '_togglePathExpanded');
@@ -856,26 +858,26 @@ limitations under the License.
// Click on the expand button, resulting in _togglePathExpanded being
// called and not resulting in a call to _reviewFile.
row.querySelector('div.show-hide').click();
- assert.isTrue(tapSpy.calledOnce);
+ assert.isTrue(clickSpy.calledOnce);
assert.isTrue(toggleExpandSpy.calledOnce);
assert.isFalse(reviewStub.called);
// Click inside the diff. This should result in no additional calls to
// _togglePathExpanded or _reviewFile.
Polymer.dom(element.root).querySelector('gr-diff-host').click();
- assert.isTrue(tapSpy.calledTwice);
+ assert.isTrue(clickSpy.calledTwice);
assert.isTrue(toggleExpandSpy.calledOnce);
assert.isFalse(reviewStub.called);
// Click the reviewed checkbox, resulting in a call to _reviewFile, but
// no additional call to _togglePathExpanded.
row.querySelector('.markReviewed').click();
- assert.isTrue(tapSpy.calledThrice);
+ assert.isTrue(clickSpy.calledThrice);
assert.isTrue(toggleExpandSpy.calledOnce);
assert.isTrue(reviewStub.calledOnce);
});
- test('_handleFileListTap editMode', () => {
+ test('_handleFileListClick editMode', () => {
element._filesByPath = {
'/COMMIT_MSG': {},
'f1.txt': {},
@@ -888,12 +890,12 @@ limitations under the License.
};
element.editMode = true;
flushAsynchronousOperations();
- const tapSpy = sandbox.spy(element, '_handleFileListTap');
+ const clickSpy = sandbox.spy(element, '_handleFileListClick');
const toggleExpandSpy = sandbox.spy(element, '_togglePathExpanded');
- // Tap the edit controls. Should be ignored by _handleFileListTap.
+ // Tap the edit controls. Should be ignored by _handleFileListClick.
MockInteractions.tap(element.$$('.editFileControls'));
- assert.isTrue(tapSpy.calledOnce);
+ assert.isTrue(clickSpy.calledOnce);
assert.isFalse(toggleExpandSpy.called);
});
@@ -1211,22 +1213,6 @@ limitations under the License.
assert.isFalse(element.classList.contains('loading'));
});
- test('no execute _computeDiffURL before patchNum is knwon', done => {
- const urlStub = sandbox.stub(element, '_computeDiffURL');
- element.change = {_number: 123};
- element.patchRange = {patchNum: undefined, basePatchNum: 'PARENT'};
- element._filesByPath = {'foo/bar.cpp': {}};
- element.editMode = false;
- flush(() => {
- assert.isFalse(urlStub.called);
- element.set('patchRange.patchNum', 4);
- flush(() => {
- assert.isTrue(urlStub.called);
- done();
- });
- });
- });
-
suite('size bars', () => {
test('_computeSizeBarLayout', () => {
assert.isUndefined(element._computeSizeBarLayout(null));
@@ -1713,7 +1699,9 @@ limitations under the License.
// Commit message should not have edit controls.
const editControls =
- Polymer.dom(element.root).querySelectorAll('.row:not(.header-row)')
+ Array.from(
+ Polymer.dom(element.root)
+ .querySelectorAll('.row:not(.header-row)'))
.map(row => row.querySelector('gr-edit-file-controls'));
assert.isTrue(editControls[0].classList.contains('invisible'));
});
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html
index b824f1c249..0afd214640 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.html
@@ -15,8 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-included-in-dialog">
@@ -27,44 +30,41 @@ limitations under the License.
display: block;
max-height: 80vh;
overflow-y: auto;
- padding: 4.5em 1em 1em 1em;
+ padding: 4.5em var(--spacing-l) var(--spacing-l) var(--spacing-l);
}
header {
background-color: var(--dialog-background-color);
border-bottom: 1px solid var(--border-color);
left: 0;
- padding: 1em;
+ padding: var(--spacing-l);
position: absolute;
right: 0;
top: 0;
}
#title {
display: inline-block;
- font-size: 1.2rem;
- margin-top: .2em;
- }
- h2 {
- font-size: 1rem;
+ font-size: var(--font-size-h3);
+ margin-top: var(--spacing-xs);
}
#filterInput {
display: inline-block;
float: right;
- margin: 0 1em;
- padding: .2em;
+ margin: 0 var(--spacing-l);
+ padding: var(--spacing-xs);
}
.closeButtonContainer {
float: right;
}
ul {
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
ul li {
border: 1px solid var(--border-color);
- border-radius: .2em;
+ border-radius: var(--border-radius);
background: var(--chip-background-color);
display: inline-block;
- margin: 0 .2em .4em .2em;
- padding: .2em .4em;
+ margin: 0 var(--spacing-xs) var(--spacing-s) var(--spacing-xs);
+ padding: var(--spacing-xs) var(--spacing-s);
}
.loading.loaded {
display: none;
@@ -75,13 +75,19 @@ limitations under the License.
<span class="closeButtonContainer">
<gr-button id="closeButton"
link
- on-tap="_handleCloseTap">Close</gr-button>
+ on-click="_handleCloseTap">Close</gr-button>
</span>
- <input
- id="filterInput"
+ <iron-input
+ id="filterInput"
+ placeholder="Filter"
+ bind-value="{{_filterText}}"
+ >
+ <input
is="iron-input"
placeholder="Filter"
- on-bind-value-changed="_onFilterChanged">
+ bind-value="{{_filterText}}"
+ />
+ </iron-input>
</header>
<div class$="[[_computeLoadingClass(_loaded)]]">Loading...</div>
<template
@@ -89,7 +95,7 @@ limitations under the License.
items="[[_computeGroups(_includedIn, _filterText)]]"
as="group">
<div>
- <h2>[[group.title]]:</h2>
+ <span>[[group.title]]:</span>
<ul>
<template is="dom-repeat" items="[[group.items]]">
<li>[[item]]</li>
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
index 7755a60099..9d9f654ca8 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-included-in-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the user presses the close button.
@@ -45,6 +44,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
loadData() {
if (!this.changeNum) { return; }
this._filterText = '';
@@ -62,7 +65,9 @@
},
_computeGroups(includedIn, filterText) {
- if (!includedIn) { return []; }
+ if (!includedIn || filterText === undefined) {
+ return [];
+ }
const filter = item => !filterText.length ||
item.toLowerCase().indexOf(filterText.toLowerCase()) !== -1;
@@ -84,17 +89,12 @@
_handleCloseTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('close', null, {bubbles: false});
},
_computeLoadingClass(loaded) {
return loaded ? 'loading loaded' : 'loading';
},
-
- _onFilterChanged() {
- this.debounce('filter-change', () => {
- this._filterText = this.$.filterInput.bindValue;
- }, 100);
- },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html
index 539011a6b4..ad2ad5a171 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-included-in-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-included-in-dialog.html">
@@ -80,5 +82,20 @@ limitations under the License.
{title: 'Tags', items: ['v2.0', 'v2.1']},
]);
});
+
+ test('_computeGroups with .bindValue', done => {
+ element.$.filterInput.bindValue = 'stable-3.2';
+ const includedIn = {branches: [], tags: []};
+ includedIn.branches.push('master', 'stable-3.2');
+
+ setTimeout(() => {
+ const filterText = element._filterText;
+ assert.deepEqual(element._computeGroups(includedIn, filterText), [
+ {title: 'Branches', items: ['stable-3.2']},
+ ]);
+
+ done();
+ });
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html
index 27c5baa969..220546b3ad 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-selector/iron-selector.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-selector/iron-selector.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../../styles/gr-voting-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -28,12 +28,12 @@ limitations under the License.
.labelContainer {
align-items: center;
display: flex;
- margin-bottom: .5em;
+ margin-bottom: var(--spacing-m);
}
.labelName {
display: inline-block;
flex: 0 0 auto;
- margin-right: .5em;
+ margin-right: var(--spacing-m);
min-width: 7em;
text-align: left;
width: 20%;
@@ -47,7 +47,7 @@ limitations under the License.
.selectedValueText {
color: var(--deemphasized-text-color);
font-style: italic;
- margin: 0 .5em 0 .5em;
+ margin: 0 var(--spacing-m);
}
.selectedValueText.hidden {
display: none;
@@ -60,7 +60,7 @@ limitations under the License.
--gr-button: {
background-color: var(--button-background-color, var(--table-header-background-color));
color: var(--primary-text-color);
- padding: .2em .85em;
+ padding: var(--spacing-xs) var(--spacing-m);
@apply --vote-chip-styles;
}
}
@@ -108,6 +108,7 @@ limitations under the License.
<span class="placeholder" data-label$="[[label.name]]"></span>
</template>
<iron-selector
+ id="labelSelector"
attr-for-selected="value"
selected="[[_computeLabelValue(labels, permittedLabels, label)]]"
hidden$="[[!_computeAnyPermittedLabelValues(permittedLabels, label.name)]]"
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
index 9b5847d7f1..76e6e64781 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-label-score-row',
- _legacyUndefinedCheck: true,
/**
* Fired when any label is changed.
@@ -66,7 +65,7 @@
},
get _ironSelector() {
- return this.$$('iron-selector');
+ return this.$ && this.$.labelSelector;
},
_computeBlankItems(permittedLabels, label, side) {
@@ -97,19 +96,30 @@
},
_computeButtonClass(value, index, totalItems) {
+ const classes = [];
+ if (value === this.selectedValue) {
+ classes.push('iron-selected');
+ }
+
if (value < 0 && index === 0) {
- return 'min';
+ classes.push('min');
} else if (value < 0) {
- return 'negative';
+ classes.push('negative');
} else if (value > 0 && index === totalItems - 1) {
- return 'max';
+ classes.push('max');
} else if (value > 0) {
- return 'positive';
+ classes.push('positive');
+ } else {
+ classes.push('neutral');
}
- return 'neutral';
+
+ return classes.join(' ');
},
_computeLabelValue(labels, permittedLabels, label) {
+ if ([labels, permittedLabels, label].some(arg => arg === undefined)) {
+ return null;
+ }
if (!labels[label.name]) { return null; }
const labelValue = this._getLabelValue(labels, permittedLabels, label);
const len = permittedLabels[label.name] != null ?
@@ -133,11 +143,12 @@
const name = e.target.selectedItem.name;
const value = e.target.selectedItem.getAttribute('value');
this.dispatchEvent(new CustomEvent(
- 'labels-changed', {detail: {name, value}, bubbles: true}));
+ 'labels-changed',
+ {detail: {name, value}, bubbles: true, composed: true}));
},
_computeAnyPermittedLabelValues(permittedLabels, label) {
- return permittedLabels.hasOwnProperty(label) &&
+ return permittedLabels && permittedLabels.hasOwnProperty(label) &&
permittedLabels[label].length;
},
@@ -147,6 +158,11 @@
},
_computePermittedLabelValues(permittedLabels, label) {
+ // Polymer 2: check for undefined
+ if ([permittedLabels, label].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
return permittedLabels[label];
},
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html
index 1e4d471a5f..519fbb80be 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-label-score-row</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-label-score-row.html">
@@ -106,7 +108,7 @@ limitations under the License.
test('label picker', () => {
const labelsChangedHandler = sandbox.stub();
element.addEventListener('labels-changed', labelsChangedHandler);
- assert.ok(element.$$('iron-selector'));
+ assert.ok(element.$.labelSelector);
MockInteractions.tap(element.$$(
'gr-button[value="-1"]'));
flushAsynchronousOperations();
@@ -155,9 +157,9 @@ limitations under the License.
test('correct item is selected', () => {
// 1 should be the value of the selected item
- assert.strictEqual(element.$$('iron-selector').selected, '+1');
+ assert.strictEqual(element.$.labelSelector.selected, '+1');
assert.strictEqual(
- element.$$('iron-selector').selectedItem
+ element.$.labelSelector.selectedItem
.textContent.trim(), '+1');
assert.strictEqual(
element.$.selectedValueLabel.textContent.trim(), 'good');
@@ -234,7 +236,7 @@ limitations under the License.
default_value: 0,
},
};
- const selector = element.$$('iron-selector');
+ const selector = element.$.labelSelector;
element.set('label', {name: 'Verified', value: ' 0'});
flushAsynchronousOperations();
assert.strictEqual(selector.selected, ' 0');
@@ -251,21 +253,21 @@ limitations under the License.
],
};
flushAsynchronousOperations();
- assert.isOk(element.$$('iron-selector'));
- assert.isFalse(element.$$('iron-selector').hidden);
+ assert.isOk(element.$.labelSelector);
+ assert.isFalse(element.$.labelSelector.hidden);
element.permittedLabels = {};
flushAsynchronousOperations();
- assert.isOk(element.$$('iron-selector'));
- assert.isTrue(element.$$('iron-selector').hidden);
+ assert.isOk(element.$.labelSelector);
+ assert.isTrue(element.$.labelSelector.hidden);
element.permittedLabels = {Verified: []};
flushAsynchronousOperations();
- assert.isOk(element.$$('iron-selector'));
- assert.isTrue(element.$$('iron-selector').hidden);
+ assert.isOk(element.$.labelSelector);
+ assert.isTrue(element.$.labelSelector.hidden);
});
- test('asymetrical labels', () => {
+ test('asymetrical labels', done => {
element.permittedLabels = {
'Code-Review': [
'-2',
@@ -279,30 +281,35 @@ limitations under the License.
'+1',
],
};
- flushAsynchronousOperations();
- assert.strictEqual(element.$$('iron-selector')
- .items.length, 2);
- assert.strictEqual(Polymer.dom(element.root).
- querySelectorAll('.placeholder').length, 3);
-
- element.permittedLabels = {
- 'Code-Review': [
- ' 0',
- '+1',
- ],
- 'Verified': [
- '-2',
- '-1',
- ' 0',
- '+1',
- '+2',
- ],
- };
- flushAsynchronousOperations();
- assert.strictEqual(element.$$('iron-selector')
- .items.length, 5);
- assert.strictEqual(Polymer.dom(element.root).
- querySelectorAll('.placeholder').length, 0);
+ flush(() => {
+ assert.strictEqual(element.$.labelSelector
+ .items.length, 2);
+ assert.strictEqual(
+ Polymer.dom(element.root).querySelectorAll('.placeholder').length,
+ 3);
+
+ element.permittedLabels = {
+ 'Code-Review': [
+ ' 0',
+ '+1',
+ ],
+ 'Verified': [
+ '-2',
+ '-1',
+ ' 0',
+ '+1',
+ '+2',
+ ],
+ };
+ flush(() => {
+ assert.strictEqual(element.$.labelSelector
+ .items.length, 5);
+ assert.strictEqual(
+ Polymer.dom(element.root).querySelectorAll('.placeholder').length,
+ 0);
+ done();
+ });
+ });
});
test('default_value', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.html b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.html
index 7dd4c76cc8..c607a9f899 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.html
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../gr-label-score-row/gr-label-score-row.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -28,6 +28,9 @@ limitations under the License.
text-align: center;
width: 100%;
}
+ gr-label-score-row.no-access {
+ display: var(--label-no-access-display, initial);
+ }
@media only screen and (max-width: 25em) {
:host {
text-align: center;
@@ -36,6 +39,7 @@ limitations under the License.
</style>
<template is="dom-repeat" items="[[_labels]]" as="label">
<gr-label-score-row
+ class$="[[_computeLabelAccessClass(label.name, permittedLabels)]]"
label="[[label]]"
name="[[label.name]]"
labels="[[change.labels]]"
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
index eaf39bc4a7..dffba3e60d 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-label-scores',
- _legacyUndefinedCheck: true,
+
properties: {
_labels: {
type: Array,
@@ -82,7 +82,12 @@
return null;
},
- _computeLabels(labelRecord) {
+ _computeLabels(labelRecord, account) {
+ // Polymer 2: check for undefined
+ if ([labelRecord, account].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const labelsObj = labelRecord.base;
if (!labelsObj) { return []; }
return Object.keys(labelsObj).sort().map(key => {
@@ -115,5 +120,19 @@
_changeIsMerged(changeStatus) {
return changeStatus === 'MERGED';
},
+
+ /**
+ * @param {string|undefined} label
+ * @param {Object|undefined} permittedLabels
+ * @return {string}
+ */
+ _computeLabelAccessClass(label, permittedLabels) {
+ if (label == null || permittedLabels == null) {
+ return '';
+ }
+
+ return permittedLabels.hasOwnProperty(label) &&
+ permittedLabels[label].length ? 'access' : 'no-access';
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
index 187c0a6340..b8d471cc29 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-label-scores</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-label-scores.html">
@@ -135,6 +137,25 @@ limitations under the License.
});
});
+ test('_computeLabelAccessClass undefined case', () => {
+ assert.strictEqual(
+ element._computeLabelAccessClass(undefined, undefined), '');
+ assert.strictEqual(
+ element._computeLabelAccessClass('', undefined), '');
+ assert.strictEqual(
+ element._computeLabelAccessClass(undefined, {}), '');
+ });
+
+ test('_computeLabelAccessClass has access', () => {
+ assert.strictEqual(
+ element._computeLabelAccessClass('foo', {foo: ['']}), 'access');
+ });
+
+ test('_computeLabelAccessClass no access', () => {
+ assert.strictEqual(
+ element._computeLabelAccessClass('zap', {foo: ['']}), 'no-access');
+ });
+
test('changes in label score are reflected in _labels', () => {
element.change = {
_number: '123',
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.html b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
index d3a46feec2..8317e2fa71 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -15,9 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-icon/iron-icon.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-icon/iron-icon.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../shared/gr-account-label/gr-account-label.html">
+<link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
<link rel="import" href="../../shared/gr-formatted-text/gr-formatted-text.html">
@@ -36,16 +38,17 @@ limitations under the License.
display: block;
position: relative;
cursor: pointer;
+ overflow-y: hidden;
}
:host(.expanded) {
cursor: auto;
}
:host > div {
- padding: 0 var(--default-horizontal-margin);
+ padding: 0 var(--spacing-l);
}
gr-avatar {
position: absolute;
- left: var(--default-horizontal-margin);
+ left: var(--spacing-l);
}
.collapsed .contentContainer {
align-items: baseline;
@@ -54,11 +57,11 @@ limitations under the License.
white-space: nowrap;
}
.contentContainer {
- margin-left: calc(var(--default-horizontal-margin) + 2.5em);
- padding: 10px 0;
+ margin-left: calc(var(--spacing-l) + 2.5em);
+ padding: var(--spacing-m) 0;
}
.showAvatar.collapsed .contentContainer {
- margin-left: calc(var(--default-horizontal-margin) + 1.75em);
+ margin-left: calc(var(--spacing-l) + 1.75em);
}
.hideAvatar.collapsed .contentContainer,
.hideAvatar.expanded .contentContainer {
@@ -67,17 +70,17 @@ limitations under the License.
.showAvatar.collapsed .contentContainer,
.hideAvatar.collapsed .contentContainer,
.hideAvatar.expanded .contentContainer {
- padding: .75em 0;
+ padding: var(--spacing-m) 0;
}
.collapsed gr-avatar {
- top: .5em;
- height: 1.75em;
- width: 1.75em;
+ top: var(--spacing-m);
+ height: var(--line-height-normal);
+ width: var(--line-height-normal);
}
.expanded gr-avatar {
- top: 12px;
- height: 2.5em;
- width: 2.5em;
+ top: var(--spacing-l);
+ height: var(--line-height-h1);
+ width: var(--line-height-h1);
}
.name {
font-weight: var(--font-weight-bold);
@@ -111,7 +114,7 @@ limitations under the License.
}
.collapsed .content {
flex: 1;
- margin-right: .25em;
+ margin-right: var(--spacing-xs);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
@@ -122,15 +125,15 @@ limitations under the License.
.collapsed .author {
overflow: hidden;
color: var(--primary-text-color);
- margin-right: .4em;
+ margin-right: var(--spacing-s);
}
.expanded .author {
cursor: pointer;
- margin-bottom: .4em;
+ margin-bottom: var(--spacing-s);
}
.dateContainer {
position: absolute;
- right: var(--default-horizontal-margin);
+ right: var(--spacing-l);
top: 10px;
}
span.date {
@@ -141,17 +144,18 @@ limitations under the License.
}
.dateContainer iron-icon {
cursor: pointer;
+ vertical-align: top;
}
.replyContainer {
- padding: .5em 0 0 0;
+ padding: var(--spacing-m) 0 0 0;
}
.score {
border: 1px solid rgba(0,0,0,.12);
- border-radius: 3px;
+ border-radius: var(--border-radius);
color: var(--primary-text-color);
display: inline-block;
- margin: -.1em 0;
- padding: 0 .1em;
+ margin: -1px 0;
+ padding: 0 var(--spacing-xxs);
}
.score.negative {
background-color: var(--vote-color-disliked);
@@ -174,7 +178,7 @@ limitations under the License.
<div class$="[[_computeClass(_expanded, showAvatar, message)]]">
<gr-avatar account="[[author]]" image-size="100"></gr-avatar>
<div class="contentContainer">
- <div class="author" on-tap="_handleAuthorTap">
+ <div class="author" on-click="_handleAuthorClick">
<span hidden$="[[!showOnBehalfOf]]">
<span class="name">[[message.real_author.name]]</span>
on behalf of
@@ -198,7 +202,7 @@ limitations under the License.
config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
<template is="dom-if" if="[[_expanded]]">
<div class="replyContainer" hidden$="[[!showReplyButton]]" hidden>
- <gr-button link small on-tap="_handleReplyTap">Reply</gr-button>
+ <gr-button link small on-click="_handleReplyTap">Reply</gr-button>
</div>
<gr-comment-list
comments="[[comments]]"
@@ -233,7 +237,7 @@ limitations under the License.
</span>
</template>
<template is="dom-if" if="[[message.id]]">
- <span class="date" on-tap="_handleAnchorTap">
+ <span class="date" on-click="_handleAnchorClick">
<gr-date-formatter
has-tooltip
show-date-and-time
@@ -242,7 +246,7 @@ limitations under the License.
</template>
<iron-icon
id="expandToggle"
- on-tap="_toggleExpanded"
+ on-click="_toggleExpanded"
title="Toggle expanded state"
icon="[[_computeExpandToggleIcon(_expanded)]]">
</span>
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.js b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
index 7be1c5d5f6..06bbc93b8c 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-message',
- _legacyUndefinedCheck: true,
/**
* Fired when this message's reply link is tapped.
@@ -37,7 +36,7 @@
*/
listeners: {
- tap: '_handleTap',
+ click: '_handleClick',
},
properties: {
@@ -103,6 +102,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
observers: [
'_updateExpandedClass(message.expanded)',
],
@@ -139,7 +142,7 @@
},
_computeShowReplyButton(message, loggedIn) {
- return !!message.message && loggedIn &&
+ return message && !!message.message && loggedIn &&
!this._computeIsAutomated(message);
},
@@ -147,13 +150,13 @@
return expanded;
},
- _handleTap(e) {
+ _handleClick(e) {
if (this.message.expanded) { return; }
e.stopPropagation();
this.set('message.expanded', true);
},
- _handleAuthorTap(e) {
+ _handleAuthorClick(e) {
if (!this.message.expanded) { return; }
e.stopPropagation();
this.set('message.expanded', false);
@@ -187,6 +190,10 @@
},
_computeScoreClass(score, labelExtremes) {
+ // Polymer 2: check for undefined
+ if ([score, labelExtremes].some(arg => arg === undefined)) {
+ return '';
+ }
const classes = [];
if (score.value > 0) {
classes.push('positive');
@@ -212,10 +219,11 @@
return classes.join(' ');
},
- _handleAnchorTap(e) {
+ _handleAnchorClick(e) {
e.preventDefault();
this.dispatchEvent(new CustomEvent('message-anchor-tap', {
bubbles: true,
+ composed: true,
detail: {id: this.message.id},
}));
},
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
index 566442963e..a90e3ab7f2 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-message</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-message.html">
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
index 80708a1d33..d71beb4d81 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../gr-message/gr-message.html">
@@ -38,10 +38,10 @@ limitations under the License.
display: flex;
justify-content: space-between;
min-height: 3.2em;
- padding: .5em var(--default-horizontal-margin);
+ padding: var(--spacing-s) var(--spacing-l);
}
#messageControlsContainer {
- padding: 0 var(--default-horizontal-margin);
+ padding: 0 var(--spacing-l);
}
.highlighted {
animation: 3s fadeOut;
@@ -58,7 +58,7 @@ limitations under the License.
justify-content: center;
}
#messageControlsContainer gr-button {
- padding: 0.4em 0;
+ padding: var(--spacing-s) 0;
}
.container {
align-items: center;
@@ -78,14 +78,14 @@ limitations under the License.
<gr-button
id="collapse-messages"
link
- on-tap="_handleExpandCollapseTap">
+ on-click="_handleExpandCollapseTap">
[[_computeExpandCollapseMessage(_expanded)]]
</gr-button>
</div>
<span
id="messageControlsContainer"
hidden$="[[_computeShowHideTextHidden(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]">
- <gr-button id="oldMessagesBtn" link on-tap="_handleShowAllTap">
+ <gr-button id="oldMessagesBtn" link on-click="_handleShowAllTap">
[[_computeNumMessagesText(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]
</gr-button>
<span
@@ -93,7 +93,7 @@ limitations under the License.
hidden$="[[_computeIncrementHidden(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]">
<span class="transparent separator"></span>
<gr-button id="incrementMessagesBtn" link
- on-tap="_handleIncrementShownMessages">
+ on-click="_handleIncrementShownMessages">
[[_computeIncrementText(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]
</gr-button>
</span>
@@ -109,7 +109,7 @@ limitations under the License.
hide-automated="[[_hideAutomated]]"
project-name="[[projectName]]"
show-reply-button="[[showReplyButtons]]"
- on-message-anchor-tap="_handleAnchorTap"
+ on-message-anchor-tap="_handleAnchorClick"
label-extremes="[[_labelExtremes]]"
data-message-id$="[[message.id]]"></gr-message>
</template>
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
index d1cd08d428..5652eca4f1 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
@@ -27,7 +27,6 @@
Polymer({
is: 'gr-messages-list',
- _legacyUndefinedCheck: true,
properties: {
changeNum: Number,
@@ -117,6 +116,11 @@
},
_computeItems(messages, reviewerUpdates) {
+ // Polymer 2: check for undefined
+ if ([messages, reviewerUpdates].some(arg => arg === undefined)) {
+ return [];
+ }
+
messages = messages || [];
reviewerUpdates = reviewerUpdates || [];
let mi = 0;
@@ -156,10 +160,18 @@
},
_expandedChanged(exp) {
- for (let i = 0; i < this._processedMessages.length; i++) {
- this._processedMessages[i].expanded = exp;
- if (i < this._visibleMessages.length) {
- this.set(['_visibleMessages', i, 'expanded'], exp);
+ if (this._processedMessages) {
+ for (let i = 0; i < this._processedMessages.length; i++) {
+ this._processedMessages[i].expanded = exp;
+ }
+ }
+ // _visibleMessages is a subarray of _processedMessages
+ // _processedMessages contains all items from _visibleMessages
+ // At this point all _visibleMessages.expanded values are set,
+ // and notifyPath must be used to notify Polymer about changes.
+ if (this._visibleMessages) {
+ for (let i = 0; i < this._visibleMessages.length; i++) {
+ this.notifyPath(`_visibleMessages.${i}.expanded`);
}
}
},
@@ -190,7 +202,7 @@
this.handleExpandCollapse(!this._expanded);
},
- _handleAnchorTap(e) {
+ _handleAnchorClick(e) {
this.scrollToMessage(e.detail.id);
},
@@ -220,6 +232,9 @@
* @return {!Object} Hash of arrays of comments, filename as key.
*/
_computeCommentsForMessage(changeComments, message) {
+ if ([changeComments, message].some(arg => arg === undefined)) {
+ return [];
+ }
const comments = changeComments.getAllPublishedComments();
if (message._index === undefined || !comments || !this.messages) {
return [];
@@ -268,8 +283,13 @@
* more visible messages in the list.
*/
_getDelta(visibleMessages, messages, hideAutomated) {
+ if ([visibleMessages, messages].some(arg => arg === undefined)) {
+ return 0;
+ }
+
let delta = MESSAGES_INCREMENT;
const msgsRemaining = messages.length - visibleMessages.length;
+
if (hideAutomated) {
let counter = 0;
let i;
@@ -286,6 +306,10 @@
* exist in _visibleMessages.
*/
_numRemaining(visibleMessages, messages, hideAutomated) {
+ if ([visibleMessages, messages].some(arg => arg === undefined)) {
+ return 0;
+ }
+
if (hideAutomated) {
return this._getHumanMessages(messages).length -
this._getHumanMessages(visibleMessages).length;
@@ -308,6 +332,10 @@
_computeShowHideTextHidden(visibleMessages, messages,
hideAutomated) {
+ if ([visibleMessages, messages].some(arg => arg === undefined)) {
+ return 0;
+ }
+
if (hideAutomated) {
messages = this._getHumanMessages(messages);
visibleMessages = this._getHumanMessages(visibleMessages);
@@ -331,7 +359,9 @@
},
_processedMessagesChanged(messages) {
- this._visibleMessages = messages.slice(-MAX_INITIAL_SHOWN_MESSAGES);
+ if (messages) {
+ this._visibleMessages = messages.slice(-MAX_INITIAL_SHOWN_MESSAGES);
+ }
},
_computeNumMessagesText(visibleMessages, messages,
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
index 9572de4646..315403ea32 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-messages-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../diff/gr-comment-api/gr-comment-api.html">
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
index 30ebc08d26..696ffdfaae 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.html
@@ -14,8 +14,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
@@ -29,7 +30,7 @@ limitations under the License.
display: block;
}
h3 {
- margin: .5em 0 0;
+ margin: var(--spacing-m) 0 0;
}
section {
margin-bottom: 1.4em; /* Same as line height for collapse purposes */
@@ -74,7 +75,7 @@ limitations under the License.
.status {
color: var(--deemphasized-text-color);
font-weight: var(--font-weight-bold);
- margin-left: .25em;
+ margin-left: var(--spacing-xs);
}
.notCurrent {
color: #e65100;
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
index 07f6e20d0d..9103f4fc83 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-related-changes-list',
- _legacyUndefinedCheck: true,
/**
* Fired when a new section is loaded so that the change view can determine
@@ -78,6 +77,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.PatchSetBehavior,
Gerrit.RESTClientBehavior,
],
@@ -121,7 +121,7 @@
];
// Get conflicts if change is open and is mergeable.
- if (this.changeIsOpen(this.change.status) && this.mergeable) {
+ if (this.changeIsOpen(this.change) && this.mergeable) {
promises.push(this._getConflicts().then(response => {
// Because the server doesn't always return a response and the
// template expects an array, always return an array.
@@ -208,6 +208,9 @@
_computeChangeContainerClass(currentChange, relatedChange) {
const classes = ['changeContainer'];
+ if ([relatedChange, currentChange].some(arg => arg === undefined)) {
+ return classes;
+ }
if (this._changesEqual(relatedChange, currentChange)) {
classes.push('thisChange');
}
@@ -244,6 +247,9 @@
* @return {number}
*/
_getChangeNumber(change) {
+ // Default to 0 if change property is not defined.
+ if (!change) return 0;
+
if (change.hasOwnProperty('_change_number')) {
return change._change_number;
}
@@ -294,6 +300,17 @@
_resultsChanged(related, submittedTogether, conflicts,
cherryPicks, sameTopic) {
+ // Polymer 2: check for undefined
+ if ([
+ related,
+ submittedTogether,
+ conflicts,
+ cherryPicks,
+ sameTopic,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
const results = [
related && related.changes,
submittedTogether && submittedTogether.changes,
@@ -316,8 +333,14 @@
},
_computeConnectedRevisions(change, patchNum, relatedChanges) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum, relatedChanges].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const connected = [];
let changeRevision;
+ if (!change) { return []; }
for (const rev in change.revisions) {
if (this.patchNumEquals(change.revisions[rev]._number, patchNum)) {
changeRevision = rev;
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
index c82bc31483..f04d40e3bc 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-related-changes-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-related-changes-list.html">
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
index 692cb93866..3632348f76 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-reply-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../plugins/gr-plugin-host/gr-plugin-host.html">
@@ -118,18 +120,18 @@ limitations under the License.
// resolving.
sandbox.stub(element, '_purgeReviewersPendingRemove');
- element.$$('#ccs').$.entry.setText('test');
+ element.$.ccs.$.entry.setText('test');
MockInteractions.tap(element.$$('gr-button.send'));
assert.isFalse(sendStub.called);
flushAsynchronousOperations();
- element.$$('#ccs').$.entry.setText('test@test.test');
+ element.$.ccs.$.entry.setText('test@test.test');
MockInteractions.tap(element.$$('gr-button.send'));
assert.isTrue(sendStub.called);
});
test('lgtm plugin', done => {
- Gerrit._resetPlugins();
+ Gerrit._testOnly_resetPlugins();
const pluginHost = fixture('plugin-host');
pluginHost.config = {
plugin: {
@@ -149,7 +151,8 @@ limitations under the License.
flush(() => {
const textarea = element.$.textarea.getNativeTextarea();
textarea.value = 'LGTM';
- textarea.dispatchEvent(new CustomEvent('input', {bubbles: true}));
+ textarea.dispatchEvent(new CustomEvent(
+ 'input', {bubbles: true, composed: true}));
const labelScoreRows = Polymer.dom(element.$.labelScores.root)
.querySelector('gr-label-score-row[name="Code-Review"]');
const selectedBtn = Polymer.dom(labelScoreRows.root)
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index f8d43cb3ea..e836cccccf 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -15,12 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
<link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
@@ -31,9 +32,12 @@ limitations under the License.
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-storage/gr-storage.html">
-<link rel="import" href="../gr-account-list/gr-account-list.html">
+<link rel="import" href="../../shared/gr-account-list/gr-account-list.html">
<link rel="import" href="../gr-label-scores/gr-label-scores.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../change/gr-comment-list/gr-comment-list.html">
+<script src="../../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js"></script>
<dom-module id="gr-reply-dialog">
<template>
@@ -57,7 +61,7 @@ limitations under the License.
section {
border-top: 1px solid var(--border-color);
flex-shrink: 0;
- padding: .5em 1.5em;
+ padding: var(--spacing-m) var(--spacing-xl);
width: 100%;
}
.actions {
@@ -70,7 +74,7 @@ limitations under the License.
z-index: 1;
}
.actions .right gr-button {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
.peopleContainer,
.labelsContainer {
@@ -82,13 +86,13 @@ limitations under the License.
}
.peopleList {
display: flex;
- padding-top: .1em;
+ padding-top: var(--spacing-xxs);
}
.peopleListLabel {
color: var(--deemphasized-text-color);
- margin-top: .2em;
+ margin-top: var(--spacing-xs);
min-width: 7em;
- padding-right: .5em;
+ padding-right: var(--spacing-m);
}
gr-account-list {
display: flex;
@@ -97,11 +101,11 @@ limitations under the License.
min-height: 1.8em;
}
#reviewerConfirmationOverlay {
- padding: 1em;
+ padding: var(--spacing-l);
text-align: center;
}
.reviewerConfirmationButtons {
- margin-top: 1em;
+ margin-top: var(--spacing-l);
}
.groupName {
font-weight: var(--font-weight-bold);
@@ -124,14 +128,14 @@ limitations under the License.
}
.previewContainer gr-formatted-text {
background: var(--table-header-background-color);
- padding: 1em;
+ padding: var(--spacing-l);
}
.draftsContainer h3 {
- margin-top: .25em;
+ margin-top: var(--spacing-xs);
}
#checkingStatusLabel,
#notLatestLabel {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
#checkingStatusLabel {
color: var(--deemphasized-text-color);
@@ -149,8 +153,8 @@ limitations under the License.
}
#pluginMessage {
color: var(--deemphasized-text-color);
- margin-left: 1em;
- margin-bottom: .5em;
+ margin-left: var(--spacing-l);
+ margin-bottom: var(--spacing-m);
}
#pluginMessage:empty {
display: none;
@@ -164,11 +168,11 @@ limitations under the License.
id="reviewers"
accounts="{{_reviewers}}"
removable-values="[[change.removable_reviewers]]"
- change="[[change]]"
filter="[[filterReviewerSuggestion]]"
pending-confirmation="{{_reviewerPendingConfirmation}}"
placeholder="Add reviewer..."
- on-account-text-changed="_handleAccountTextEntry">
+ on-account-text-changed="_handleAccountTextEntry"
+ suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
</gr-account-list>
</div>
<div class="peopleList">
@@ -176,12 +180,12 @@ limitations under the License.
<gr-account-list
id="ccs"
accounts="{{_ccs}}"
- change="[[change]]"
filter="[[filterCCSuggestion]]"
pending-confirmation="{{_ccPendingConfirmation}}"
allow-any-input
placeholder="Add CC..."
- on-account-text-changed="_handleAccountTextEntry">
+ on-account-text-changed="_handleAccountTextEntry"
+ suggestions-provider="[[_getCcSuggestionsProvider(change)]]">
</gr-account-list>
</div>
<gr-overlay
@@ -201,8 +205,8 @@ limitations under the License.
Are you sure you want to add them all?
</div>
<div class="reviewerConfirmationButtons">
- <gr-button on-tap="_confirmPendingReviewer">Yes</gr-button>
- <gr-button on-tap="_cancelPendingReviewer">No</gr-button>
+ <gr-button on-click="_confirmPendingReviewer">Yes</gr-button>
+ <gr-button on-click="_cancelPendingReviewer">No</gr-button>
</div>
</gr-overlay>
</section>
@@ -273,7 +277,7 @@ limitations under the License.
class="action save"
has-tooltip
title="[[_saveTooltip]]"
- on-tap="_saveTapHandler">Save</gr-button>
+ on-click="_saveTapHandler">Save</gr-button>
</template>
<span
id="checkingStatusLabel"
@@ -284,7 +288,7 @@ limitations under the License.
id="notLatestLabel"
hidden$="[[!_isState(knownLatestState, 'not-latest')]]">
[[_computePatchSetWarning(patchNum, _labelsChanged)]]
- <gr-button link on-tap="_reload">Reload</gr-button>
+ <gr-button link on-click="_reload">Reload</gr-button>
</span>
</div>
<div class="right">
@@ -292,7 +296,7 @@ limitations under the License.
link
id="cancelButton"
class="action cancel"
- on-tap="_cancelTapHandler">Cancel</gr-button>
+ on-click="_cancelTapHandler">Cancel</gr-button>
<gr-button
id="sendButton"
link
@@ -301,7 +305,7 @@ limitations under the License.
class="action send"
has-tooltip
title$="[[_computeSendButtonTooltip(canBeStarted)]]"
- on-tap="_sendTapHandler">[[_sendButtonLabel]]</gr-button>
+ on-click="_sendTapHandler">[[_sendButtonLabel]]</gr-button>
</div>
</section>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index 43f5e8fbc6..da19e62887 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -48,17 +48,12 @@
SEND: 'Send reply',
};
- // TODO(logan): Remove once the fix for issue 6841 is stable on
- // googlesource.com.
- const START_REVIEW_MESSAGE = 'This change is ready for review.';
-
const EMPTY_REPLY_MESSAGE = 'Cannot send an empty reply.';
const SEND_REPLY_TIMING_LABEL = 'SendReply';
Polymer({
is: 'gr-reply-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when a reply is successfully sent.
@@ -220,12 +215,9 @@
FocusTarget,
- // TODO(logan): Remove once the fix for issue 6841 is stable on
- // googlesource.com.
- START_REVIEW_MESSAGE,
-
behaviors: [
Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PatchSetBehavior,
Gerrit.RESTClientBehavior,
@@ -314,31 +306,37 @@
},
_ccsChanged(splices) {
- if (splices && splices.indexSplices) {
- this._reviewersMutated = true;
- this._processReviewerChange(splices.indexSplices, ReviewerTypes.CC);
- }
+ this._reviewerTypeChanged(splices, ReviewerTypes.CC);
},
_reviewersChanged(splices) {
+ this._reviewerTypeChanged(splices, ReviewerTypes.REVIEWER);
+ },
+
+ _reviewerTypeChanged(splices, reviewerType) {
if (splices && splices.indexSplices) {
this._reviewersMutated = true;
this._processReviewerChange(splices.indexSplices,
- ReviewerTypes.REVIEWER);
+ reviewerType);
let key;
let index;
let account;
- // Remove any accounts that already exist as a CC.
+ // Remove any accounts that already exist as a CC for reviewer
+ // or vice versa.
+ const isReviewer = ReviewerTypes.REVIEWER === reviewerType;
for (const splice of splices.indexSplices) {
- for (const addedKey of splice.addedKeys) {
- account = this.get(`_reviewers.${addedKey}`);
+ for (let i = 0; i < splice.addedCount; i++) {
+ account = splice.object[splice.index + i];
key = this._accountOrGroupKey(account);
- index = this._ccs.findIndex(
+ const array = isReviewer ? this._ccs : this._reviewers;
+ index = array.findIndex(
account => this._accountOrGroupKey(account) === key);
if (index >= 0) {
- this.splice('_ccs', index, 1);
+ this.splice(isReviewer ? '_ccs' : '_reviewers', index, 1);
+ const moveFrom = isReviewer ? 'CC' : 'reviewer';
+ const moveTo = isReviewer ? 'reviewer' : 'CC';
const message = (account.name || account.email || key) +
- ' moved from CC to reviewer.';
+ ` moved from ${moveFrom} to ${moveTo}.`;
this.fire('show-alert', {message});
}
}
@@ -390,9 +388,6 @@
*
* @param {!Object} account
* @param {string} type
- *
- * * TODO(beckysiegel) submit Polymer PR
- * @suppress {checkTypes}
*/
_removeAccount(account, type) {
if (account._pendingAdd) { return; }
@@ -404,7 +399,7 @@
const reviewers = this.change.reviewers[type] || [];
for (let i = 0; i < reviewers.length; i++) {
if (reviewers[i]._account_id == account._account_id) {
- this.splice(['change', 'reviewers', type], i, 1);
+ this.splice(`change.reviewers.${type}`, i, 1);
break;
}
}
@@ -447,7 +442,7 @@
}
return this._mapReviewer(reviewer);
});
- const ccsEl = this.$$('#ccs');
+ const ccsEl = this.$.ccs;
if (ccsEl) {
for (let reviewer of ccsEl.additions()) {
if (reviewer.account) {
@@ -461,13 +456,6 @@
this.disabled = true;
- if (obj.ready && !obj.message) {
- // TODO(logan): The server currently doesn't send email in this case.
- // Insert a dummy message to force an email to be sent. Remove this
- // once the fix for issue 6841 is stable on googlesource.com.
- obj.message = START_REVIEW_MESSAGE;
- }
-
const errFn = this._handle400Error.bind(this);
return this._saveReview(obj, errFn).then(response => {
if (!response) {
@@ -480,18 +468,10 @@
return {};
}
- // TODO(logan): Remove once the required API changes are live and stable
- // on googlesource.com.
- return this._maybeSetReady(startReview, response).catch(err => {
- // We catch error here because we still want to treat this as a
- // successful review.
- console.error('error setting ready:', err);
- }).then(() => {
- this.draft = '';
- this._includeComments = true;
- this.fire('send', null, {bubbles: false});
- return accountAdditions;
- });
+ this.draft = '';
+ this._includeComments = true;
+ this.fire('send', null, {bubbles: false});
+ return accountAdditions;
}).then(result => {
this.disabled = false;
return result;
@@ -501,32 +481,6 @@
});
},
- /**
- * Returns a promise resolving to true if review was successfully posted,
- * false otherwise.
- *
- * TODO(logan): Remove this once the required API changes are live and
- * stable on googlesource.com.
- */
- _maybeSetReady(startReview, response) {
- return this.$.restAPI.getResponseObject(response).then(result => {
- if (!startReview || result.ready) {
- return Promise.resolve();
- }
- // We don't have confirmation that review was started, so attempt to
- // start review explicitly.
- return this.$.restAPI.startReview(
- this.change._number, null, response => {
- // If we see a 409 response code, then that means the server
- // *does* support moving from WIP->ready when posting a
- // review. Only alert user for non-409 failures.
- if (response.status !== 409) {
- this.fire('server-error', {response});
- }
- });
- });
- },
-
_focusOn(section) {
// Safeguard- always want to focus on something.
if (!section || section === FocusTarget.ANY) {
@@ -540,7 +494,7 @@
const reviewerEntry = this.$.reviewers.focusStart;
reviewerEntry.async(reviewerEntry.focus);
} else if (section === FocusTarget.CCS) {
- const ccEntry = this.$$('#ccs').focusStart;
+ const ccEntry = this.$.ccs.focusStart;
ccEntry.async(ccEntry.focus);
}
},
@@ -571,31 +525,31 @@
//
this.disabled = false;
- if (response.status !== 400) {
- // This is all restAPI does when there is no custom error handling.
- this.fire('server-error', {response});
- return response;
- }
-
- // Process the response body, format a better error message, and fire
- // an event for gr-event-manager to display.
- const jsonPromise = this.$.restAPI.getResponseObject(response);
+ // Using response.clone() here, because getResponseObject() and
+ // potentially the generic error handler will want to call text() on the
+ // response object, which can only be done once per object.
+ const jsonPromise = this.$.restAPI.getResponseObject(response.clone());
return jsonPromise.then(result => {
- const errors = [];
- for (const state of ['reviewers', 'ccs']) {
- if (!result.hasOwnProperty(state)) { continue; }
- for (const reviewer of Object.values(result[state])) {
- if (reviewer.error) {
- errors.push(reviewer.error);
+ // Only perform custom error handling for 400s and a parseable
+ // ReviewResult response.
+ if (response.status === 400 && result) {
+ const errors = [];
+ for (const state of ['reviewers', 'ccs']) {
+ if (!result.hasOwnProperty(state)) { continue; }
+ for (const reviewer of Object.values(result[state])) {
+ if (reviewer.error) {
+ errors.push(reviewer.error);
+ }
}
}
+ response = {
+ ok: false,
+ status: response.status,
+ text() { return Promise.resolve(errors.join(', ')); },
+ };
}
- response = {
- ok: false,
- status: response.status,
- text() { return Promise.resolve(errors.join(', ')); },
- };
this.fire('server-error', {response});
+ return null; // Means that the error has been handled.
});
},
@@ -622,6 +576,11 @@
},
_changeUpdated(changeRecord, owner) {
+ // Polymer 2: check for undefined
+ if ([changeRecord, owner].some(arg => arg === undefined)) {
+ return;
+ }
+
this._rebuildReviewerArrays(changeRecord.base, owner);
},
@@ -712,7 +671,7 @@
_saveTapHandler(e) {
e.preventDefault();
- if (!this.$$('#ccs').submitEntryText()) {
+ if (!this.$.ccs.submitEntryText()) {
// Do not proceed with the save if there is an invalid email entry in
// the text field of the CC entry.
return;
@@ -728,7 +687,7 @@
},
_submit() {
- if (!this.$$('#ccs').submitEntryText()) {
+ if (!this.$.ccs.submitEntryText()) {
// Do not proceed with the send if there is an invalid email entry in
// the text field of the CC entry.
return;
@@ -736,6 +695,7 @@
if (this._sendDisabled) {
this.dispatchEvent(new CustomEvent('show-alert', {
bubbles: true,
+ composed: true,
detail: {message: EMPTY_REPLY_MESSAGE},
}));
return;
@@ -743,6 +703,13 @@
return this.send(this._includeComments, this.canBeStarted)
.then(keepReviewers => {
this._purgeReviewersPendingRemove(false, keepReviewers);
+ })
+ .catch(err => {
+ this.dispatchEvent(new CustomEvent('show-error', {
+ bubbles: true,
+ composed: true,
+ detail: {message: `Error submitting review ${err}`},
+ }));
});
},
@@ -763,7 +730,7 @@
_confirmPendingReviewer() {
if (this._ccPendingConfirmation) {
- this.$$('#ccs').confirmGroup(this._ccPendingConfirmation.group);
+ this.$.ccs.confirmGroup(this._ccPendingConfirmation.group);
this._focusOn(FocusTarget.CCS);
} else {
this.$.reviewers.confirmGroup(this._reviewerPendingConfirmation.group);
@@ -831,7 +798,8 @@
_reload() {
// Load the current change without any patch range.
- location.href = this.getBaseUrl() + '/c/' + this.change._number;
+ Gerrit.Nav.navigateToChange(this.change);
+ this.cancel();
},
_computeSendButtonLabel(canBeStarted) {
@@ -848,6 +816,19 @@
_computeSendButtonDisabled(buttonLabel, drafts, text, reviewersMutated,
labelsChanged, includeComments, disabled) {
+ // Polymer 2: check for undefined
+ if ([
+ buttonLabel,
+ drafts,
+ text,
+ reviewersMutated,
+ labelsChanged,
+ includeComments,
+ disabled,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
if (disabled) { return true; }
if (buttonLabel === ButtonLabels.START_REVIEW) { return false; }
const hasDrafts = includeComments && Object.keys(drafts).length;
@@ -869,5 +850,19 @@
_sendDisabledChanged(sendDisabled) {
this.dispatchEvent(new CustomEvent('send-disabled-changed'));
},
+
+ _getReviewerSuggestionsProvider(change) {
+ const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
+ change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.REVIEWER);
+ provider.init();
+ return provider;
+ },
+
+ _getCcSuggestionsProvider(change) {
+ const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
+ change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.CC);
+ provider.init();
+ return provider;
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
index 5577c1b586..d8d49cf5a5 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-reply-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-reply-dialog.html">
@@ -33,6 +35,25 @@ limitations under the License.
</test-fixture>
<script>
+ function cloneableResponse(status, text) {
+ return {
+ ok: false,
+ status,
+ text() {
+ return Promise.resolve(text);
+ },
+ clone() {
+ return {
+ ok: false,
+ status,
+ text() {
+ return Promise.resolve(text);
+ },
+ };
+ },
+ };
+ }
+
suite('gr-reply-dialog tests', () => {
let element;
let changeNum;
@@ -256,17 +277,19 @@ limitations under the License.
});
});
- test('setlabelValue', () => {
+ test('setlabelValue', done => {
element._account = {_account_id: 1};
- flushAsynchronousOperations();
- const label = 'Verified';
- const value = '+1';
- element.setLabelValue(label, value);
- flushAsynchronousOperations();
- const labels = element.$.labelScores.getLabelValues();
- assert.deepEqual(labels, {
- 'Code-Review': 0,
- 'Verified': 1,
+ flush(() => {
+ const label = 'Verified';
+ const value = '+1';
+ element.setLabelValue(label, value);
+
+ const labels = element.$.labelScores.getLabelValues();
+ assert.deepEqual(labels, {
+ 'Code-Review': 0,
+ 'Verified': 1,
+ });
+ done();
});
});
@@ -289,6 +312,26 @@ limitations under the License.
});
}
+ function isFocusInsideElement(element) {
+ // In Polymer 2 focused element either <paper-input> or nested
+ // native input <input> element depending on the current focus
+ // in browser window.
+ // For example, the focus is changed if the developer console
+ // get a focus.
+ let activeElement = getActiveElement();
+ while (activeElement) {
+ if (activeElement === element) {
+ return true;
+ }
+ if (activeElement.parentElement) {
+ activeElement = activeElement.parentElement;
+ } else {
+ activeElement = activeElement.getRootNode().host;
+ }
+ }
+ return false;
+ }
+
function testConfirmationDialog(done, cc) {
const yesButton =
element.$$('.reviewerConfirmationButtons gr-button:first-child');
@@ -342,10 +385,11 @@ limitations under the License.
assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
// We should be focused on account entry input.
- assert.equal(getActiveElement().id, 'input');
+ assert.isTrue(
+ isFocusInsideElement(element.$.reviewers.$.entry.$.input.$.input));
// No reviewer/CC should have been added.
- assert.equal(element.$$('#ccs').additions().length, 0);
+ assert.equal(element.$.ccs.additions().length, 0);
assert.equal(element.$.reviewers.additions().length, 0);
// Reopen confirmation dialog.
@@ -370,7 +414,7 @@ limitations under the License.
}).then(() => {
assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay));
const additions = cc ?
- element.$$('#ccs').additions() :
+ element.$.ccs.additions() :
element.$.reviewers.additions();
assert.deepEqual(
additions,
@@ -387,7 +431,13 @@ limitations under the License.
]);
// We should be focused on account entry input.
- assert.equal(getActiveElement().id, 'input');
+ if (cc) {
+ assert.isTrue(
+ isFocusInsideElement(element.$.ccs.$.entry.$.input.$.input));
+ } else {
+ assert.isTrue(
+ isFocusInsideElement(element.$.reviewers.$.entry.$.input.$.input));
+ }
}).then(done);
}
@@ -409,10 +459,10 @@ limitations under the License.
test('_reviewersMutated when account-text-change is fired from ccs', () => {
flushAsynchronousOperations();
assert.isFalse(element._reviewersMutated);
- assert.isTrue(element.$$('#ccs').allowAnyInput);
+ assert.isTrue(element.$.ccs.allowAnyInput);
assert.isFalse(element.$$('#reviewers').allowAnyInput);
- element.$$('#ccs').dispatchEvent(new CustomEvent('account-text-changed',
- {bubbles: true}));
+ element.$.ccs.dispatchEvent(new CustomEvent('account-text-changed',
+ {bubbles: true, composed: true}));
assert.isTrue(element._reviewersMutated);
});
@@ -471,11 +521,7 @@ limitations under the License.
sandbox.stub(window, 'fetch', () => {
const text = '....{"reviewers":{"id1":{"error":"first error"}},' +
'"ccs":{"id2":{"error":"second error"}}}';
- return Promise.resolve({
- ok: false,
- status: 400,
- text() { return Promise.resolve(text); },
- });
+ return Promise.resolve(cloneableResponse(400, text));
});
element.addEventListener('server-error', event => {
@@ -493,6 +539,27 @@ limitations under the License.
flush(() => { element.send(); });
});
+ test('non-json 400 is treated as a normal server-error', done => {
+ sandbox.stub(window, 'fetch', () => {
+ const text = 'Comment validation error!';
+ return Promise.resolve(cloneableResponse(400, text));
+ });
+
+ element.addEventListener('server-error', event => {
+ if (event.target !== element) {
+ return;
+ }
+ event.detail.response.text().then(body => {
+ assert.equal(body, 'Comment validation error!');
+ done();
+ });
+ });
+
+ // Async tick is needed because iron-selector content is distributed and
+ // distributed content requires an observer to be set up.
+ flush(() => { element.send(); });
+ });
+
test('filterReviewerSuggestion', () => {
const owner = makeAccount();
const reviewer1 = makeAccount();
@@ -528,7 +595,7 @@ limitations under the License.
const textareaStub = sandbox.stub(element.$.textarea, 'async');
const reviewerEntryStub = sandbox.stub(element.$.reviewers.focusStart,
'async');
- const ccStub = sandbox.stub(element.$$('#ccs').focusStart, 'async');
+ const ccStub = sandbox.stub(element.$.ccs.focusStart, 'async');
element._focusOn();
assert.equal(element._chooseFocusTarget.callCount, 1);
assert.deepEqual(textareaStub.callCount, 1);
@@ -695,6 +762,40 @@ limitations under the License.
assert.deepEqual(element._reviewersPendingRemove.CC, [cc1, cc4, cc3]);
});
+ test('moving from reviewer to cc', () => {
+ element._reviewersPendingRemove = {
+ CC: [],
+ REVIEWER: [],
+ };
+ flushAsynchronousOperations();
+
+ const reviewer1 = makeAccount();
+ const reviewer2 = makeAccount();
+ const reviewer3 = makeAccount();
+ const cc1 = makeAccount();
+ const cc2 = makeAccount();
+ const cc3 = makeAccount();
+ const cc4 = makeAccount();
+ element._reviewers = [reviewer1, reviewer2, reviewer3];
+ element._ccs = [cc1, cc2, cc3, cc4];
+ element.push('_ccs', reviewer1);
+ flushAsynchronousOperations();
+
+ assert.deepEqual(element._reviewers,
+ [reviewer2, reviewer3]);
+ assert.deepEqual(element._ccs, [cc1, cc2, cc3, cc4, reviewer1]);
+ assert.deepEqual(element._reviewersPendingRemove.REVIEWER, [reviewer1]);
+
+ element.push('_ccs', reviewer3, reviewer2);
+ flushAsynchronousOperations();
+
+ assert.deepEqual(element._reviewers, []);
+ assert.deepEqual(element._ccs,
+ [cc1, cc2, cc3, cc4, reviewer1, reviewer3, reviewer2]);
+ assert.deepEqual(element._reviewersPendingRemove.REVIEWER,
+ [reviewer1, reviewer3, reviewer2]);
+ });
+
test('migrate reviewers between states', done => {
element._reviewersPendingRemove = {
CC: [],
@@ -702,7 +803,7 @@ limitations under the License.
};
flushAsynchronousOperations();
const reviewers = element.$.reviewers;
- const ccs = element.$$('#ccs');
+ const ccs = element.$.ccs;
const reviewer1 = makeAccount();
const reviewer2 = makeAccount();
const cc1 = makeAccount();
@@ -801,60 +902,50 @@ limitations under the License.
const error1 = 'error 1';
const error2 = 'error 2';
const error3 = 'error 3';
- const response = {
- status: 400,
- text() {
- return Promise.resolve(')]}\'' + JSON.stringify({
- reviewers: {
- username1: {
- input: 'user 1',
- error: error1,
- },
- username2: {
- input: 'user 2',
- error: error2,
- },
- },
- ccs: {
- username3: {
- input: 'user 3',
- error: error3,
- },
- },
- }));
+ const text = ')]}\'' + JSON.stringify({
+ reviewers: {
+ username1: {
+ input: 'user 1',
+ error: error1,
+ },
+ username2: {
+ input: 'user 2',
+ error: error2,
+ },
},
- };
+ ccs: {
+ username3: {
+ input: 'user 3',
+ error: error3,
+ },
+ },
+ });
element.addEventListener('server-error', e => {
e.detail.response.text().then(text => {
assert.equal(text, [error1, error2, error3].join(', '));
done();
});
});
- element._handle400Error(response);
+ element._handle400Error(cloneableResponse(400, text));
});
test('_handle400Error CCs only', done => {
const error1 = 'error 1';
- const response = {
- status: 400,
- text() {
- return Promise.resolve(')]}\'' + JSON.stringify({
- ccs: {
- username1: {
- input: 'user 1',
- error: error1,
- },
- },
- }));
+ const text = ')]}\'' + JSON.stringify({
+ ccs: {
+ username1: {
+ input: 'user 1',
+ error: error1,
+ },
},
- };
+ });
element.addEventListener('server-error', e => {
e.detail.response.text().then(text => {
assert.equal(text, error1);
done();
});
});
- element._handle400Error(response);
+ element._handle400Error(cloneableResponse(400, text));
});
test('fires height change when the drafts load', done => {
@@ -898,21 +989,6 @@ limitations under the License.
assert.isFalse(startReviewStub.called);
});
});
-
- test('fall back to start review against old backend', () => {
- stubSaveReview(review => {
- return {}; // old backend won't set ready: true
- });
-
- return element.send(true, true).then(() => {
- assert.isTrue(startReviewStub.called);
- }).then(() => {
- startReviewStub.reset();
- return element.send(true, false);
- }).then(() => {
- assert.isFalse(startReviewStub.called);
- });
- });
});
suite('start review and save buttons', () => {
@@ -938,28 +1014,9 @@ limitations under the License.
});
});
- test('dummy message to force email on start review', () => {
- stubSaveReview(review => {
- assert.equal(review.message, element.START_REVIEW_MESSAGE);
- return {ready: true};
- });
- return element.send(true, true);
- });
-
test('buttons disabled until all API calls are resolved', () => {
stubSaveReview(review => {
- return {}; // old backend won't set ready: true
- });
- // Check that element is disabled asynchronously after the setReady
- // promise is returned. The element should not be reenabled until
- // that promise is resolved.
- sandbox.stub(element, '_maybeSetReady', (startReview, response) => {
- return new Promise(resolve => {
- Polymer.Base.async(() => {
- assert.isTrue(element.disabled);
- resolve();
- });
- });
+ return {ready: true};
});
return element.send(true, true).then(() => {
assert.isFalse(element.disabled);
@@ -979,11 +1036,6 @@ limitations under the License.
assert.isFalse(element.disabled);
}
- function assertDialogClosed() {
- assert.strictEqual('', element.draft);
- assert.isFalse(element.disabled);
- }
-
test('error occurs in _saveReview', () => {
stubSaveReview(review => {
throw expectedError;
@@ -994,46 +1046,6 @@ limitations under the License.
});
});
- test('error occurs during startReview', () => {
- stubSaveReview(review => {
- return {}; // old backend won't set ready: true
- });
- const errorStub = sandbox.stub(
- console, 'error', (msg, err) => undefined);
- sandbox.stub(element.$.restAPI, 'startReview', () => {
- throw expectedError;
- });
- return element.send(true, true).then(() => {
- assertDialogClosed();
- assert.isTrue(
- errorStub.calledWith('error setting ready:', expectedError));
- });
- });
-
- test('non-ok response received by startReview', () => {
- stubSaveReview(review => {
- return {}; // old backend won't set ready: true
- });
- sandbox.stub(element.$.restAPI, 'startReview', (c, b, f) => {
- f({status: 500});
- });
- return element.send(true, true).then(() => {
- assertDialogClosed();
- });
- });
-
- test('409 response received by startReview', () => {
- stubSaveReview(review => {
- return {}; // old backend won't set ready: true
- });
- sandbox.stub(element.$.restAPI, 'startReview', (c, b, f) => {
- f({status: 409});
- });
- return element.send(true, true).then(() => {
- assertDialogClosed();
- });
- });
-
suite('pending diff drafts?', () => {
test('yes', () => {
const promise = mockPromise();
@@ -1065,20 +1077,84 @@ limitations under the License.
test('_computeSendButtonDisabled', () => {
const fn = element._computeSendButtonDisabled.bind(element);
- assert.isFalse(fn('Start review'));
- assert.isTrue(fn('Send', {}, '', false, false, false));
+ assert.isFalse(fn(
+ /* buttonLabel= */ 'Start review',
+ /* drafts= */ {},
+ /* text= */ '',
+ /* reviewersMutated= */ false,
+ /* labelsChanged= */ false,
+ /* includeComments= */ false,
+ /* disabled= */ false
+ ));
+ assert.isTrue(fn(
+ /* buttonLabel= */ 'Send',
+ /* drafts= */ {},
+ /* text= */ '',
+ /* reviewersMutated= */ false,
+ /* labelsChanged= */ false,
+ /* includeComments= */ false,
+ /* disabled= */ false
+ ));
// Mock nonempty comment draft array, with seding comments.
- assert.isFalse(fn('Send', {file: ['draft']}, '', false, false, true));
+ assert.isFalse(fn(
+ /* buttonLabel= */ 'Send',
+ /* drafts= */ {file: ['draft']},
+ /* text= */ '',
+ /* reviewersMutated= */ false,
+ /* labelsChanged= */ false,
+ /* includeComments= */ true,
+ /* disabled= */ false
+ ));
// Mock nonempty comment draft array, without seding comments.
- assert.isTrue(fn('Send', {file: ['draft']}, '', false, false, false));
+ assert.isTrue(fn(
+ /* buttonLabel= */ 'Send',
+ /* drafts= */ {file: ['draft']},
+ /* text= */ '',
+ /* reviewersMutated= */ false,
+ /* labelsChanged= */ false,
+ /* includeComments= */ false,
+ /* disabled= */ false
+ ));
// Mock nonempty change message.
- assert.isFalse(fn('Send', {}, 'test', false, false, false));
+ assert.isFalse(fn(
+ /* buttonLabel= */ 'Send',
+ /* drafts= */ {},
+ /* text= */ 'test',
+ /* reviewersMutated= */ false,
+ /* labelsChanged= */ false,
+ /* includeComments= */ false,
+ /* disabled= */ false
+ ));
// Mock reviewers mutated.
- assert.isFalse(fn('Send', {}, '', true, false, false));
+ assert.isFalse(fn(
+ /* buttonLabel= */ 'Send',
+ /* drafts= */ {},
+ /* text= */ '',
+ /* reviewersMutated= */ true,
+ /* labelsChanged= */ false,
+ /* includeComments= */ false,
+ /* disabled= */ false
+ ));
// Mock labels changed.
- assert.isFalse(fn('Send', {}, '', false, true, false));
+ assert.isFalse(fn(
+ /* buttonLabel= */ 'Send',
+ /* drafts= */ {},
+ /* text= */ '',
+ /* reviewersMutated= */ false,
+ /* labelsChanged= */ true,
+ /* includeComments= */ false,
+ /* disabled= */ false
+ ));
// Whole dialog is disabled.
- assert.isTrue(fn('Send', {}, '', false, true, false, true));
+ assert.isTrue(fn(
+ /* buttonLabel= */ 'Send',
+ /* drafts= */ {},
+ /* text= */ '',
+ /* reviewersMutated= */ false,
+ /* labelsChanged= */ true,
+ /* includeComments= */ false,
+ /* disabled= */ true
+ ));
});
test('_submit blocked when no mutations exist', () => {
@@ -1092,7 +1168,7 @@ limitations under the License.
MockInteractions.tap(element.$$('gr-button.send'));
assert.isFalse(sendStub.called);
- element.diffDrafts = {test: true};
+ element.diffDrafts = {test: [{val: true}]};
flushAsynchronousOperations();
MockInteractions.tap(element.$$('gr-button.send'));
@@ -1104,7 +1180,7 @@ limitations under the License.
// computed to false.
element.diffDrafts = {};
assert.equal(element.getFocusStops().end, element.$.cancelButton);
- element.diffDrafts = {test: true};
+ element.diffDrafts = {test: [{val: true}]};
assert.equal(element.getFocusStops().end, element.$.sendButton);
});
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
index 73e8bea5d1..a5875ab1c7 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -32,53 +32,35 @@ limitations under the License.
opacity: .8;
pointer-events: none;
}
- .autocompleteContainer {
- position: relative;
- }
- .hiddenReviewers {
- margin-top: .3em;
- }
- .inputContainer {
- display: flex;
- margin-top: .25em;
- }
- .inputContainer input {
- flex: 1;
- font: inherit;
- }
- gr-account-chip {
- margin-top: .3em;
+ .container > :not(:first-child) {
+ margin-top: var(--spacing-s);
}
gr-button {
--gr-button: {
- padding-left: 0;
- padding-right: 0;
- }
- }
- @media screen and (max-width: 50em), screen and (min-width: 75em) {
- gr-account-chip:first-of-type {
- margin-top: 0;
+ padding: 0px 0px;
}
}
</style>
- <template is="dom-repeat" items="[[_displayedReviewers]]" as="reviewer">
- <gr-account-chip class="reviewer" account="[[reviewer]]"
- on-remove="_handleRemove"
- additional-text="[[_computeReviewerTooltip(reviewer, change)]]"
- removable="[[_computeCanRemoveReviewer(reviewer, mutable)]]">
- </gr-account-chip>
- </template>
- <gr-button
- class="hiddenReviewers"
- link
- hidden$="[[!_hiddenReviewerCount]]"
- on-tap="_handleViewAll">and [[_hiddenReviewerCount]] more</gr-button>
- <div class="controlsContainer" hidden$="[[!mutable]]">
+ <div class="container">
+ <template is="dom-repeat" items="[[_displayedReviewers]]" as="reviewer">
+ <gr-account-chip class="reviewer" account="[[reviewer]]"
+ on-remove="_handleRemove"
+ additional-text="[[_computeReviewerTooltip(reviewer, change)]]"
+ removable="[[_computeCanRemoveReviewer(reviewer, mutable)]]">
+ </gr-account-chip>
+ </template>
<gr-button
+ class="hiddenReviewers"
link
- id="addReviewer"
- class="addReviewer"
- on-tap="_handleAddTap">[[_addLabel]]</gr-button>
+ hidden$="[[!_hiddenReviewerCount]]"
+ on-click="_handleViewAll">and [[_hiddenReviewerCount]] more</gr-button>
+ <div class="controlsContainer" hidden$="[[!mutable]]">
+ <gr-button
+ link
+ id="addReviewer"
+ class="addReviewer"
+ on-click="_handleAddTap">[[_addLabel]]</gr-button>
+ </div>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
index 4f1b05155c..c94625d55f 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-reviewer-list',
- _legacyUndefinedCheck: true,
/**
* Fired when the "Add reviewer..." button is tapped.
@@ -75,6 +74,10 @@
_xhrPromise: Object,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
observers: [
'_reviewersChanged(change.reviewers.*, change.owner)',
],
@@ -163,6 +166,11 @@
},
_reviewersChanged(changeRecord, owner) {
+ // Polymer 2: check for undefined
+ if ([changeRecord, owner].some(arg => arg === undefined)) {
+ return;
+ }
+
let result = [];
const reviewers = changeRecord.base;
for (const key in reviewers) {
@@ -180,10 +188,10 @@
return reviewer._account_id != owner._account_id;
});
- // If there is one more than the max reviewers, don't show the 'show
- // more' button, because it takes up just as much space.
+ // If there is one or two more than the max reviewers, don't show the
+ // 'show more' button, because it takes up just as much space.
if (this.maxReviewersDisplayed &&
- this._reviewers.length > this.maxReviewersDisplayed + 1) {
+ this._reviewers.length > this.maxReviewersDisplayed + 2) {
this._displayedReviewers =
this._reviewers.slice(0, this.maxReviewersDisplayed);
} else {
@@ -192,6 +200,11 @@
},
_computeHiddenCount(reviewers, displayedReviewers) {
+ // Polymer 2: check for undefined
+ if ([reviewers, displayedReviewers].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
return reviewers.length - displayedReviewers.length;
},
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
index 81827fdd4d..6936a04199 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-reviewer-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-reviewer-list.html">
@@ -212,10 +214,10 @@ limitations under the License.
assert.isTrue(element.$$('.hiddenReviewers').hidden);
});
- test('show all reviewers button with 7 reviewers', () => {
+ test('show all reviewers button with 8 reviewers', () => {
const reviewers = [];
element.maxReviewersDisplayed = 5;
- for (let i = 0; i < 7; i++) {
+ for (let i = 0; i < 8; i++) {
reviewers.push(
{email: i+'reviewer@google.com', name: 'reviewer-' + i});
}
@@ -229,9 +231,9 @@ limitations under the License.
CC: reviewers,
},
};
- assert.equal(element._hiddenReviewerCount, 2);
+ assert.equal(element._hiddenReviewerCount, 3);
assert.equal(element._displayedReviewers.length, 5);
- assert.equal(element._reviewers.length, 7);
+ assert.equal(element._reviewers.length, 8);
assert.isFalse(element.$$('.hiddenReviewers').hidden);
});
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.html b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.html
index 4d8e5aea52..4c82d2a166 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.html
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-comment-thread/gr-comment-thread.html">
@@ -26,11 +26,11 @@ limitations under the License.
#threads {
display: block;
min-height: 20rem;
- padding: 1rem;
+ padding: var(--spacing-l);
}
gr-comment-thread {
display: block;
- margin-bottom: .5rem;
+ margin-bottom: var(--spacing-m);
max-width: 80ch;
}
.header {
@@ -41,7 +41,7 @@ limitations under the License.
display: flex;
justify-content: left;
min-height: 3.2em;
- padding: .5em var(--default-horizontal-margin);
+ padding: var(--spacing-m) var(--spacing-l);
}
.toggleItem.draftToggle {
display: none;
@@ -52,7 +52,7 @@ limitations under the License.
.toggleItem {
align-items: center;
display: flex;
- margin-right: 1rem;
+ margin-right: var(--spacing-l);
}
.draftsOnly:not(.unresolvedOnly) gr-comment-thread[has-draft],
.unresolvedOnly:not(.draftsOnly) gr-comment-thread[unresolved],
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
index 317685b207..747a47a992 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
@@ -25,7 +25,6 @@
Polymer({
is: 'gr-thread-list',
- _legacyUndefinedCheck: true,
properties: {
/** @type {?} */
@@ -95,12 +94,35 @@
},
_computeFilteredThreads(sortedThreads, unresolvedOnly, draftsOnly) {
+ // Polymer 2: check for undefined
+ if ([
+ sortedThreads,
+ unresolvedOnly,
+ draftsOnly,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
return sortedThreads.filter(c => {
if (draftsOnly) {
return c.hasDraft;
} else if (unresolvedOnly) {
return c.unresolved;
} else {
+ const comments = c && c.thread && c.thread.comments;
+ let robotComment = false;
+ let humanReplyToRobotComment = false;
+ comments.forEach(comment => {
+ if (comment.robot_id) {
+ robotComment = true;
+ } else if (robotComment) {
+ // Robot comment exists and human comment exists after it
+ humanReplyToRobotComment = true;
+ }
+ });
+ if (robotComment) {
+ return humanReplyToRobotComment ? c : false;
+ }
return c;
}
}).map(threadInfo => threadInfo.thread);
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
index 792644e7b0..ff65aa81ca 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-thread-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-thread-list.html">
@@ -168,6 +170,68 @@ limitations under the License.
rootId: 'zcf0b9fa_fe1a5f62',
start_datetime: '2018-02-09 18:49:18.000000000',
},
+ {
+ comments: [
+ {
+ __path: '/COMMIT_MSG',
+ author: {
+ _account_id: 1000000,
+ name: 'user',
+ username: 'user',
+ },
+ patch_set: 4,
+ id: 'rc1',
+ line: 5,
+ updated: '2019-02-08 18:49:18.000000000',
+ message: 'test',
+ unresolved: true,
+ robot_id: 'rc1',
+ },
+ ],
+ patchNum: 4,
+ path: '/COMMIT_MSG',
+ line: 5,
+ rootId: 'rc1',
+ start_datetime: '2019-02-08 18:49:18.000000000',
+ },
+ {
+ comments: [
+ {
+ __path: '/COMMIT_MSG',
+ author: {
+ _account_id: 1000000,
+ name: 'user',
+ username: 'user',
+ },
+ patch_set: 4,
+ id: 'rc2',
+ line: 5,
+ updated: '2019-03-08 18:49:18.000000000',
+ message: 'test',
+ unresolved: true,
+ robot_id: 'rc2',
+ },
+ {
+ __path: '/COMMIT_MSG',
+ author: {
+ _account_id: 1000000,
+ name: 'user',
+ username: 'user',
+ },
+ patch_set: 4,
+ id: 'c2_1',
+ line: 5,
+ updated: '2019-03-08 18:49:18.000000000',
+ message: 'test',
+ unresolved: true,
+ },
+ ],
+ patchNum: 4,
+ path: '/COMMIT_MSG',
+ line: 5,
+ rootId: 'rc2',
+ start_datetime: '2019-03-08 18:49:18.000000000',
+ },
];
flushAsynchronousOperations();
threadElements = Polymer.dom(element.root)
@@ -192,46 +256,69 @@ limitations under the License.
});
test('_computeSortedThreads', () => {
- assert.equal(element._sortedThreads.length, 5);
+ assert.equal(element._sortedThreads.length, 7);
// Draft and unresolved
assert.equal(element._sortedThreads[0].thread.rootId,
'ecf0b9fa_fe1a5f62');
- // unresolved
+ // Unresolved robot comment
assert.equal(element._sortedThreads[1].thread.rootId,
+ 'rc2');
+ // Unresolved robot comment
+ assert.equal(element._sortedThreads[2].thread.rootId,
+ 'rc1');
+ // unresolved
+ assert.equal(element._sortedThreads[3].thread.rootId,
'scaddf38_44770ec1');
// unresolved
- assert.equal(element._sortedThreads[2].thread.rootId,
+ assert.equal(element._sortedThreads[4].thread.rootId,
'8caddf38_44770ec1');
// resolved and draft
- assert.equal(element._sortedThreads[3].thread.rootId,
+ assert.equal(element._sortedThreads[5].thread.rootId,
'zcf0b9fa_fe1a5f62');
// resolved
- assert.equal(element._sortedThreads[4].thread.rootId,
+ assert.equal(element._sortedThreads[6].thread.rootId,
'09a9fb0a_1484e6cf');
});
+ test('filtered threads do not contain robot comments without reply', () => {
+ const thread = element.threads.find(thread => thread.rootId === 'rc1');
+ assert.equal(element._filteredThreads.includes(thread), false);
+ });
+
+ test('filtered threads contains robot comments with reply', () => {
+ const thread = element.threads.find(thread => thread.rootId === 'rc2');
+ assert.equal(element._filteredThreads.includes(thread), true);
+ });
+
+
test('thread removal', () => {
- threadElements[1].fire('thread-discard', {rootId: 'scaddf38_44770ec1'});
+ threadElements[1].fire('thread-discard', {rootId: 'rc2'});
flushAsynchronousOperations();
- assert.equal(element._sortedThreads.length, 4);
+ assert.equal(element._sortedThreads.length, 6);
assert.equal(element._sortedThreads[0].thread.rootId,
'ecf0b9fa_fe1a5f62');
- // unresolved
+ // Unresolved robot comment
assert.equal(element._sortedThreads[1].thread.rootId,
+ 'rc1');
+ // unresolved
+ assert.equal(element._sortedThreads[2].thread.rootId,
+ 'scaddf38_44770ec1');
+ // unresolved
+ assert.equal(element._sortedThreads[3].thread.rootId,
'8caddf38_44770ec1');
// resolved and draft
- assert.equal(element._sortedThreads[2].thread.rootId,
+ assert.equal(element._sortedThreads[4].thread.rootId,
'zcf0b9fa_fe1a5f62');
// resolved
- assert.equal(element._sortedThreads[3].thread.rootId,
+ assert.equal(element._sortedThreads[5].thread.rootId,
'09a9fb0a_1484e6cf');
});
- test('toggle unresolved only shows unressolved comments', () => {
+ test('toggle unresolved only shows unresolved comments', () => {
MockInteractions.tap(element.$.unresolvedToggle);
flushAsynchronousOperations();
assert.equal(Polymer.dom(element.root)
- .querySelectorAll('gr-comment-thread').length, 3);
+ .querySelectorAll('gr-comment-thread').length, 5);
});
test('toggle drafts only shows threads with draft comments', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html
index 792c300383..e3cee56d23 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-shell-command/gr-shell-command.html">
@@ -32,11 +33,11 @@ limitations under the License.
width: 100%;
}
ol {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
list-style: decimal;
}
p {
- margin-bottom: .75em;
+ margin-bottom: var(--spacing-m);
}
</style>
<gr-dialog
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
index df96be2d8f..092204ad60 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
@@ -29,7 +29,6 @@
Polymer({
is: 'gr-upload-help-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the user presses the close button.
@@ -58,6 +57,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
attached() {
this.$.restAPI.getLoggedIn().then(loggedIn => {
if (loggedIn) {
@@ -73,11 +76,21 @@
_handleCloseTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('close', null, {bubbles: false});
},
_computeFetchCommand(revision, preferredDownloadCommand,
preferredDownloadScheme) {
+ // Polymer 2: check for undefined
+ if ([
+ revision,
+ preferredDownloadCommand,
+ preferredDownloadScheme,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
if (!revision) { return; }
if (!revision || !revision.fetch) { return; }
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html
index a5a6e7698f..577b978d05 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-upload-help-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-upload-help-dialog.html">
@@ -73,31 +75,40 @@ limitations under the License.
assert.isUndefined(element._computeFetchCommand({fetch: {}}));
});
+ test('not all defined', () => {
+ assert.isUndefined(
+ element._computeFetchCommand(testRev, undefined, ''));
+ assert.isUndefined(
+ element._computeFetchCommand(testRev, '', undefined));
+ assert.isUndefined(
+ element._computeFetchCommand(undefined, '', ''));
+ });
+
test('insufficiently defined scheme', () => {
assert.isUndefined(
- element._computeFetchCommand(testRev, undefined, 'badscheme'));
+ element._computeFetchCommand(testRev, '', 'badscheme'));
const rev = Object.assign({}, testRev);
rev.fetch = Object.assign({}, testRev.fetch, {nocmds: {commands: {}}});
assert.isUndefined(
- element._computeFetchCommand(rev, undefined, 'nocmds'));
+ element._computeFetchCommand(rev, '', 'nocmds'));
rev.fetch.nocmds.commands.unsupported = 'unsupported';
assert.isUndefined(
- element._computeFetchCommand(rev, undefined, 'nocmds'));
+ element._computeFetchCommand(rev, '', 'nocmds'));
});
test('default scheme and command', () => {
- const cmd = element._computeFetchCommand(testRev);
+ const cmd = element._computeFetchCommand(testRev, '', '');
assert.isTrue(cmd === 'http checkout' || cmd === 'ssh pull');
});
test('default command', () => {
assert.strictEqual(
- element._computeFetchCommand(testRev, undefined, 'http'),
+ element._computeFetchCommand(testRev, '', 'http'),
'http checkout');
assert.strictEqual(
- element._computeFetchCommand(testRev, undefined, 'ssh'),
+ element._computeFetchCommand(testRev, '', 'ssh'),
'ssh pull');
});
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
index 2c2fb4ea8c..5152ef9555 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
@@ -15,18 +15,19 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-avatar/gr-avatar.html">
<dom-module id="gr-account-dropdown">
<template>
<style include="shared-styles">
gr-dropdown {
- padding: 0 .5em;
+ padding: 0 var(--spacing-m);
--gr-button: {
color: var(--header-text-color);
}
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
index af4510d360..7cbe9883a3 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-account-dropdown',
- _legacyUndefinedCheck: true,
properties: {
account: Object,
@@ -58,7 +57,7 @@
},
behaviors: [
- Gerrit.AnonymousNameBehavior,
+ Gerrit.DisplayNameBehavior,
],
detached() {
@@ -66,6 +65,11 @@
},
_getLinks(switchAccountUrl, path) {
+ // Polymer 2: check for undefined
+ if ([switchAccountUrl, path].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const links = [{name: 'Settings', url: '/settings/'}];
if (switchAccountUrl) {
const replacements = {path};
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
index fe63a3e4c1..e29faa847f 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-account-dropdown</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-account-dropdown.html">
@@ -78,11 +80,15 @@ limitations under the License.
});
test('switch account', () => {
+ // Missing params.
+ assert.isUndefined(element._getLinks());
+ assert.isUndefined(element._getLinks(null));
+
// No switch account link.
- assert.equal(element._getLinks(null).length, 2);
+ assert.equal(element._getLinks(null, '').length, 2);
// Unparameterized switch account link.
- let links = element._getLinks('/switch-account');
+ let links = element._getLinks('/switch-account', '');
assert.equal(links.length, 3);
assert.deepEqual(links[1], {
name: 'Switch account',
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html
index f8bf33c2d4..09b928ed67 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../../styles/shared-styles.html">
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
index 5679408286..8d3b58e363 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-error-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the dismiss button is pressed.
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html
index e2c314b0dd..648f8be28f 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-error-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-error-dialog.html">
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
index 4ca106eeb6..048d39227e 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
@@ -16,7 +16,8 @@ limitations under the License.
-->
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../core/gr-error-dialog/gr-error-dialog.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../../shared/gr-alert/gr-alert.html">
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
index 38dfecb5a3..5865e3c5c5 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
@@ -27,10 +27,10 @@
Polymer({
is: 'gr-error-manager',
- _legacyUndefinedCheck: true,
behaviors: [
Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
],
properties: {
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
index 88e3efd9dc..9140c17a0d 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-error-manager</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-error-manager.html">
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.html b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.html
index 2ff7953a37..a8632762b1 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.html
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-key-binding-display">
@@ -24,10 +24,10 @@ limitations under the License.
.key {
background-color: var(--chip-background-color);
border: 1px solid var(--border-color);
- border-radius: 3px;
+ border-radius: var(--border-radius);
display: inline-block;
font-weight: var(--font-weight-bold);
- padding: .1em .5em;
+ padding: var(--spacing-xxs) var(--spacing-m);
text-align: center;
}
</style>
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
index e8c6479947..89d1091086 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-key-binding-display',
- _legacyUndefinedCheck: true,
properties: {
/** @type {Array<string>} */
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html
index 0361d76d93..39c8af8012 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-key-binding-display</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-key-binding-display.html">
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
index e3552ccd25..5494f626cc 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../gr-key-binding-display/gr-key-binding-display.html">
@@ -30,11 +31,11 @@ limitations under the License.
overflow-y: auto;
}
header{
- padding: 1em;
+ padding: var(--spacing-l);
}
main {
display: flex;
- padding: 0 2em 2em;
+ padding: 0 var(--spacing-xxl) var(--spacing-xxl);
}
header {
align-items: center;
@@ -43,18 +44,18 @@ limitations under the License.
justify-content: space-between;
}
table:last-of-type {
- margin-left: 3em;
+ margin-left: var(--spacing-xxl);
}
td {
- padding: .2em 0;
+ padding: var(--spacing-xs) 0;
}
td:first-child {
- padding-right: .5em;
+ padding-right: var(--spacing-m);
text-align: right;
}
.header {
font-weight: var(--font-weight-bold);
- padding-top: 1em;
+ padding-top: var(--spacing-l);
}
.modifier {
font-weight: normal;
@@ -62,7 +63,7 @@ limitations under the License.
</style>
<header>
<h3>Keyboard shortcuts</h3>
- <gr-button link on-tap="_handleCloseTap">Close</gr-button>
+ <gr-button link on-click="_handleCloseTap">Close</gr-button>
</header>
<main>
<table>
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
index f206db1496..4bc6e11bdc 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-keyboard-shortcuts-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the user presses the close button.
@@ -51,6 +50,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
],
@@ -70,6 +70,7 @@
_handleCloseTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('close', null, {bubbles: false});
},
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html
index 50579dda40..1a3d6c78e6 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-key-binding-display</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-keyboard-shortcuts-dialog.html">
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index e552cdad36..d29858eedd 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -14,10 +14,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/docs-url-behavior/docs-url-behavior.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html">
<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
@@ -45,7 +46,6 @@ limitations under the License.
.bigTitle:hover {
text-decoration: underline;
}
- /* TODO (viktard): Clean-up after chromium-style migrates to component. */
.titleText::before {
background-image: var(--header-icon);
background-size: var(--header-icon-size) var(--header-icon-size);
@@ -62,7 +62,7 @@ limitations under the License.
}
ul {
list-style: none;
- padding-left: 1em;
+ padding-left: var(--spacing-l);
}
.links > li {
cursor: default;
@@ -86,16 +86,16 @@ limitations under the License.
justify-content: flex-end;
}
.rightItems gr-endpoint-decorator:not(:empty) {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
gr-smart-search {
flex-grow: 1;
- margin-left: .5em;
+ margin: 0 var(--spacing-m);
max-width: 500px;
}
gr-dropdown,
.browse {
- padding: .6em .5em;
+ padding: var(--spacing-m);
}
gr-dropdown {
--gr-dropdown-item: {
@@ -103,7 +103,7 @@ limitations under the License.
}
}
.settingsButton {
- margin-left: .5em;
+ margin-left: var(--spacing-m);
}
.browse {
color: var(--header-text-color);
@@ -128,13 +128,13 @@ limitations under the License.
.accountContainer {
align-items: center;
display: flex;
- margin: 0 -.5em 0 .5em;
+ margin: 0 calc(0 - var(--spacing-m)) 0 var(--spacing-m);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.loginButton, .registerButton {
- padding: .5em 1em;
+ padding: var(--spacing-m) var(--spacing-l);
}
.dropdown-trigger {
text-decoration: none;
@@ -160,7 +160,7 @@ limitations under the License.
}
@media screen and (max-width: 50em) {
.bigTitle {
- font-size: var(--font-size-large);
+ font-size: var(--font-size-h3);
font-weight: var(--font-weight-bold);
}
gr-smart-search,
@@ -173,10 +173,10 @@ limitations under the License.
display: inline-flex;
}
.accountContainer {
- margin-left: .5em !important;
+ margin-left: var(--spacing-m) !important;
}
gr-dropdown {
- padding: .5em 0 .5em .5em;
+ padding: var(--spacing-m) 0 var(--spacing-m) var(--spacing-m);
}
}
</style>
@@ -188,16 +188,16 @@ limitations under the License.
</a>
<ul class="links">
<template is="dom-repeat" items="[[_links]]" as="linkGroup">
- <li class$="[[linkGroup.class]]">
- <gr-dropdown
- link
- down-arrow
- items = [[linkGroup.links]]
- horizontal-align="left">
- <span class="linksTitle" id="[[linkGroup.title]]">
- [[linkGroup.title]]
- </span>
- </gr-dropdown>
+ <li class$="[[_computeLinkGroupClass(linkGroup)]]">
+ <gr-dropdown
+ link
+ down-arrow
+ items = [[linkGroup.links]]
+ horizontal-align="left">
+ <span class="linksTitle" id="[[linkGroup.title]]">
+ [[linkGroup.title]]
+ </span>
+ </gr-dropdown>
</li>
</template>
</ul>
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
index 69fc89ff0b..773ad68dc6 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -71,7 +71,6 @@
Polymer({
is: 'gr-main-header',
- _legacyUndefinedCheck: true,
hostAttributes: {
role: 'banner',
@@ -138,6 +137,7 @@
Gerrit.AdminNavBehavior,
Gerrit.BaseUrlBehavior,
Gerrit.DocsUrlBehavior,
+ Gerrit.FireBehavior,
],
observers: [
@@ -180,6 +180,17 @@
},
_computeLinks(defaultLinks, userLinks, adminLinks, topMenus, docBaseUrl) {
+ // Polymer 2: check for undefined
+ if ([
+ defaultLinks,
+ userLinks,
+ adminLinks,
+ topMenus,
+ docBaseUrl,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const links = defaultLinks.map(menu => {
return {
title: menu.title,
@@ -318,7 +329,16 @@
_onMobileSearchTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('mobile-search', null, {bubbles: false});
},
+
+ _computeLinkGroupClass(linkGroup) {
+ if (linkGroup && linkGroup.class) {
+ return linkGroup.class;
+ }
+
+ return '';
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
index 89b3908754..3309aa594e 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-main-header</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-main-header.html">
@@ -109,22 +111,34 @@ limitations under the License.
}];
// When no admin links are passed, it should use the default.
- assert.deepEqual(element._computeLinks(defaultLinks, [], adminLinks, []),
- defaultLinks.concat({
- title: 'Browse',
- links: adminLinks,
- }));
- assert.deepEqual(
- element._computeLinks(defaultLinks, userLinks, adminLinks, []),
- defaultLinks.concat([
- {
- title: 'Your',
- links: userLinks,
- },
- {
- title: 'Browse',
- links: adminLinks,
- }]));
+ assert.deepEqual(element._computeLinks(
+ defaultLinks,
+ /* userLinks= */[],
+ adminLinks,
+ /* topMenus= */[],
+ /* docBaseUrl= */ ''
+ ),
+ defaultLinks.concat({
+ title: 'Browse',
+ links: adminLinks,
+ }));
+ assert.deepEqual(element._computeLinks(
+ defaultLinks,
+ userLinks,
+ adminLinks,
+ /* topMenus= */[],
+ /* docBaseUrl= */ ''
+ ),
+ defaultLinks.concat([
+ {
+ title: 'Your',
+ links: userLinks,
+ },
+ {
+ title: 'Browse',
+ links: adminLinks,
+ }])
+ );
});
test('documentation links', () => {
@@ -166,7 +180,13 @@ limitations under the License.
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
}],
}];
- assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+ assert.deepEqual(element._computeLinks(
+ /* defaultLinks= */ [],
+ /* userLinks= */ [],
+ adminLinks,
+ topMenus,
+ /* baseDocUrl= */ ''
+ ), [{
title: 'Browse',
links: adminLinks,
},
@@ -196,7 +216,13 @@ limitations under the License.
url: '/plugins/myplugin/index.html',
}],
}];
- assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+ assert.deepEqual(element._computeLinks(
+ /* defaultLinks= */ [],
+ /* userLinks= */ [],
+ adminLinks,
+ topMenus,
+ /* baseDocUrl= */ ''
+ ), [{
title: 'Browse',
links: adminLinks,
},
@@ -229,7 +255,13 @@ limitations under the License.
url: 'https://gerrit/plugins/plugin-manager/static/create.html',
}],
}];
- assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+ assert.deepEqual(element._computeLinks(
+ /* defaultLinks= */ [],
+ /* userLinks= */ [],
+ adminLinks,
+ topMenus,
+ /* baseDocUrl= */ ''
+ ), [{
title: 'Browse',
links: adminLinks,
}, {
@@ -260,7 +292,13 @@ limitations under the License.
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
}],
}];
- assert.deepEqual(element._computeLinks(defaultLinks, [], [], topMenus), [{
+ assert.deepEqual(element._computeLinks(
+ defaultLinks,
+ /* userLinks= */ [],
+ /* adminLinks= */ [],
+ topMenus,
+ /* baseDocUrl= */ ''
+ ), [{
title: 'Faves',
links: defaultLinks[0].links.concat([{
name: 'Manage',
@@ -285,7 +323,13 @@ limitations under the License.
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
}],
}];
- assert.deepEqual(element._computeLinks([], userLinks, [], topMenus), [{
+ assert.deepEqual(element._computeLinks(
+ /* defaultLinks= */ [],
+ userLinks,
+ /* adminLinks= */ [],
+ topMenus,
+ /* baseDocUrl= */ ''
+ ), [{
title: 'Your',
links: userLinks.concat([{
name: 'Manage',
@@ -310,7 +354,13 @@ limitations under the License.
url: 'https://gerrit/plugins/plugin-manager/static/index.html',
}],
}];
- assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+ assert.deepEqual(element._computeLinks(
+ /* defaultLinks= */ [],
+ /* userLinks= */ [],
+ adminLinks,
+ topMenus,
+ /* baseDocUrl= */ ''
+ ), [{
title: 'Browse',
links: adminLinks.concat([{
name: 'Manage',
@@ -348,4 +398,4 @@ limitations under the License.
assert.equal(element._registerText, 'Sign up');
});
});
-</script>
+ </script>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index ce2012770e..c8bf6c1236 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -115,6 +115,7 @@ limitations under the License.
query: 'assignee:${user} (-is:wip OR owner:self OR assignee:self) ' +
'is:open -is:ignored',
hideIfEmpty: true,
+ suffixForDashboard: 'limit:25',
},
{
// WIP open changes owned by viewing user. This section is omitted when
@@ -123,6 +124,7 @@ limitations under the License.
query: 'is:open owner:${user} is:wip',
selfOnly: true,
hideIfEmpty: true,
+ suffixForDashboard: 'limit:25',
},
{
// Non-WIP open changes owned by viewed user. Filter out changes ignored
@@ -130,6 +132,7 @@ limitations under the License.
name: 'Outgoing reviews',
query: 'is:open owner:${user} -is:wip -is:ignored',
isOutgoing: true,
+ suffixForDashboard: 'limit:25',
},
{
// Non-WIP open changes not owned by the viewed user, that the viewed user
@@ -138,12 +141,14 @@ limitations under the License.
name: 'Incoming reviews',
query: 'is:open -owner:${user} -is:wip -is:ignored ' +
'(reviewer:${user} OR assignee:${user})',
+ suffixForDashboard: 'limit:25',
},
{
// Non-WIP open changes the viewed user is CCed on. Changes ignored by the
// viewing user are filtered out.
name: 'CCed on',
query: 'is:open -is:wip -is:ignored cc:${user}',
+ suffixForDashboard: 'limit:10',
},
{
name: 'Recently closed',
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html
index 2f7233812e..73ce86a3a1 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-navigation</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js
deleted file mode 100644
index 28c46f4e9e..0000000000
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * @license
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-(function() {
- 'use strict';
-
- const JANK_SLEEP_TIME_MS = 1000;
-
- const GrJankDetector = {
- // Slowdowns counter.
- jank: 0,
- fps: 0,
- _lastFrameTime: 0,
-
- start() {
- this._requestAnimationFrame(this._detect.bind(this));
- },
-
- _requestAnimationFrame(callback) {
- window.requestAnimationFrame(callback);
- },
-
- _detect(now) {
- if (this._lastFrameTime === 0) {
- this._lastFrameTime = now;
- this.fps = 0;
- this._requestAnimationFrame(this._detect.bind(this));
- return;
- }
- const fpsNow = 1000/(now - this._lastFrameTime);
- this._lastFrameTime = now;
- // Calculate moving average within last 3 measurements.
- this.fps = this.fps === 0 ? fpsNow : ((this.fps * 2 + fpsNow) / 3);
- if (this.fps > 10) {
- this._requestAnimationFrame(this._detect.bind(this));
- } else {
- this.jank++;
- console.warn('JANK', this.jank);
- this._lastFrameTime = 0;
- window.setTimeout(
- () => this._requestAnimationFrame(this._detect.bind(this)),
- JANK_SLEEP_TIME_MS);
- }
- },
- };
-
- window.GrJankDetector = GrJankDetector;
-})();
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html
deleted file mode 100644
index 6faeec1d81..0000000000
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2018 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-jank-detector</title>
-
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
-<link rel="import" href="../../../test/common-test-setup.html"/>
-
-<script src="gr-jank-detector.js"></script>
-
-<script>
- suite('gr-jank-detector tests', () => {
- let sandbox;
- let clock;
- let instance;
-
- const NOW_TIME = 100;
-
- setup(() => {
- sandbox = sinon.sandbox.create();
- clock = sinon.useFakeTimers(NOW_TIME);
- instance = GrJankDetector;
- instance._lastFrameTime = 0;
- sandbox.stub(instance, '_requestAnimationFrame');
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('start() installs frame callback', () => {
- sandbox.stub(instance, '_detect');
- instance._requestAnimationFrame.callsArg(0);
- instance.start();
- assert.isTrue(instance._detect.calledOnce);
- });
-
- test('measures fps', () => {
- instance._detect(10);
- instance._detect(30);
- assert.equal(instance.fps, 50);
- });
-
- test('detects jank', () => {
- let now = 10;
- instance._detect(now);
- const fastFrame = () => instance._detect(now += 20);
- const slowFrame = () => instance._detect(now += 300);
- fastFrame();
- assert.equal(instance.jank, 0);
- _.times(4, slowFrame);
- assert.equal(instance.jank, 0);
- instance._requestAnimationFrame.reset();
- slowFrame();
- assert.equal(instance.jank, 1);
- assert.isFalse(instance._requestAnimationFrame.called);
- clock.tick(1000);
- assert.isTrue(instance._requestAnimationFrame.called);
- });
- });
-</script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
index 935de6bf1e..0ba8a22e80 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
@@ -15,9 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-reporting">
- <script src="gr-jank-detector.js"></script>
<script src="gr-reporting.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index 0947d77d09..276c137264 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -49,14 +49,6 @@
STARTED_HIDDEN: 'hidden',
};
- // Frame rate related constants.
- const JANK = {
- TYPE: 'lifecycle',
- CATEGORY: 'UI Latency',
- // Reported events - alphabetize below.
- COUNT: 'Jank count',
- };
-
// Navigation reporting constants.
const NAVIGATION = {
TYPE: 'nav-report',
@@ -78,24 +70,33 @@
CHANGE_DISPLAYED: 'ChangeDisplayed',
CHANGE_LOAD_FULL: 'ChangeFullyLoaded',
DASHBOARD_DISPLAYED: 'DashboardDisplayed',
+ DIFF_VIEW_CONTENT_DISPLAYED: 'DiffViewOnlyContent',
DIFF_VIEW_DISPLAYED: 'DiffViewDisplayed',
+ DIFF_VIEW_LOAD_FULL: 'DiffViewFullyLoaded',
FILE_LIST_DISPLAYED: 'FileListDisplayed',
PLUGINS_LOADED: 'PluginsLoaded',
STARTUP_CHANGE_DISPLAYED: 'StartupChangeDisplayed',
STARTUP_CHANGE_LOAD_FULL: 'StartupChangeFullyLoaded',
STARTUP_DASHBOARD_DISPLAYED: 'StartupDashboardDisplayed',
+ STARTUP_DIFF_VIEW_CONTENT_DISPLAYED: 'StartupDiffViewOnlyContent',
STARTUP_DIFF_VIEW_DISPLAYED: 'StartupDiffViewDisplayed',
+ STARTUP_DIFF_VIEW_LOAD_FULL: 'StartupDiffViewFullyLoaded',
STARTUP_FILE_LIST_DISPLAYED: 'StartupFileListDisplayed',
WEB_COMPONENTS_READY: 'WebComponentsReady',
+ METRICS_PLUGIN_LOADED: 'MetricsPluginLoaded',
};
const STARTUP_TIMERS = {};
STARTUP_TIMERS[TIMER.PLUGINS_LOADED] = 0;
+ STARTUP_TIMERS[TIMER.METRICS_PLUGIN_LOADED] = 0;
STARTUP_TIMERS[TIMER.STARTUP_CHANGE_DISPLAYED] = 0;
STARTUP_TIMERS[TIMER.STARTUP_CHANGE_LOAD_FULL] = 0;
STARTUP_TIMERS[TIMER.STARTUP_DASHBOARD_DISPLAYED] = 0;
+ STARTUP_TIMERS[TIMER.STARTUP_DIFF_VIEW_CONTENT_DISPLAYED] = 0;
STARTUP_TIMERS[TIMER.STARTUP_DIFF_VIEW_DISPLAYED] = 0;
+ STARTUP_TIMERS[TIMER.STARTUP_DIFF_VIEW_LOAD_FULL] = 0;
STARTUP_TIMERS[TIMER.STARTUP_FILE_LIST_DISPLAYED] = 0;
+ STARTUP_TIMERS[TIMING.APP_STARTED] = 0;
// WebComponentsReady timer is triggered from gr-router.
STARTUP_TIMERS[TIMER.WEB_COMPONENTS_READY] = 0;
@@ -106,6 +107,9 @@
const pending = [];
+ const loadedPlugins = [];
+ const detectedExtensions = [];
+
const onError = function(oldOnError, msg, url, line, column, error) {
if (oldOnError) {
oldOnError(msg, url, line, column, error);
@@ -113,7 +117,13 @@
if (error) {
line = line || error.lineNumber;
column = column || error.columnNumber;
- msg = msg || error.toString();
+ let shortenedErrorStack = msg;
+ if (error.stack) {
+ const errorStackLines = error.stack.split('\n');
+ shortenedErrorStack = errorStackLines.slice(0,
+ Math.min(3, errorStackLines.length)).join('\n');
+ }
+ msg = shortenedErrorStack || error.toString();
}
const payload = {
url,
@@ -138,13 +148,10 @@
};
catchErrors();
- GrJankDetector.start();
-
// The Polymer pass of JSCompiler requires this to be reassignable
// eslint-disable-next-line prefer-const
let GrReporting = Polymer({
is: 'gr-reporting',
- _legacyUndefinedCheck: true,
properties: {
category: String,
@@ -173,9 +180,15 @@
!this._baselines.hasOwnProperty(TIMER.PLUGINS_LOADED);
},
+ _isMetricsPluginLoaded() {
+ return this._arePluginsLoaded() || this._baselines &&
+ !this._baselines.hasOwnProperty(TIMER.METRICS_PLUGIN_LOADED);
+ },
+
reporter(...args) {
- const report = (this._arePluginsLoaded() && !pending.length) ?
+ const report = (this._isMetricsPluginLoaded() && !pending.length) ?
this.defaultReporter : this.cachingReporter;
+ args.splice(4, 0, loadedPlugins, detectedExtensions);
report.apply(this, args);
},
@@ -186,23 +199,33 @@
* @param {string} category
* @param {string} eventName
* @param {string|number} eventValue
+ * @param {Array} plugins
+ * @param {Array} extensions
* @param {boolean|undefined} opt_noLog If true, the event will not be
* logged to the JS console.
*/
- defaultReporter(type, category, eventName, eventValue, opt_noLog) {
+ defaultReporter(type, category, eventName, eventValue,
+ loadedPlugins, detectedExtensions, opt_noLog) {
const detail = {
type,
category,
name: eventName,
value: eventValue,
};
+ if (category === TIMING.CATEGORY_UI_LATENCY) {
+ detail.loadedPlugins = loadedPlugins;
+ detail.detectedExtensions = detectedExtensions;
+ }
document.dispatchEvent(new CustomEvent(type, {detail}));
if (opt_noLog) { return; }
if (type === ERROR.TYPE && category === ERROR.CATEGORY) {
- console.error(eventValue.error || eventName);
+ console.error(eventValue && eventValue.error || eventName);
} else {
- console.log(eventName + (eventValue !== undefined ?
- (': ' + eventValue) : ''));
+ if (eventValue !== undefined) {
+ console.log(`Reporting: ${eventName}: ${eventValue}`);
+ } else {
+ console.log(`Reporting: ${eventName}`);
+ }
}
},
@@ -214,22 +237,27 @@
* @param {string} category
* @param {string} eventName
* @param {string|number} eventValue
+ * @param {Array} plugins
+ * @param {Array} extensions
* @param {boolean|undefined} opt_noLog If true, the event will not be
* logged to the JS console.
*/
- cachingReporter(type, category, eventName, eventValue, opt_noLog) {
+ cachingReporter(type, category, eventName, eventValue,
+ plugins, extensions, opt_noLog) {
if (type === ERROR.TYPE && category === ERROR.CATEGORY) {
- console.error(eventValue.error || eventName);
+ console.error(eventValue && eventValue.error || eventName);
}
- if (this._arePluginsLoaded()) {
+ if (this._isMetricsPluginLoaded()) {
if (pending.length) {
for (const args of pending.splice(0)) {
this.reporter(...args);
}
}
- this.reporter(type, category, eventName, eventValue, opt_noLog);
+ this.reporter(type, category, eventName, eventValue,
+ plugins, extensions, opt_noLog);
} else {
- pending.push([type, category, eventName, eventValue, opt_noLog]);
+ pending.push([type, category, eventName, eventValue,
+ plugins, extensions, opt_noLog]);
}
},
@@ -237,8 +265,7 @@
* User-perceived app start time, should be reported when the app is ready.
*/
appStarted(hidden) {
- this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
- TIMING.APP_STARTED, this.now());
+ this.timeEnd(TIMING.APP_STARTED);
if (hidden) {
this.reporter(PAGE_VISIBILITY.TYPE, PAGE_VISIBILITY.CATEGORY,
PAGE_VISIBILITY.STARTED_HIDDEN);
@@ -261,18 +288,15 @@
},
beforeLocationChanged() {
- if (GrJankDetector.jank > 0) {
- this.reporter(
- JANK.TYPE, JANK.CATEGORY, JANK.COUNT, GrJankDetector.jank);
- GrJankDetector.jank = 0;
- }
for (const prop of Object.keys(this._baselines)) {
delete this._baselines[prop];
}
this.time(TIMER.CHANGE_DISPLAYED);
this.time(TIMER.CHANGE_LOAD_FULL);
this.time(TIMER.DASHBOARD_DISPLAYED);
+ this.time(TIMER.DIFF_VIEW_CONTENT_DISPLAYED);
this.time(TIMER.DIFF_VIEW_DISPLAYED);
+ this.time(TIMER.DIFF_VIEW_LOAD_FULL);
this.time(TIMER.FILE_LIST_DISPLAYED);
},
@@ -313,6 +337,23 @@
}
},
+ diffViewFullyLoaded() {
+ if (this._baselines.hasOwnProperty(TIMER.STARTUP_DIFF_VIEW_LOAD_FULL)) {
+ this.timeEnd(TIMER.STARTUP_DIFF_VIEW_LOAD_FULL);
+ } else {
+ this.timeEnd(TIMER.DIFF_VIEW_LOAD_FULL);
+ }
+ },
+
+ diffViewContentDisplayed() {
+ if (this._baselines.hasOwnProperty(
+ TIMER.STARTUP_DIFF_VIEW_CONTENT_DISPLAYED)) {
+ this.timeEnd(TIMER.STARTUP_DIFF_VIEW_CONTENT_DISPLAYED);
+ } else {
+ this.timeEnd(TIMER.DIFF_VIEW_CONTENT_DISPLAYED);
+ }
+ },
+
fileListDisplayed() {
if (this._baselines.hasOwnProperty(TIMER.STARTUP_FILE_LIST_DISPLAYED)) {
this.timeEnd(TIMER.STARTUP_FILE_LIST_DISPLAYED);
@@ -323,6 +364,16 @@
reportExtension(name) {
this.reporter(EXTENSION.TYPE, EXTENSION.DETECTED, name);
+ if (!detectedExtensions.includes(name)) {
+ detectedExtensions.push(name);
+ }
+ },
+
+ pluginLoaded(name) {
+ if (name.startsWith('metrics-')) {
+ this.timeEnd(TIMER.METRICS_PLUGIN_LOADED);
+ }
+ loadedPlugins.push(name);
},
pluginsLoaded(pluginsList) {
@@ -336,6 +387,7 @@
*/
time(name) {
this._baselines[name] = this.now();
+ window.performance.mark(`${name}-start`);
},
/**
@@ -344,8 +396,18 @@
timeEnd(name) {
if (!this._baselines.hasOwnProperty(name)) { return; }
const baseTime = this._baselines[name];
- this._reportTiming(name, this.now() - baseTime);
delete this._baselines[name];
+ this._reportTiming(name, this.now() - baseTime);
+
+ // Finalize the interval. Either from a registered start mark or
+ // the navigation start time (if baseTime is 0).
+ if (baseTime !== 0) {
+ window.performance.measure(name, `${name}-start`);
+ } else {
+ // Microsft Edge does not handle the 2nd param correctly
+ // (if undefined).
+ window.performance.measure(name);
+ }
},
/**
@@ -465,7 +527,7 @@
reportErrorDialog(message) {
this.reporter(ERROR_DIALOG.TYPE, ERROR_DIALOG.CATEGORY,
- 'ErrorDialog: ' + message);
+ 'ErrorDialog: ' + message, {error: new Error(message)});
},
});
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
index 29e70ea74e..4c561a2c91 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-reporting</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-reporting.html">
@@ -93,11 +95,7 @@ limitations under the License.
test('beforeLocationChanged', () => {
element._baselines['garbage'] = 'monster';
sandbox.stub(element, 'time');
- GrJankDetector.jank = 42;
element.beforeLocationChanged();
- assert.equal(GrJankDetector.jank, 0);
- assert.isTrue(element.reporter.calledWithExactly(
- 'lifecycle', 'UI Latency', 'Jank count', 42));
assert.isTrue(element.time.calledWithExactly('DashboardDisplayed'));
assert.isTrue(element.time.calledWithExactly('ChangeDisplayed'));
assert.isTrue(element.time.calledWithExactly('ChangeFullyLoaded'));
@@ -193,7 +191,6 @@ limitations under the License.
assert.isTrue(element.reporter.calledOnce);
assert.throws(() => {
timer.end();
- done();
}, 'Timer for "foo-bar" already ended.');
});
@@ -263,8 +260,8 @@ limitations under the License.
test('pluginsLoaded reports time', () => {
sandbox.stub(element, 'now').returns(42);
element.pluginsLoaded();
- assert.isTrue(element.defaultReporter.calledWithExactly(
- 'timing-report', 'UI Latency', 'PluginsLoaded', 42, undefined
+ assert.isTrue(element.defaultReporter.calledWith(
+ 'timing-report', 'UI Latency', 'PluginsLoaded', 42
));
});
@@ -285,6 +282,24 @@ limitations under the License.
assert.isTrue(element.defaultReporter.called);
});
+ test('reports plugins in timing events', () => {
+ element.pluginsLoaded = [];
+ sandbox.stub(element, 'now').returns(42);
+ element.pluginLoaded('metrics-xyz1');
+ // element.pluginLoaded('foo');
+ element.time('timeAction');
+ element.timeEnd('timeAction');
+ assert.isTrue(element.defaultReporter.getCall(1).calledWith(
+ 'timing-report', 'UI Latency', 'timeAction', 0,
+ ['metrics-xyz1']
+ ));
+ });
+
+ test('reports if metrics plugin xyz is loaded', () => {
+ element.pluginLoaded('metrics-xyz');
+ assert.isTrue(element.defaultReporter.called);
+ });
+
test('reports cached events preserving order', () => {
element.time('foo');
element.time('bar');
@@ -334,6 +349,7 @@ limitations under the License.
test('is reported', () => {
const error = new Error('bar');
+ error.stack = undefined;
emulateThrow('bar', 'http://url', 4, 2, error);
assert.isTrue(reporter.calledWith('error', 'exception', 'bar'));
const payload = reporter.lastCall.args[3];
@@ -345,6 +361,15 @@ limitations under the License.
});
});
+ test('is reported with 3 lines of stack', () => {
+ const error = new Error('bar');
+ emulateThrow('bar', 'http://url', 4, 2, error);
+ const expectedStack = error.stack.split('\n').slice(0, 3)
+ .join('\n');
+ assert.isTrue(reporter.calledWith('error', 'exception',
+ expectedStack));
+ });
+
test('prevent default event handler', () => {
assert.isTrue(emulateThrow());
});
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.html b/polygerrit-ui/app/elements/core/gr-router/gr-router.html
index 68ddef626d..71a58321a7 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.html
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.html
@@ -14,9 +14,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
@@ -28,6 +29,6 @@ limitations under the License.
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
<gr-reporting id="reporting"></gr-reporting>
</template>
- <script src="../../../bower_components/page/page.js"></script>
+ <script src="/bower_components/page/page.js"></script>
<script src="gr-router.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index 2e1f5b8369..738a89b727 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -200,7 +200,9 @@
const reporting = document.createElement('gr-reporting');
window.addEventListener('load', () => {
- reporting.pageLoaded();
+ setTimeout(() => {
+ reporting.pageLoaded();
+ }, 0);
});
window.addEventListener('WebComponentsReady', () => {
@@ -210,7 +212,6 @@
Polymer({
is: 'gr-router',
- _legacyUndefinedCheck: true,
properties: {
_app: {
@@ -228,6 +229,7 @@
behaviors: [
Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
Gerrit.PatchSetBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -238,7 +240,17 @@
},
_setParams(params) {
- this._app.params = params;
+ this._appElement().params = params;
+ },
+
+ _appElement() {
+ // In Polymer2 you have to reach through the shadow root of the app
+ // element. This obviously breaks encapsulation.
+ // TODO(brohlfs): Make this more elegant, e.g. by exposing app-element
+ // explicitly in app, or by delegating to it.
+ return document.getElementById('app-element') ||
+ document.getElementById('app').shadowRoot.getElementById(
+ 'app-element');
},
_redirect(url) {
@@ -1404,9 +1416,7 @@
}
},
- // TODO fix this so it properly redirects
- // to /settings#Agreements (Scrolls down)
- _handleAgreementsRoute(data) {
+ _handleAgreementsRoute() {
this._redirect('/settings/#Agreements');
},
@@ -1505,7 +1515,7 @@
// Note: the app's 404 display is tightly-coupled with catching 404
// network responses, so we simulate a 404 response status to display it.
// TODO: Decouple the gr-app error view from network responses.
- this._app.dispatchEvent(new CustomEvent('page-error',
+ this._appElement().dispatchEvent(new CustomEvent('page-error',
{detail: {response: {status: 404}}}));
},
});
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
index 168dc289db..5c7addc563 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-router</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-router.html">
@@ -671,11 +673,12 @@ limitations under the License.
});
test('_handleDefaultRoute on first load', () => {
- element._app = {dispatchEvent: sinon.stub()};
+ const appElementStub = {dispatchEvent: sinon.stub()};
+ element._appElement = () => appElementStub;
element._handleDefaultRoute();
- assert.isTrue(element._app.dispatchEvent.calledOnce);
+ assert.isTrue(appElementStub.dispatchEvent.calledOnce);
assert.equal(
- element._app.dispatchEvent.lastCall.args[0].detail.response.status,
+ appElementStub.dispatchEvent.lastCall.args[0].detail.response.status,
404);
});
@@ -691,7 +694,8 @@ limitations under the License.
sandbox.stub(window, 'page');
element._startRouter();
- element._app = {dispatchEvent: sinon.stub()};
+ const appElementStub = {dispatchEvent: sinon.stub()};
+ element._appElement = () => appElementStub;
element._handleDefaultRoute();
onExit('', () => {}); // we left page;
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
index 3a48213bdb..0cdef8ca76 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
@@ -17,7 +17,7 @@ limitations under the License.
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -30,11 +30,10 @@ limitations under the License.
gr-autocomplete {
background-color: var(--view-background-color);
border: 1px solid var(--border-color);
- border-radius: 2px;
+ border-radius: var(--border-radius);
flex: 1;
- font: inherit;
outline: none;
- padding: .25em;
+ padding: var(--spacing-xs);
}
</style>
<form>
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
index c877ac412d..0030babe97 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
@@ -60,7 +60,6 @@
'is:merged',
'is:open',
'is:owner',
- 'is:pending',
'is:private',
'is:reviewed',
'is:reviewer',
@@ -90,7 +89,6 @@
'status:closed',
'status:merged',
'status:open',
- 'status:pending',
'status:reviewed',
'topic:',
'tr:',
@@ -106,7 +104,6 @@
Polymer({
is: 'gr-search-bar',
- _legacyUndefinedCheck: true,
/**
* Fired when a search is committed
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
index b162828ec9..a4927c3c07 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-search-bar</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<link rel="import" href="gr-search-bar.html">
<script src="../../../scripts/util.js"></script>
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html
index 4c98068b5e..c4ae41b2fa 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html
@@ -14,9 +14,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
-<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
+<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../gr-search-bar/gr-search-bar.html">
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
index 65141aa4b4..2446486fcb 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
@@ -23,7 +23,6 @@
Polymer({
is: 'gr-smart-search',
- _legacyUndefinedCheck: true,
properties: {
searchQuery: String,
@@ -49,7 +48,7 @@
},
behaviors: [
- Gerrit.AnonymousNameBehavior,
+ Gerrit.DisplayNameBehavior,
],
attached() {
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html
index af0fc3cb07..a70eb7c3e3 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-smart-search</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-smart-search.html">
diff --git a/polygerrit-ui/app/elements/custom-dark-theme_test.html b/polygerrit-ui/app/elements/custom-dark-theme_test.html
new file mode 100644
index 0000000000..4cf35f16a2
--- /dev/null
+++ b/polygerrit-ui/app/elements/custom-dark-theme_test.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-app-it_test</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../test/common-test-setup.html"/>
+<link rel="import" href="./gr-app.html">
+
+<script>void(0);</script>
+
+<test-fixture id="element">
+ <template>
+ <gr-app id="app"></gr-app>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-app custom dark theme tests', () => {
+ let sandbox;
+ let element;
+
+ setup(done => {
+ sandbox = sinon.sandbox.create();
+ stub('gr-reporting', {
+ appStarted: sandbox.stub(),
+ });
+ stub('gr-account-dropdown', {
+ _getTopContent: sinon.stub(),
+ });
+ stub('gr-rest-api-interface', {
+ getAccount() { return Promise.resolve(null); },
+ getAccountCapabilities() { return Promise.resolve({}); },
+ getConfig() {
+ return Promise.resolve({
+ plugin: {
+ js_resource_paths: [],
+ html_resource_paths: [
+ new URL('test/plugin.html', window.location.href).toString(),
+ ],
+ },
+ });
+ },
+ getVersion() { return Promise.resolve(42); },
+ getLoggedIn() { return Promise.resolve(false); },
+ });
+
+ window.localStorage.setItem('dark-theme', 'true');
+
+ element = fixture('element');
+
+ const importSpy = sandbox.spy(
+ element.$['app-element'].$.externalStyleForAll,
+ '_import');
+ const importForThemeSpy = sandbox.spy(
+ element.$['app-element'].$.externalStyleForTheme,
+ '_import');
+ Gerrit.awaitPluginsLoaded().then(() => {
+ Promise.all(importSpy.returnValues.concat(importForThemeSpy.returnValues))
+ .then(() => {
+ flush(done);
+ });
+ });
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('applies the right theme', () => {
+ assert.equal(
+ util.getComputedStyleValue('--primary-text-color', element),
+ 'red');
+ assert.equal(
+ util.getComputedStyleValue('--header-background-color', element),
+ 'black');
+ assert.equal(
+ util.getComputedStyleValue('--footer-background-color', element),
+ 'yellow');
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/gr-app-it_test.html b/polygerrit-ui/app/elements/custom-light-theme_test.html
index 2601aebe14..e346af53bc 100644
--- a/polygerrit-ui/app/elements/gr-app-it_test.html
+++ b/polygerrit-ui/app/elements/custom-light-theme_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-app-it_test</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../test/common-test-setup.html"/>
<link rel="import" href="gr-app.html">
@@ -33,7 +35,7 @@ limitations under the License.
</test-fixture>
<script>
- suite('gr-app integration tests', () => {
+ suite('gr-app custom light theme tests', () => {
let sandbox;
let element;
@@ -61,13 +63,22 @@ limitations under the License.
getVersion() { return Promise.resolve(42); },
getLoggedIn() { return Promise.resolve(false); },
});
+
+ window.localStorage.removeItem('dark-theme');
+
element = fixture('element');
- const importSpy = sandbox.spy(element.$.externalStyle, '_import');
+ const importSpy = sandbox.spy(
+ element.$['app-element'].$.externalStyleForAll,
+ '_import');
+ const importForThemeSpy = sandbox.spy(
+ element.$['app-element'].$.externalStyleForTheme,
+ '_import');
Gerrit.awaitPluginsLoaded().then(() => {
- Promise.all(importSpy.returnValues).then(() => {
- flush(done);
- });
+ Promise.all(importSpy.returnValues.concat(importForThemeSpy.returnValues))
+ .then(() => {
+ flush(done);
+ });
});
});
@@ -75,17 +86,15 @@ limitations under the License.
sandbox.restore();
});
- test('applies --primary-text-color', () => {
+ test('applies the right theme', () => {
assert.equal(
- element.getComputedStyleValue('--primary-text-color'), '#F00BAA');
- });
-
- test('applies --header-background-color', () => {
- assert.equal(element.getComputedStyleValue('--header-background-color'),
+ util.getComputedStyleValue('--primary-text-color', element),
+ '#F00BAA');
+ assert.equal(
+ util.getComputedStyleValue('--header-background-color', element),
'#F01BAA');
- });
- test('applies --footer-background-color', () => {
- assert.equal(element.getComputedStyleValue('--footer-background-color'),
+ assert.equal(
+ util.getComputedStyleValue('--footer-background-color', element),
'#F02BAA');
});
});
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
index 7bf71f5c66..b7994e6078 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
@@ -19,7 +19,6 @@
Polymer({
is: 'comment-api-mock',
- _legacyUndefinedCheck: true,
properties: {
_changeComments: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.html b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.html
index c31bd11660..317e9e5c73 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.html
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
index 37491d265a..1e8158da19 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
@@ -19,35 +19,6 @@
const PARENT = 'PARENT';
- const Defs = {};
-
- /**
- * @typedef {{
- * basePatchNum: (string|number),
- * patchNum: (number),
- * }}
- */
- Defs.patchRange;
-
- /**
- * @typedef {{
- * changeNum: number,
- * path: string,
- * patchRange: !Defs.patchRange,
- * projectConfig: (Object|undefined),
- * }}
- */
- Defs.commentMeta;
-
- /**
- * @typedef {{
- * meta: !Defs.commentMeta,
- * left: !Array,
- * right: !Array,
- * }}
- */
- Defs.commentsBySide;
-
/**
* Construct a change comments object, which can be data-bound to child
* elements of that which uses the gr-comment-api.
@@ -92,7 +63,7 @@
* Paths with comments are mapped to true, whereas paths without comments
* are not mapped.
*
- * @param {Defs.patchRange=} opt_patchRange The patch-range object containing
+ * @param {Gerrit.PatchRange=} opt_patchRange The patch-range object containing
* patchNum and basePatchNum properties to represent the range.
* @return {!Object}
*/
@@ -251,17 +222,26 @@
* arrays of comments in on either side of the patch range for that path.
*
* @param {!string} path
- * @param {!Defs.patchRange} patchRange The patch-range object containing patchNum
+ * @param {!Gerrit.PatchRange} patchRange The patch-range object containing patchNum
* and basePatchNum properties to represent the range.
* @param {Object=} opt_projectConfig Optional project config object to
* include in the meta sub-object.
- * @return {!Defs.commentsBySide}
+ * @return {!Gerrit.CommentsBySide}
*/
ChangeComments.prototype.getCommentsBySideForPath = function(path,
patchRange, opt_projectConfig) {
- const comments = this.comments[path] || [];
- const drafts = this.drafts[path] || [];
- const robotComments = this.robotComments[path] || [];
+ let comments = [];
+ let drafts = [];
+ let robotComments = [];
+ if (this.comments && this.comments[path]) {
+ comments = this.comments[path];
+ }
+ if (this.drafts && this.drafts[path]) {
+ drafts = this.drafts[path];
+ }
+ if (this.robotComments && this.robotComments[path]) {
+ robotComments = this.robotComments[path];
+ }
drafts.forEach(d => { d.__draft = true; });
@@ -430,7 +410,7 @@
* given patch range.
*
* @param {!Object} comment
- * @param {!Defs.patchRange} range
+ * @param {!Gerrit.PatchRange} range
* @return {boolean}
*/
ChangeComments.prototype._isInBaseOfPatchRange = function(comment, range) {
@@ -462,7 +442,7 @@
* given patch range.
*
* @param {!Object} comment
- * @param {!Defs.patchRange} range
+ * @param {!Gerrit.PatchRange} range
* @return {boolean}
*/
ChangeComments.prototype._isInRevisionOfPatchRange = function(comment,
@@ -475,7 +455,7 @@
* Whether the given comment should be included in the given patch range.
*
* @param {!Object} comment
- * @param {!Defs.patchRange} range
+ * @param {!Gerrit.PatchRange} range
* @return {boolean|undefined}
*/
ChangeComments.prototype._isInPatchRange = function(comment, range) {
@@ -485,7 +465,6 @@
Polymer({
is: 'gr-comment-api',
- _legacyUndefinedCheck: true,
properties: {
_changeComments: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html
index 1ca2a69349..47181f900f 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-comment-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="./gr-comment-api.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.html b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.html
index 56a6fb9b1a..549bf4318c 100644
--- a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.html
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.html
@@ -15,10 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-coverage-layer">
<template>
</template>
+ <script src="../../../types/types.js"></script>
<script src="../gr-diff-highlight/gr-annotation.js"></script>
<script src="gr-coverage-layer.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js
index e8d6900c7d..3d9c17221a 100644
--- a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js
@@ -17,27 +17,6 @@
(function() {
'use strict';
- /** @enum {string} */
- Gerrit.CoverageType = {
- /**
- * start_character and end_character of the range will be ignored for this
- * type.
- */
- COVERED: 'COVERED',
- /**
- * start_character and end_character of the range will be ignored for this
- * type.
- */
- NOT_COVERED: 'NOT_COVERED',
- PARTIALLY_COVERED: 'PARTIALLY_COVERED',
- /**
- * You don't have to use this. If there is no coverage information for a
- * range, then it implicitly means NOT_INSTRUMENTED. start_character and
- * end_character of the range will be ignored for this type.
- */
- NOT_INSTRUMENTED: 'NOT_INSTRUMENTED',
- };
-
const TOOLTIP_MAP = new Map([
[Gerrit.CoverageType.COVERED, 'Covered by tests.'],
[Gerrit.CoverageType.NOT_COVERED, 'Not covered by tests.'],
@@ -45,15 +24,6 @@
[Gerrit.CoverageType.NOT_INSTRUMENTED, 'Not instrumented by any tests.'],
]);
- /**
- * @typedef {{
- * side: string,
- * type: Gerrit.CoverageType,
- * code_range: Gerrit.Range,
- * }}
- */
- Gerrit.CoverageRange;
-
Polymer({
is: 'gr-coverage-layer',
diff --git a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html
index edd88a24e1..45a67e1261 100644
--- a/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-coverage-layer</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<script src="../gr-diff/gr-diff-line.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
index 11bea8c820..283b7fdb1e 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
@@ -95,7 +95,7 @@
image._width = imageEl.naturalWidth;
this._updateImageLabel(section, className, image);
}.bind(this);
- imageEl.src = 'data:' + image.type + ';base64, ' + image.body;
+ imageEl.setAttribute('src', `data:${image.type};base64, ${image.body}`);
imageEl.addEventListener('error', () => {
imageEl.remove();
td.textContent = '[Image failed to load]';
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.js
index 1ef278f99d..bb590ba5f4 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.js
@@ -35,6 +35,9 @@
if (group.dueToRebase) {
sectionEl.classList.add('dueToRebase');
}
+ if (group.ignoredWhitespaceOnly) {
+ sectionEl.classList.add('ignoredWhitespaceOnly');
+ }
const pairs = group.getSideBySidePairs();
for (let i = 0; i < pairs.length; i++) {
sectionEl.appendChild(this._createRow(sectionEl, pairs[i].left,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.js
index 6be209799e..144cc5605f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.js
@@ -35,9 +35,18 @@
if (group.dueToRebase) {
sectionEl.classList.add('dueToRebase');
}
+ if (group.ignoredWhitespaceOnly) {
+ sectionEl.classList.add('ignoredWhitespaceOnly');
+ }
for (let i = 0; i < group.lines.length; ++i) {
- sectionEl.appendChild(this._createRow(sectionEl, group.lines[i]));
+ const line = group.lines[i];
+ // If only whitespace has changed and the settings ask for whitespace to
+ // be ignored, only render the right-side line in unified diff mode.
+ if (group.ignoredWhitespaceOnly && line.type == GrDiffLine.Type.REMOVE) {
+ continue;
+ }
+ sectionEl.appendChild(this._createRow(sectionEl, line));
}
return sectionEl;
};
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.html
new file mode 100644
index 0000000000..19e017d565
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.html
@@ -0,0 +1,205 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>GrDiffBuilderUnified</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<script src="../../../scripts/util.js"></script>
+<script src="../gr-diff/gr-diff-line.js"></script>
+<script src="../gr-diff/gr-diff-group.js"></script>
+<script src="../gr-diff-highlight/gr-annotation.js"></script>
+<script src="gr-diff-builder.js"></script>
+<script src="gr-diff-builder-unified.js"></script>
+
+<script>void(0);</script>
+
+<script>
+ suite('GrDiffBuilderUnified tests', () => {
+ let prefs;
+ let outputEl;
+ let diffBuilder;
+
+ setup(()=> {
+ prefs = {
+ line_length: 10,
+ show_tabs: true,
+ tab_size: 4,
+ };
+ outputEl = document.createElement('div');
+ diffBuilder = new GrDiffBuilderUnified({}, prefs, outputEl, []);
+ });
+
+ suite('buildSectionElement for BOTH group', () => {
+ let lines;
+ let group;
+
+ setup(() => {
+ lines = [
+ new GrDiffLine(GrDiffLine.Type.BOTH, 1, 2),
+ new GrDiffLine(GrDiffLine.Type.BOTH, 2, 3),
+ new GrDiffLine(GrDiffLine.Type.BOTH, 3, 4),
+ ];
+ lines[0].text = 'def hello_world():';
+ lines[1].text = ' print "Hello World";';
+ lines[2].text = ' return True';
+
+ group = new GrDiffGroup(GrDiffGroup.Type.BOTH, lines);
+ });
+
+ test('creates the section', () => {
+ const sectionEl = diffBuilder.buildSectionElement(group);
+ assert.isTrue(sectionEl.classList.contains('section'));
+ assert.isTrue(sectionEl.classList.contains('both'));
+ });
+
+ test('creates each unchanged row once', () => {
+ const sectionEl = diffBuilder.buildSectionElement(group);
+ const rowEls = sectionEl.querySelectorAll('.diff-row');
+
+ assert.equal(rowEls.length, 3);
+
+ assert.equal(
+ rowEls[0].querySelector('.lineNum.left').textContent,
+ lines[0].beforeNumber);
+ assert.equal(
+ rowEls[0].querySelector('.lineNum.right').textContent,
+ lines[0].afterNumber);
+ assert.equal(
+ rowEls[0].querySelector('.content').textContent, lines[0].text);
+
+ assert.equal(
+ rowEls[1].querySelector('.lineNum.left').textContent,
+ lines[1].beforeNumber);
+ assert.equal(
+ rowEls[1].querySelector('.lineNum.right').textContent,
+ lines[1].afterNumber);
+ assert.equal(
+ rowEls[1].querySelector('.content').textContent, lines[1].text);
+
+ assert.equal(
+ rowEls[2].querySelector('.lineNum.left').textContent,
+ lines[2].beforeNumber);
+ assert.equal(
+ rowEls[2].querySelector('.lineNum.right').textContent,
+ lines[2].afterNumber);
+ assert.equal(
+ rowEls[2].querySelector('.content').textContent, lines[2].text);
+ });
+ });
+
+ suite('buildSectionElement for DELTA group', () => {
+ let lines;
+ let group;
+
+ setup(() => {
+ lines = [
+ new GrDiffLine(GrDiffLine.Type.REMOVE, 1),
+ new GrDiffLine(GrDiffLine.Type.REMOVE, 2),
+ new GrDiffLine(GrDiffLine.Type.ADD, 2),
+ new GrDiffLine(GrDiffLine.Type.ADD, 3),
+ ];
+ lines[0].text = 'def hello_world():';
+ lines[1].text = ' print "Hello World"';
+ lines[2].text = 'def hello_universe()';
+ lines[3].text = ' print "Hello Universe"';
+
+ group = new GrDiffGroup(GrDiffGroup.Type.DELTA, lines);
+ });
+
+ test('creates the section', () => {
+ const sectionEl = diffBuilder.buildSectionElement(group);
+ assert.isTrue(sectionEl.classList.contains('section'));
+ assert.isTrue(sectionEl.classList.contains('delta'));
+ });
+
+ test('creates the section with class if ignoredWhitespaceOnly', () => {
+ group.ignoredWhitespaceOnly = true;
+ const sectionEl = diffBuilder.buildSectionElement(group);
+ assert.isTrue(sectionEl.classList.contains('ignoredWhitespaceOnly'));
+ });
+
+ test('creates the section with class if dueToRebase', () => {
+ group.dueToRebase = true;
+ const sectionEl = diffBuilder.buildSectionElement(group);
+ assert.isTrue(sectionEl.classList.contains('dueToRebase'));
+ });
+
+ test('creates first the removed and then the added rows', () => {
+ const sectionEl = diffBuilder.buildSectionElement(group);
+ const rowEls = sectionEl.querySelectorAll('.diff-row');
+
+ assert.equal(rowEls.length, 4);
+
+ assert.equal(
+ rowEls[0].querySelector('.lineNum.left').textContent,
+ lines[0].beforeNumber);
+ assert.isNotOk(rowEls[0].querySelector('.lineNum.right'));
+ assert.equal(
+ rowEls[0].querySelector('.content').textContent, lines[0].text);
+
+ assert.equal(
+ rowEls[1].querySelector('.lineNum.left').textContent,
+ lines[1].beforeNumber);
+ assert.isNotOk(rowEls[1].querySelector('.lineNum.right'));
+ assert.equal(
+ rowEls[1].querySelector('.content').textContent, lines[1].text);
+
+ assert.isNotOk(rowEls[2].querySelector('.lineNum.left'));
+ assert.equal(
+ rowEls[2].querySelector('.lineNum.right').textContent,
+ lines[2].afterNumber);
+ assert.equal(
+ rowEls[2].querySelector('.content').textContent, lines[2].text);
+
+ assert.isNotOk(rowEls[3].querySelector('.lineNum.left'));
+ assert.equal(
+ rowEls[3].querySelector('.lineNum.right').textContent,
+ lines[3].afterNumber);
+ assert.equal(
+ rowEls[3].querySelector('.content').textContent, lines[3].text);
+ });
+
+ test('creates only the added rows if only ignored whitespace', () => {
+ group.ignoredWhitespaceOnly = true;
+ const sectionEl = diffBuilder.buildSectionElement(group);
+ const rowEls = sectionEl.querySelectorAll('.diff-row');
+
+ assert.equal(rowEls.length, 2);
+
+ assert.isNotOk(rowEls[0].querySelector('.lineNum.left'));
+ assert.equal(
+ rowEls[0].querySelector('.lineNum.right').textContent,
+ lines[2].afterNumber);
+ assert.equal(
+ rowEls[0].querySelector('.content').textContent, lines[2].text);
+
+ assert.isNotOk(rowEls[1].querySelector('.lineNum.left'));
+ assert.equal(
+ rowEls[1].querySelector('.lineNum.right').textContent,
+ lines[3].afterNumber);
+ assert.equal(
+ rowEls[1].querySelector('.content').textContent, lines[3].text);
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index d42d8c1fd6..40fbe3c7e4 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -14,12 +14,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../gr-coverage-layer/gr-coverage-layer.html">
<link rel="import" href="../gr-diff-processor/gr-diff-processor.html">
<link rel="import" href="../gr-ranged-comment-layer/gr-ranged-comment-layer.html">
-<link rel="import" href="../gr-syntax-layer/gr-syntax-layer.html">
<dom-module id="gr-diff-builder">
<template>
@@ -29,9 +28,6 @@ limitations under the License.
<gr-ranged-comment-layer
id="rangeLayer"
comment-ranges="[[commentRanges]]"></gr-ranged-comment-layer>
- <gr-syntax-layer
- id="syntaxLayer"
- diff="[[diff]]"></gr-syntax-layer>
<gr-coverage-layer
id="coverageLayerLeft"
coverage-ranges="[[_leftCoverageRanges]]"
@@ -43,7 +39,6 @@ limitations under the License.
<gr-diff-processor
id="processor"
groups="{{_groups}}"></gr-diff-processor>
- <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
</template>
<script src="../../../scripts/util.js"></script>
<script src="../gr-diff/gr-diff-line.js"></script>
@@ -63,18 +58,10 @@ limitations under the License.
UNIFIED: 'UNIFIED_DIFF',
};
- // If any line of the diff is more than the character limit, then disable
- // syntax highlighting for the entire file.
- const SYNTAX_MAX_LINE_LENGTH = 500;
-
- // Disable syntax highlighting if the overall diff is too large.
- const SYNTAX_MAX_DIFF_LENGTH = 20000;
-
const TRAILING_WHITESPACE_PATTERN = /\s+$/;
Polymer({
is: 'gr-diff-builder',
- _legacyUndefinedCheck: true,
/**
* Fired when the diff begins rendering.
@@ -83,21 +70,13 @@ limitations under the License.
*/
/**
- * Fired when the diff finishes rendering text content and starts
- * syntax highlighting.
+ * Fired when the diff finishes rendering text content.
*
* @event render-content
*/
- /**
- * Fired when the diff finishes syntax highlighting.
- *
- * @event render-syntax
- */
-
properties: {
diff: Object,
- diffPath: String,
changeNum: String,
patchNum: String,
viewMode: String,
@@ -138,8 +117,16 @@ limitations under the License.
* @type {?Object}
*/
_cancelableRenderPromise: Object,
+ layers: {
+ type: Array,
+ value: [],
+ },
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
get diffElement() {
return this.queryEffectiveChildren('#diffTable');
},
@@ -163,11 +150,10 @@ limitations under the License.
// attached before plugins are installed.
this._setupAnnotationLayers();
- this.$.syntaxLayer.enabled = prefs.syntax_highlighting;
this._showTabs = !!prefs.show_tabs;
this._showTrailingWhitespace = !!prefs.show_whitespace_errors;
- // Stop the processor and syntax layer (if they're running).
+ // Stop the processor if it's running.
this.cancel();
this._builder = this._getDiffBuilder(this.diff, prefs);
@@ -180,7 +166,8 @@ limitations under the License.
const isBinary = !!(this.isImageDiff || this.diff.binary);
- this.dispatchEvent(new CustomEvent('render-start', {bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'render-start', {bubbles: true, composed: true}));
this._cancelableRenderPromise = util.makeCancelable(
this.$.processor.process(this.diff.content, isBinary)
.then(() => {
@@ -188,17 +175,7 @@ limitations under the License.
this._builder.renderDiff();
}
this.dispatchEvent(new CustomEvent('render-content',
- {bubbles: true}));
-
- if (this._diffTooLargeForSyntax()) {
- this.$.syntaxLayer.enabled = false;
- }
-
- return this.$.syntaxLayer.process();
- })
- .then(() => {
- this.dispatchEvent(
- new CustomEvent('render-syntax', {bubbles: true}));
+ {bubbles: true, composed: true}));
}));
return this._cancelableRenderPromise
.finally(() => { this._cancelableRenderPromise = null; })
@@ -211,7 +188,6 @@ limitations under the License.
_setupAnnotationLayers() {
const layers = [
this._createTrailingWhitespaceLayer(),
- this.$.syntaxLayer,
this._createIntralineLayer(),
this._createTabIndicatorLayer(),
this.$.rangeLayer,
@@ -219,12 +195,9 @@ limitations under the License.
this.$.coverageLayerRight,
];
- // Get layers from plugins (if any).
- for (const pluginLayer of this.$.jsAPI.getDiffLayers(
- this.diffPath, this.changeNum, this.patchNum)) {
- layers.push(pluginLayer);
+ if (this.layers) {
+ layers.push(...this.layers);
}
-
this._layers = layers;
},
@@ -289,7 +262,7 @@ limitations under the License.
const contextIndex = groups.findIndex(group =>
group.element === sectionEl
);
- groups.splice(...[contextIndex, 1].concat(newGroups));
+ groups.splice(contextIndex, 1, ...newGroups);
for (const newGroup of newGroups) {
this._builder.emitGroup(newGroup, sectionEl);
@@ -301,7 +274,6 @@ limitations under the License.
cancel() {
this.$.processor.cancel();
- this.$.syntaxLayer.cancel();
if (this._cancelableRenderPromise) {
this._cancelableRenderPromise.cancel();
this._cancelableRenderPromise = null;
@@ -314,7 +286,7 @@ limitations under the License.
this.dispatchEvent(new CustomEvent('show-alert', {
detail: {
message,
- }, bubbles: true}));
+ }, bubbles: true, composed: true}));
throw Error(`Invalid preference value: ${pref}`);
},
@@ -440,45 +412,10 @@ limitations under the License.
};
},
- /**
- * @return {boolean} whether any of the lines in _groups are longer
- * than SYNTAX_MAX_LINE_LENGTH.
- */
- _anyLineTooLong() {
- return this._groups.reduce((acc, group) => {
- return acc || group.lines.reduce((acc, line) => {
- return acc || line.text.length >= SYNTAX_MAX_LINE_LENGTH;
- }, false);
- }, false);
- },
-
- _diffTooLargeForSyntax() {
- return this._anyLineTooLong() ||
- this.getDiffLength() > SYNTAX_MAX_DIFF_LENGTH;
- },
-
setBlame(blame) {
if (!this._builder || !blame) { return; }
this._builder.setBlame(blame);
},
-
- /**
- * Get the approximate length of the diff as the sum of the maximum
- * length of the chunks.
- *
- * @return {number}
- */
- getDiffLength() {
- return this.diff.content.reduce((sum, sec) => {
- if (sec.hasOwnProperty('ab')) {
- return sum + sec.ab.length;
- } else {
- return sum + Math.max(
- sec.hasOwnProperty('a') ? sec.a.length : 0,
- sec.hasOwnProperty('b') ? sec.b.length : 0);
- }
- }, 0);
- },
});
})();
</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
index 65a56f04fe..54303f611d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
@@ -115,18 +115,6 @@
group.element = element;
};
- GrDiffBuilder.prototype.renderSection = function(element) {
- for (let i = 0; i < this.groups.length; i++) {
- const group = this.groups[i];
- if (group.element === element) {
- const newElement = this.buildSectionElement(group);
- group.element.parentElement.replaceChild(newElement, group.element);
- group.element = newElement;
- break;
- }
- }
- };
-
GrDiffBuilder.prototype.getGroupsByLineRange = function(
startLine, endLine, opt_side) {
const groups = [];
@@ -233,57 +221,38 @@
group => { return group.element; });
};
- // TODO(wyatta): Move this completely into the processor.
- GrDiffBuilder.prototype._insertContextGroups = function(groups, lines,
- hiddenRange) {
- const linesBeforeCtx = lines.slice(0, hiddenRange[0]);
- const hiddenLines = lines.slice(hiddenRange[0], hiddenRange[1]);
- const linesAfterCtx = lines.slice(hiddenRange[1]);
-
- if (linesBeforeCtx.length > 0) {
- groups.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, linesBeforeCtx));
- }
-
- const ctxLine = new GrDiffLine(GrDiffLine.Type.CONTEXT_CONTROL);
- ctxLine.contextGroup =
- new GrDiffGroup(GrDiffGroup.Type.BOTH, hiddenLines);
- groups.push(new GrDiffGroup(GrDiffGroup.Type.CONTEXT_CONTROL,
- [ctxLine]));
+ GrDiffBuilder.prototype._createContextControl = function(section, line) {
+ if (!line.contextGroups) return null;
- if (linesAfterCtx.length > 0) {
- groups.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, linesAfterCtx));
- }
- };
+ const numLines =
+ line.contextGroups[line.contextGroups.length - 1].lineRange.left.end -
+ line.contextGroups[0].lineRange.left.start + 1;
- GrDiffBuilder.prototype._createContextControl = function(section, line) {
- if (!line.contextGroup || !line.contextGroup.lines.length) {
- return null;
- }
+ if (numLines === 0) return null;
const td = this._createElement('td');
- const showPartialLinks =
- line.contextGroup.lines.length > PARTIAL_CONTEXT_AMOUNT;
+ const showPartialLinks = numLines > PARTIAL_CONTEXT_AMOUNT;
if (showPartialLinks) {
td.appendChild(this._createContextButton(
- GrDiffBuilder.ContextButtonType.ABOVE, section, line));
+ GrDiffBuilder.ContextButtonType.ABOVE, section, line, numLines));
td.appendChild(document.createTextNode(' - '));
}
td.appendChild(this._createContextButton(
- GrDiffBuilder.ContextButtonType.ALL, section, line));
+ GrDiffBuilder.ContextButtonType.ALL, section, line, numLines));
if (showPartialLinks) {
td.appendChild(document.createTextNode(' - '));
td.appendChild(this._createContextButton(
- GrDiffBuilder.ContextButtonType.BELOW, section, line));
+ GrDiffBuilder.ContextButtonType.BELOW, section, line, numLines));
}
return td;
};
- GrDiffBuilder.prototype._createContextButton = function(type, section, line) {
- const contextLines = line.contextGroup.lines;
+ GrDiffBuilder.prototype._createContextButton = function(type, section, line,
+ numLines) {
const context = PARTIAL_CONTEXT_AMOUNT;
const button = this._createElement('gr-button', 'showContext');
@@ -291,20 +260,20 @@
button.setAttribute('no-uppercase', true);
let text;
- const groups = []; // The groups that replace this one if tapped.
+ let groups = []; // The groups that replace this one if tapped.
if (type === GrDiffBuilder.ContextButtonType.ALL) {
- text = 'Show ' + contextLines.length + ' common line';
- if (contextLines.length > 1) { text += 's'; }
- groups.push(line.contextGroup);
+ text = 'Show ' + numLines + ' common line';
+ if (numLines > 1) { text += 's'; }
+ groups.push(...line.contextGroups);
} else if (type === GrDiffBuilder.ContextButtonType.ABOVE) {
text = '+' + context + '↑';
- this._insertContextGroups(groups, contextLines,
- [context, contextLines.length]);
+ groups = GrDiffGroup.hideInContextControl(line.contextGroups,
+ context, numLines);
} else if (type === GrDiffBuilder.ContextButtonType.BELOW) {
text = '+' + context + '↓';
- this._insertContextGroups(groups, contextLines,
- [0, contextLines.length - context]);
+ groups = GrDiffGroup.hideInContextControl(line.contextGroups,
+ 0, numLines - context);
}
Polymer.dom(button).textContent = text;
@@ -337,8 +306,6 @@
return td;
} else if (line.type === GrDiffLine.Type.CONTEXT_CONTROL) {
td.classList.add('contextLineNum');
- td.setAttribute('data-value', '@@');
- td.textContent = '@@';
} else if (line.type === GrDiffLine.Type.BOTH || line.type === type) {
td.classList.add('lineNum');
td.setAttribute('data-value', number);
@@ -353,6 +320,12 @@
if (line.type !== GrDiffLine.Type.BLANK) {
td.classList.add('content');
}
+
+ // If intraline info is not available, the entire line will be
+ // considered as changed and marked as dark red / green color
+ if (!line.hasIntralineInfo) {
+ td.classList.add('no-intraline-info');
+ }
td.classList.add(line.type);
const lineLimit =
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
index a8db47a7c2..42414b7de4 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-builder</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
<script src="../gr-diff/gr-diff-line.js"></script>
@@ -28,13 +30,14 @@ limitations under the License.
<script src="../gr-diff-highlight/gr-annotation.js"></script>
<script src="gr-diff-builder.js"></script>
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
<link rel="import" href="gr-diff-builder.html">
<script>void(0);</script>
<test-fixture id="basic">
- <template>
+ <template is="dom-template">
<gr-diff-builder>
<table id="diffTable"></table>
</gr-diff-builder>
@@ -88,26 +91,37 @@ limitations under the License.
});
test('context control buttons', () => {
- const section = {};
- const line = {contextGroup: {lines: []}};
-
// Create 10 lines.
+ const lines = [];
for (let i = 0; i < 10; i++) {
- line.contextGroup.lines.push('lorem upsum');
+ const line = new GrDiffLine(GrDiffLine.Type.BOTH);
+ line.beforeNumber = i + 1;
+ line.afterNumber = i + 1;
+ line.text = 'lorem upsum';
+ lines.push(line);
}
+ const contextLine = {
+ contextGroups: [new GrDiffGroup(GrDiffGroup.Type.BOTH, lines)],
+ };
+
+ const section = {};
// Does not include +10 buttons when there are fewer than 11 lines.
- let td = builder._createContextControl(section, line);
+ let td = builder._createContextControl(section, contextLine);
let buttons = td.querySelectorAll('gr-button.showContext');
assert.equal(buttons.length, 1);
assert.equal(Polymer.dom(buttons[0]).textContent, 'Show 10 common lines');
// Add another line.
- line.contextGroup.lines.push('lorem upsum');
+ const line = new GrDiffLine(GrDiffLine.Type.BOTH);
+ line.text = 'lorem upsum';
+ line.beforeNumber = 11;
+ line.afterNumber = 11;
+ contextLine.contextGroups[0].addLine(line);
// Includes +10 buttons when there are at least 11 lines.
- td = builder._createContextControl(section, line);
+ td = builder._createContextControl(section, contextLine);
buttons = td.querySelectorAll('gr-button.showContext');
assert.equal(buttons.length, 3);
@@ -577,31 +591,39 @@ limitations under the License.
});
});
- suite('layers from plugins', () => {
+ suite('layers', () => {
let element;
let initialLayersCount;
-
+ let withLayerCount;
setup(() => {
+ const layers = [];
element = fixture('basic');
+ element.layers = layers;
element._showTrailingWhitespace = true;
element._setupAnnotationLayers();
initialLayersCount = element._layers.length;
});
- test('no plugin layers', () => {
- const getDiffLayersStub = sinon.stub(element.$.jsAPI, 'getDiffLayers')
- .returns([]);
+ test('no layers', () => {
element._setupAnnotationLayers();
- assert.isTrue(getDiffLayersStub.called);
assert.equal(element._layers.length, initialLayersCount);
});
- test('with plugin layers', () => {
- const getDiffLayersStub = sinon.stub(element.$.jsAPI, 'getDiffLayers')
- .returns([{}, {}]);
- element._setupAnnotationLayers();
- assert.isTrue(getDiffLayersStub.called);
- assert.equal(element._layers.length, initialLayersCount + 2);
+ suite('with layers', () => {
+ const layers = [{}, {}];
+ setup(() => {
+ element = fixture('basic');
+ element.layers = layers;
+ element._showTrailingWhitespace = true;
+ element._setupAnnotationLayers();
+ withLayerCount = element._layers.length;
+ });
+ test('with layers', () => {
+ element._setupAnnotationLayers();
+ assert.equal(element._layers.length, withLayerCount);
+ assert.equal(initialLayersCount + layers.length,
+ withLayerCount);
+ });
});
});
@@ -712,7 +734,6 @@ limitations under the License.
element.viewMode = 'SIDE_BY_SIDE';
processStub = sandbox.stub(element.$.processor, 'process')
.returns(Promise.resolve());
- sandbox.stub(element, '_anyLineTooLong').returns(true);
keyLocations = {left: {}, right: {}};
prefs = {
line_length: 10,
@@ -802,15 +823,6 @@ limitations under the License.
element.render(keyLocations, prefs).then(done);
});
- test('renderSection', () => {
- let section = outputEl.querySelector('stub:nth-of-type(2)');
- const prevInnerHTML = section.innerHTML;
- section.innerHTML = 'wiped';
- element._builder.renderSection(section);
- section = outputEl.querySelector('stub:nth-of-type(2)');
- assert.equal(section.innerHTML, prevInnerHTML);
- });
-
test('addColumns is called', done => {
element.render(keyLocations, {}).then(done);
assert.isTrue(element._builder.addColumns.called);
@@ -841,37 +853,14 @@ limitations under the License.
.map(c => { return c.args[0].type; });
assert.include(firedEventTypes, 'render-start');
assert.include(firedEventTypes, 'render-content');
- assert.include(firedEventTypes, 'render-syntax');
- done();
- });
- });
-
- test('rendering normal-sized diff does not disable syntax', () => {
- assert.isTrue(element.$.syntaxLayer.enabled);
- });
-
- test('rendering large diff disables syntax', done => {
- // Before it renders, set the first diff line to 500 '*' characters.
- element.diff.content[0].a = [new Array(501).join('*')];
- const prefs = {
- line_length: 10,
- show_tabs: true,
- tab_size: 4,
- context: -1,
- syntax_highlighting: true,
- };
- element.render(keyLocations, prefs).then(() => {
- assert.isFalse(element.$.syntaxLayer.enabled);
done();
});
});
test('cancel', () => {
const processorCancelStub = sandbox.stub(element.$.processor, 'cancel');
- const syntaxCancelStub = sandbox.stub(element.$.syntaxLayer, 'cancel');
element.cancel();
assert.isTrue(processorCancelStub.called);
- assert.isTrue(syntaxCancelStub.called);
});
});
@@ -900,10 +889,6 @@ limitations under the License.
});
});
- test('getDiffLength', () => {
- assert.equal(element.getDiffLength(diff), 52);
- });
-
test('getContentByLine', () => {
let actual;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.html b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.html
index c24574e6a2..99d0498098 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
<dom-module id="gr-diff-cursor">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
index 1cfd5e75b6..6ddb3902f3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
@@ -37,7 +37,6 @@
Polymer({
is: 'gr-diff-cursor',
- _legacyUndefinedCheck: true,
properties: {
/**
@@ -65,7 +64,10 @@
/**
* If set, the cursor will attempt to move to the line number (instead of
* the first chunk) the next time the diff renders. It is set back to null
- * when used.
+ * when used. It should be only used if you want the line to be focused
+ * after initialization of the component and page should scroll
+ * to that position. This parameter should be set at most for one gr-diff
+ * element in the page.
*
* @type {?number}
*/
@@ -136,11 +138,11 @@
}
},
- moveToNextChunk() {
+ moveToNextChunk(opt_clipToTop) {
this.$.cursorManager.next(this._isFirstRowOfChunk.bind(this),
target => {
return target.parentNode.scrollHeight;
- });
+ }, opt_clipToTop);
this._fixSide();
},
@@ -192,16 +194,20 @@
},
getTargetDiffElement() {
- // Find the parent diff element of the cursor row.
- for (let diff = this.diffRow; diff; diff = diff.parentElement) {
- if (diff.tagName === 'GR-DIFF') { return diff; }
+ if (!this.diffRow) return null;
+
+ const hostOwner = Polymer.dom(/** @type {Node} */ (this.diffRow))
+ .getOwnerRoot();
+ if (hostOwner && hostOwner.host &&
+ hostOwner.host.tagName === 'GR-DIFF') {
+ return hostOwner.host;
}
return null;
},
moveToFirstChunk() {
this.$.cursorManager.moveToStart();
- this.moveToNextChunk();
+ this.moveToNextChunk(true);
},
reInitCursor() {
@@ -224,8 +230,12 @@
handleDiffUpdate() {
this._updateStops();
-
if (!this.diffRow) {
+ // does not scroll during init unless requested
+ const scrollingBehaviorForInit = this.initialLineNumber ?
+ ScrollBehavior.KEEP_VISIBLE :
+ ScrollBehavior.NEVER;
+ this._scrollBehavior = scrollingBehaviorForInit;
this.reInitCursor();
}
this._scrollBehavior = ScrollBehavior.KEEP_VISIBLE;
@@ -296,7 +306,7 @@
},
_rowHasThread(row) {
- return row.querySelector('.comment-thread');
+ return row.querySelector('.thread-group');
},
/**
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
index f111378a74..1c1100d70b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
@@ -18,14 +18,17 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-cursor</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
<link rel="import" href="../gr-diff/gr-diff.html">
<link rel="import" href="./gr-diff-cursor.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
<script>void(0);</script>
@@ -207,40 +210,55 @@ limitations under the License.
assert.equal(cursorElement.side, 'left');
});
- test('initialLineNumber disabled', done => {
+ test('initialLineNumber not provided', done => {
+ let scrollBehaviorDuringMove;
const moveToNumStub = sandbox.stub(cursorElement, 'moveToLineNumber');
- const moveToChunkStub = sandbox.stub(cursorElement, 'moveToFirstChunk');
+ const moveToChunkStub = sandbox.stub(cursorElement, 'moveToFirstChunk',
+ () => { scrollBehaviorDuringMove = cursorElement._scrollBehavior; });
function renderHandler() {
diffElement.removeEventListener('render', renderHandler);
assert.isFalse(moveToNumStub.called);
assert.isTrue(moveToChunkStub.called);
+ assert.equal(scrollBehaviorDuringMove, 'never');
+ assert.equal(cursorElement._scrollBehavior, 'keep-visible');
done();
}
diffElement.addEventListener('render', renderHandler);
diffElement._diffChanged(mockDiffResponse.diffResponse);
});
- test('initialLineNumber enabled', done => {
- const moveToNumStub = sandbox.stub(cursorElement, 'moveToLineNumber');
+ test('initialLineNumber provided', done => {
+ let scrollBehaviorDuringMove;
+ const moveToNumStub = sandbox.stub(cursorElement, 'moveToLineNumber',
+ () => { scrollBehaviorDuringMove = cursorElement._scrollBehavior; });
const moveToChunkStub = sandbox.stub(cursorElement, 'moveToFirstChunk');
-
function renderHandler() {
diffElement.removeEventListener('render', renderHandler);
assert.isFalse(moveToChunkStub.called);
assert.isTrue(moveToNumStub.called);
assert.equal(moveToNumStub.lastCall.args[0], 10);
assert.equal(moveToNumStub.lastCall.args[1], 'right');
+ assert.equal(scrollBehaviorDuringMove, 'keep-visible');
+ assert.equal(cursorElement._scrollBehavior, 'keep-visible');
done();
}
diffElement.addEventListener('render', renderHandler);
-
cursorElement.initialLineNumber = 10;
cursorElement.side = 'right';
diffElement._diffChanged(mockDiffResponse.diffResponse);
});
+ test('getTargetDiffElement', () => {
+ cursorElement.initialLineNumber = 1;
+ assert.isTrue(!!cursorElement.diffRow);
+ assert.equal(
+ cursorElement.getTargetDiffElement(),
+ diffElement
+ );
+ });
+
test('getAddress', () => {
// It should initialize to the first chunk: line 5 of the revision.
assert.deepEqual(cursorElement.getAddress(),
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
index c07d3708e7..c1bf3edc1c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-annotation</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="gr-annotation.js"></script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
index c912a16223..3b1719011f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.html
@@ -14,10 +14,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
-<link rel="import" href="../gr-selection-action-box/gr-selection-action-box.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../gr-selection-action-box/gr-selection-action-box.html">
<dom-module id="gr-diff-highlight">
<template>
@@ -25,14 +26,6 @@ limitations under the License.
:host {
position: relative;
}
- .contentWrapper ::content .range {
- background-color: var(--diff-highlight-range-color);
- display: inline;
- }
- .contentWrapper ::content .rangeHighlight {
- background-color: var(--diff-highlight-range-hover-color);
- display: inline;
- }
gr-selection-action-box {
/**
* Needs z-index to apear above wrapped content, since it's inseted
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
index c820668547..0c6f4a3507 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-diff-highlight',
- _legacyUndefinedCheck: true,
properties: {
/** @type {!Array<!Gerrit.HoveredRange>} */
@@ -36,6 +35,10 @@
_cachedDiffBuilder: Object,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
listeners: {
'comment-thread-mouseleave': '_handleCommentThreadMouseleave',
'comment-thread-mouseenter': '_handleCommentThreadMouseenter',
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
index 2e50fdbab1..c929e1ed6e 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-highlight</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-diff-highlight.html">
@@ -92,7 +94,7 @@ limitations under the License.
<tbody class="section contextControl">
<tr class="diff-row side-by-side" left-type="contextControl" right-type="contextControl">
- <td class="left contextLineNum" data-value="@@"></td>
+ <td class="left contextLineNum"></td>
<td>
<gr-button>+10↑</gr-button>
-
@@ -100,7 +102,7 @@ limitations under the License.
-
<gr-button>+10↓</gr-button>
</td>
- <td class="right contextLineNum" data-value="@@"></td>
+ <td class="right contextLineNum"></td>
<td>
<gr-button>+10↑</gr-button>
-
@@ -179,8 +181,8 @@ limitations under the License.
element.commentRanges = [{side: 'right'}];
sandbox.stub(element, 'set');
- threadEl.dispatchEvent(
- new CustomEvent('comment-thread-mouseenter', {bubbles: true}));
+ threadEl.dispatchEvent(new CustomEvent(
+ 'comment-thread-mouseenter', {bubbles: true, composed: true}));
assert.isFalse(element.set.called);
});
@@ -204,8 +206,8 @@ limitations under the License.
}}];
sandbox.stub(element, 'set');
- threadEl.dispatchEvent(
- new CustomEvent('comment-thread-mouseenter', {bubbles: true}));
+ threadEl.dispatchEvent(new CustomEvent(
+ 'comment-thread-mouseenter', {bubbles: true, composed: true}));
assert.isTrue(element.set.called);
const args = element.set.lastCall.args;
assert.deepEqual(args[0], ['commentRanges', 0, 'hovering']);
@@ -221,8 +223,8 @@ limitations under the License.
element.commentRanges = [{side: 'right'}];
sandbox.stub(element, 'set');
- threadEl.dispatchEvent(
- new CustomEvent('comment-thread-mouseleave', {bubbles: true}));
+ threadEl.dispatchEvent(new CustomEvent(
+ 'comment-thread-mouseleave', {bubbles: true, composed: true}));
assert.isFalse(element.set.called);
});
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-range-normalizer.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-range-normalizer.js
index 927c7591a6..cb482b2f17 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-range-normalizer.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-range-normalizer.js
@@ -54,7 +54,7 @@
if (element.nodeName === '#text') {
element = element.parentElement;
}
- while (!element.classList.contains('contentText')) {
+ while (element && !element.classList.contains('contentText')) {
if (element.parentElement === null) {
return target;
}
@@ -80,7 +80,7 @@
if (n === child) {
break;
}
- if (n.childNodes && n.childNodes.length !== 0) {
+ if (n && n.childNodes && n.childNodes.length !== 0) {
const arr = [];
for (const childNode of n.childNodes) {
arr.push(childNode);
@@ -102,7 +102,9 @@
* @return {number} The length of the text.
*/
_getLength(node) {
- return node.textContent.replace(REGEX_ASTRAL_SYMBOL, '_').length;
+ return node
+ ? node.textContent.replace(REGEX_ASTRAL_SYMBOL, '_').length
+ : 0;
},
};
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
index 53ce6e6975..7d60f134bd 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
@@ -15,13 +15,15 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-comment-thread/gr-comment-thread.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="../gr-diff/gr-diff.html">
+<link rel="import" href="../gr-syntax-layer/gr-syntax-layer.html">
<dom-module id="gr-diff-host">
<template>
@@ -48,7 +50,13 @@ limitations under the License.
revision-image=[[_revisionImage]]
coverage-ranges="[[_coverageRanges]]"
blame="[[_blame]]"
- diff="[[diff]]"></gr-diff>
+ layers="[[_layers]]"
+ diff="[[diff]]">
+ </gr-diff>
+ <gr-syntax-layer
+ id="syntaxLayer"
+ enabled="[[_syntaxHighlightingEnabled]]"
+ diff="[[diff]]"></gr-syntax-layer>
<gr-js-api-interface id="jsAPI"></gr-js-api-interface>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
<gr-reporting id="reporting" category="diff"></gr-reporting>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
index 9760d50580..2dd19a00eb 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
@@ -35,6 +35,16 @@
SYNTAX: 'Diff Syntax Render',
};
+ // Disable syntax highlighting if the overall diff is too large.
+ const SYNTAX_MAX_DIFF_LENGTH = 20000;
+
+ // If any line of the diff is more than the character limit, then disable
+ // syntax highlighting for the entire file.
+ const SYNTAX_MAX_LINE_LENGTH = 500;
+
+ // 120 lines is good enough threshold for full-sized window viewport
+ const NUM_OF_LINES_THRESHOLD_FOR_VIEWPORT = 120;
+
const WHITESPACE_IGNORE_NONE = 'IGNORE_NONE';
/**
@@ -66,7 +76,6 @@
*/
Polymer({
is: 'gr-diff-host',
- _legacyUndefinedCheck: true,
/**
* Fired when the user selects a line.
@@ -111,7 +120,9 @@
commitRange: Object,
filesWeblinks: {
type: Object,
- value() { return {}; },
+ value() {
+ return {};
+ },
notify: true,
},
hidden: {
@@ -194,9 +205,7 @@
},
/**
- * TODO(brohlfs): Replace Object type by Gerrit.CoverageRange.
- *
- * @type {!Array<!Object>}
+ * @type {!Array<!Gerrit.CoverageRange>}
*/
_coverageRanges: {
type: Array,
@@ -209,9 +218,21 @@
type: Number,
computed: '_computeParentIndex(patchRange.*)',
},
+
+ _syntaxHighlightingEnabled: {
+ type: Boolean,
+ computed:
+ '_isSyntaxHighlightingEnabled(prefs.*, diff)',
+ },
+
+ _layers: {
+ type: Array,
+ value: [],
+ },
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.PatchSetBehavior,
],
@@ -229,7 +250,6 @@
'render-start': '_handleRenderStart',
'render-content': '_handleRenderContent',
- 'render-syntax': '_handleRenderSyntax',
'normalize-range': '_handleNormalizeRange',
},
@@ -237,6 +257,7 @@
observers: [
'_whitespaceChanged(prefs.ignore_whitespace, _loadedWhitespaceLevel,' +
' noRenderOnPrefsChange)',
+ '_syntaxHighlightingChanged(noRenderOnPrefsChange, prefs.*)',
],
ready() {
@@ -251,34 +272,35 @@
});
},
- /** @return {!Promise} */
- reload() {
+ /**
+ * @param {boolean=} haveParamsChanged ends reporting events that started
+ * on location change.
+ * @return {!Promise}
+ **/
+ reload(haveParamsChanged) {
this._loading = true;
this._errorMessage = null;
const whitespaceLevel = this._getIgnoreWhitespace();
- this._coverageRanges = [];
- const {changeNum, path, patchRange: {basePatchNum, patchNum}} = this;
- this.$.jsAPI.getCoverageRanges(changeNum, path, basePatchNum, patchNum).
- then(coverageRanges => {
- if (changeNum !== this.changeNum ||
- path !== this.path ||
- basePatchNum !== this.patchRange.basePatchNum ||
- patchNum !== this.patchRange.patchNum) {
- return;
- }
- this._coverageRanges = coverageRanges;
- }).catch(err => {
- console.warn('Loading coverage ranges failed: ', err);
- });
+ const layers = [this.$.syntaxLayer];
+ // Get layers from plugins (if any).
+ for (const pluginLayer of this.$.jsAPI.getDiffLayers(
+ this.path, this.changeNum, this.patchNum)) {
+ layers.push(pluginLayer);
+ }
+ this._layers = layers;
+ if (haveParamsChanged) {
+ // We listen on render viewport only on DiffPage (on paramsChanged)
+ this._listenToViewportRender();
+ }
+
+ this._coverageRanges = [];
+ this._getCoverageData();
const diffRequest = this._getDiff()
.then(diff => {
this._loadedWhitespaceLevel = whitespaceLevel;
this._reportDiff(diff);
- if (this._getIgnoreWhitespace() !== WHITESPACE_IGNORE_NONE) {
- return this._translateChunksToIgnore(diff);
- }
return diff;
})
.catch(e => {
@@ -293,6 +315,8 @@
return this._loadDiffAssets(diff);
});
+ // Not waiting for coverage ranges intentionally as
+ // plugin loading should not block the content rendering
return Promise.all([diffRequest, assetRequest])
.then(results => {
const diff = results[0];
@@ -301,8 +325,20 @@
}
this.filesWeblinks = this._getFilesWeblinks(diff);
return new Promise(resolve => {
- const callback = () => {
- resolve();
+ const callback = event => {
+ const needsSyntaxHighlighting = event.detail
+ && event.detail.contentRendered;
+ if (needsSyntaxHighlighting) {
+ this.$.reporting.time(TimingLabel.SYNTAX);
+ this.$.syntaxLayer.process().then(() => {
+ this.$.reporting.timeEnd(TimingLabel.SYNTAX);
+ this.$.reporting.timeEnd(TimingLabel.TOTAL);
+ resolve();
+ });
+ } else {
+ this.$.reporting.timeEnd(TimingLabel.TOTAL);
+ resolve();
+ }
this.removeEventListener('render', callback);
};
this.addEventListener('render', callback);
@@ -315,8 +351,53 @@
.then(() => { this._loading = false; });
},
+ _getCoverageData() {
+ const {changeNum, path, patchRange: {basePatchNum, patchNum}} = this;
+ this.$.jsAPI.getCoverageAnnotationApi().
+ then(coverageAnnotationApi => {
+ if (!coverageAnnotationApi) return;
+ const provider = coverageAnnotationApi.getCoverageProvider();
+ return provider(changeNum, path, basePatchNum, patchNum)
+ .then(coverageRanges => {
+ if (!coverageRanges ||
+ changeNum !== this.changeNum ||
+ path !== this.path ||
+ basePatchNum !== this.patchRange.basePatchNum ||
+ patchNum !== this.patchRange.patchNum) {
+ return;
+ }
+
+ const existingCoverageRanges = this._coverageRanges;
+ this._coverageRanges = coverageRanges;
+
+ // Notify with existing coverage ranges
+ // in case there is some existing coverage data that needs to be removed
+ existingCoverageRanges.forEach(range => {
+ coverageAnnotationApi.notify(
+ path,
+ range.code_range.start_line,
+ range.code_range.end_line,
+ range.side);
+ });
+
+ // Notify with new coverage data
+ coverageRanges.forEach(range => {
+ coverageAnnotationApi.notify(
+ path,
+ range.code_range.start_line,
+ range.code_range.end_line,
+ range.side);
+ });
+ });
+ }).catch(err => {
+ console.warn('Loading coverage ranges failed: ', err);
+ });
+ },
+
_getFilesWeblinks(diff) {
- if (!this.commitRange) { return {}; }
+ if (!this.commitRange) {
+ return {};
+ }
return {
meta_a: Gerrit.Nav.getFileWebLinks(
this.projectName, this.commitRange.baseCommit, this.path,
@@ -375,7 +456,6 @@
* @return {!Array<!HTMLElement>}
*/
getThreadEls() {
- // Polymer2: querySelectorAll returns NodeList instead of Array.
return Array.from(
Polymer.dom(this.$.diff).querySelectorAll('.comment-thread'));
},
@@ -444,7 +524,9 @@
* Report info about the diff response.
*/
_reportDiff(diff) {
- if (!diff || !diff.content) { return; }
+ if (!diff || !diff.content) {
+ return;
+ }
// Count the delta lines stemming from normal deltas, and from
// due_to_rebase deltas.
@@ -672,7 +754,7 @@
* @param {!Gerrit.Range=} range
* @return {?Node}
*/
- _getThreadEl(lineNum, commentSide, range=undefined) {
+ _getThreadEl(lineNum, commentSide, range = undefined) {
let line;
if (commentSide === GrDiffBuilder.Side.LEFT) {
line = {beforeNumber: lineNum};
@@ -737,50 +819,6 @@
matchers.some(matcher => matcher(threadEl)));
},
- /**
- * Take a diff that was loaded with a ignore-whitespace other than
- * IGNORE_NONE, and convert delta chunks labeled as common into shared
- * chunks.
- *
- * @param {!Object} diff
- * @returns {!Object}
- */
- _translateChunksToIgnore(diff) {
- const newDiff = Object.assign({}, diff);
- const mergedContent = [];
-
- // Was the last chunk visited a shared chunk?
- let lastWasShared = false;
-
- for (const chunk of diff.content) {
- if (lastWasShared && chunk.common && chunk.b) {
- // The last chunk was shared and this chunk should be ignored, so
- // add its revision content to the previous chunk.
- mergedContent[mergedContent.length - 1].ab.push(...chunk.b);
- } else if (chunk.common && !chunk.b) {
- // If the chunk should be ignored, but it doesn't have revision
- // content, then drop it and continue without updating lastWasShared.
- continue;
- } else if (lastWasShared && chunk.ab) {
- // Both the last chunk and the current chunk are shared. Merge this
- // chunk's shared content into the previous shared content.
- mergedContent[mergedContent.length - 1].ab.push(...chunk.ab);
- } else if (!lastWasShared && chunk.common && chunk.b) {
- // If the previous chunk was not shared, but this one should be
- // ignored, then add it as a shared chunk.
- mergedContent.push({ab: chunk.b});
- } else {
- // Otherwise add the chunk as is.
- mergedContent.push(chunk);
- }
-
- lastWasShared = !!mergedContent[mergedContent.length - 1].ab;
- }
-
- newDiff.content = mergedContent;
- return newDiff;
- },
-
_getIgnoreWhitespace() {
if (!this.prefs || !this.prefs.ignore_whitespace) {
return WHITESPACE_IGNORE_NONE;
@@ -788,14 +826,42 @@
return this.prefs.ignore_whitespace;
},
- _whitespaceChanged(preferredWhitespaceLevel, loadedWhitespaceLevel,
+ _whitespaceChanged(
+ preferredWhitespaceLevel, loadedWhitespaceLevel,
noRenderOnPrefsChange) {
+ // Polymer 2: check for undefined
+ if ([
+ preferredWhitespaceLevel,
+ loadedWhitespaceLevel,
+ noRenderOnPrefsChange,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
if (preferredWhitespaceLevel !== loadedWhitespaceLevel &&
!noRenderOnPrefsChange) {
this.reload();
}
},
+ _syntaxHighlightingChanged(noRenderOnPrefsChange, prefsChangeRecord) {
+ // Polymer 2: check for undefined
+ if ([
+ noRenderOnPrefsChange,
+ prefsChangeRecord,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
+ if (prefsChangeRecord.path !== 'prefs.syntax_highlighting') {
+ return;
+ }
+
+ if (!noRenderOnPrefsChange) {
+ this.reload();
+ }
+ },
+
/**
* @param {Object} patchRangeRecord
* @return {number|null}
@@ -841,8 +907,8 @@
},
_handleCommentSaveOrDiscard() {
- this.dispatchEvent(new CustomEvent('diff-comments-modified',
- {bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'diff-comments-modified', {bubbles: true, composed: true}));
},
_removeComment(comment) {
@@ -877,6 +943,42 @@
item => item.__draftID === comment.__draftID);
},
+ _isSyntaxHighlightingEnabled(preferenceChangeRecord, diff) {
+ if (!preferenceChangeRecord ||
+ !preferenceChangeRecord.base ||
+ !preferenceChangeRecord.base.syntax_highlighting ||
+ !diff) {
+ return false;
+ }
+ return !this._anyLineTooLong(diff) &&
+ this.$.diff.getDiffLength(diff) <= SYNTAX_MAX_DIFF_LENGTH;
+ },
+
+ /**
+ * @return {boolean} whether any of the lines in diff are longer
+ * than SYNTAX_MAX_LINE_LENGTH.
+ */
+ _anyLineTooLong(diff) {
+ if (!diff) return false;
+ return diff.content.some(section => {
+ const lines = section.ab ?
+ section.ab :
+ (section.a || []).concat(section.b || []);
+ return lines.some(line => line.length >= SYNTAX_MAX_LINE_LENGTH);
+ });
+ },
+
+ _listenToViewportRender() {
+ const renderUpdateListener = start => {
+ if (start > NUM_OF_LINES_THRESHOLD_FOR_VIEWPORT) {
+ this.$.reporting.diffViewDisplayed();
+ this.$.syntaxLayer.removeListener(renderUpdateListener);
+ }
+ };
+
+ this.$.syntaxLayer.addListener(renderUpdateListener);
+ },
+
_handleRenderStart() {
this.$.reporting.time(TimingLabel.TOTAL);
this.$.reporting.time(TimingLabel.CONTENT);
@@ -884,12 +986,7 @@
_handleRenderContent() {
this.$.reporting.timeEnd(TimingLabel.CONTENT);
- this.$.reporting.time(TimingLabel.SYNTAX);
- },
-
- _handleRenderSyntax() {
- this.$.reporting.timeEnd(TimingLabel.SYNTAX);
- this.$.reporting.timeEnd(TimingLabel.TOTAL);
+ this.$.reporting.diffViewContentDisplayed();
},
_handleNormalizeRange(event) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
index 6e7c239e08..94b2f7d200 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-diff-host.html">
@@ -49,7 +51,6 @@ limitations under the License.
time: sandbox.stub(),
timeEnd: sandbox.stub(),
});
-
element = fixture('basic');
});
@@ -57,6 +58,22 @@ limitations under the License.
sandbox.restore();
});
+
+ suite('plugin layers', () => {
+ const pluginLayers = [{annotate: () => {}}, {annotate: () => {}}];
+ setup(() => {
+ stub('gr-js-api-interface', {
+ getDiffLayers() { return pluginLayers; },
+ });
+ element = fixture('basic');
+ });
+ test('plugin layers requested', () => {
+ element.patchRange = {};
+ element.reload();
+ assert(element.$.jsAPI.getDiffLayers.called);
+ });
+ });
+
suite('handle comment-update', () => {
setup(() => {
sandbox.stub(element, '_commentsChanged');
@@ -250,7 +267,15 @@ limitations under the License.
element.path = 'some/path';
element.projectName = 'Some project';
const threadEls = threads.map(
- thread => element._createThreadElement(thread));
+ thread => {
+ const threadEl = element._createThreadElement(thread);
+ // Polymer 2 doesn't fire ready events and doesn't execute
+ // observers if element is not added to the Dom.
+ // See https://github.com/Polymer/old-docs-site/issues/2322
+ // and https://github.com/Polymer/polymer/issues/4526
+ element._attachThreadElement(threadEl);
+ return threadEl;
+ });
assert.equal(threadEls.length, 2);
assert.equal(threadEls[0].rootId, 4711);
assert.equal(threadEls[1].rootId, 42);
@@ -269,7 +294,7 @@ limitations under the License.
suite('render reporting', () => {
test('starts total and content timer on render-start', done => {
element.dispatchEvent(
- new CustomEvent('render-start', {bubbles: true}));
+ new CustomEvent('render-start', {bubbles: true, composed: true}));
assert.isTrue(element.$.reporting.time.calledWithExactly(
'Diff Total Render'));
assert.isTrue(element.$.reporting.time.calledWithExactly(
@@ -277,24 +302,82 @@ limitations under the License.
done();
});
- test('ends content and starts syntax timer on render-content', done => {
+ test('ends content timer on render-content', () => {
element.dispatchEvent(
- new CustomEvent('render-content', {bubbles: true}));
- assert.isTrue(element.$.reporting.time.calledWithExactly(
- 'Diff Syntax Render'));
+ new CustomEvent('render-content', {bubbles: true, composed: true}));
assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
'Diff Content Render'));
- done();
});
- test('ends total and syntax timer on render-syntax', done => {
- element.dispatchEvent(
- new CustomEvent('render-syntax', {bubbles: true}));
- assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
- 'Diff Total Render'));
- assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
- 'Diff Syntax Render'));
- done();
+ test('ends total and syntax timer after syntax layer processing', done => {
+ let notifySyntaxProcessed;
+ sandbox.stub(element.$.syntaxLayer, 'process').returns(new Promise(
+ resolve => {
+ notifySyntaxProcessed = resolve;
+ }));
+ sandbox.stub(element.$.restAPI, 'getDiff').returns(
+ Promise.resolve({content: []}));
+ element.patchRange = {};
+ element.$.restAPI.getDiffPreferences().then(prefs => {
+ element.prefs = prefs;
+ return element.reload();
+ });
+ // Multiple cascading microtasks are scheduled.
+ setTimeout(() => {
+ notifySyntaxProcessed();
+ // Assert after the notification task is processed.
+ Promise.resolve().then(() => {
+ assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+ 'Diff Total Render'));
+ assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+ 'Diff Syntax Render'));
+ assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+ 'StartupDiffViewOnlyContent'));
+ done();
+ });
+ });
+ });
+
+ test('ends total timer w/ no syntax layer processing', done => {
+ sandbox.stub(element.$.restAPI, 'getDiff').returns(
+ Promise.resolve({content: []}));
+ element.patchRange = {};
+ element.reload();
+ // Multiple cascading microtasks are scheduled.
+ setTimeout(() => {
+ assert.isTrue(element.$.reporting.timeEnd.calledOnce);
+ assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+ 'Diff Total Render'));
+ done();
+ });
+ });
+
+ test('completes reload promise after syntax layer processing', done => {
+ let notifySyntaxProcessed;
+ sandbox.stub(element.$.syntaxLayer, 'process').returns(new Promise(
+ resolve => {
+ notifySyntaxProcessed = resolve;
+ }));
+ sandbox.stub(element.$.restAPI, 'getDiff').returns(
+ Promise.resolve({content: []}));
+ element.patchRange = {};
+ let reloadComplete = false;
+ element.$.restAPI.getDiffPreferences().then(prefs => {
+ element.prefs = prefs;
+ return element.reload();
+ }).then(() => {
+ reloadComplete = true;
+ });
+ // Multiple cascading microtasks are scheduled.
+ setTimeout(() => {
+ assert.isFalse(reloadComplete);
+ notifySyntaxProcessed();
+ // Assert after the notification task is processed.
+ setTimeout(() => {
+ assert.isTrue(reloadComplete);
+ done();
+ });
+ });
});
});
@@ -303,6 +386,7 @@ limitations under the License.
// Stub the network calls into requests that never resolve.
sandbox.stub(element, '_getDiff', () => new Promise(() => {}));
+ element.patchRange = {};
element.reload();
assert.isTrue(cancelStub.called);
@@ -366,6 +450,7 @@ limitations under the License.
(changeNum, basePatchNum, patchNum, path, onErr) => {
onErr(error);
});
+ element.patchRange = {};
return element.reload().then(() => {
assert.isTrue(onErrStub.calledOnce);
});
@@ -723,6 +808,7 @@ limitations under the License.
test('delegates cancel()', () => {
const stub = sandbox.stub(element.$.diff, 'cancel');
+ element.patchRange = {};
element.reload();
assert.isTrue(stub.calledOnce);
assert.equal(stub.lastCall.args.length, 0);
@@ -1241,9 +1327,11 @@ limitations under the License.
const l = document.createElement('div');
l.setAttribute('comment-side', 'left');
+ l.setAttribute('line-num', 'FILE');
const r = document.createElement('div');
r.setAttribute('comment-side', 'right');
+ r.setAttribute('line-num', 'FILE');
const threadEls = [l, r];
assert.deepEqual(element._filterThreadElsForLocation(threadEls, line),
@@ -1256,86 +1344,154 @@ limitations under the License.
Gerrit.DiffSide.RIGHT), [r]);
});
- suite('_translateChunksToIgnore', () => {
- let content;
-
+ suite('syntax layer with syntax_highlighting on', () => {
setup(() => {
- content = [
- {ab: ['one', 'two']},
- {a: ['three'], b: ['different three']},
- {b: ['four']},
- {ab: ['five', 'six']},
- {a: ['seven']},
- {ab: ['eight', 'nine']},
- ];
+ const prefs = {
+ line_length: 10,
+ show_tabs: true,
+ tab_size: 4,
+ context: -1,
+ syntax_highlighting: true,
+ };
+ element.patchRange = {};
+ element.prefs = prefs;
});
- test('does nothing to unmarked diff', () => {
- assert.deepEqual(element._translateChunksToIgnore({content}),
- {content});
+ test('gr-diff-host provides syntax highlighting layer to gr-diff', () => {
+ element.reload();
+ assert.equal(element.$.diff.layers[0], element.$.syntaxLayer);
});
- test('merges marked delta chunk', () => {
- content[1].common = true;
- assert.deepEqual(element._translateChunksToIgnore({content}), {
- content: [
- {ab: ['one', 'two', 'different three']},
- {b: ['four']},
- {ab: ['five', 'six']},
- {a: ['seven']},
- {ab: ['eight', 'nine']},
- ],
- });
+ test('rendering normal-sized diff does not disable syntax', () => {
+ element.diff = {
+ content: [{
+ a: ['foo'],
+ }],
+ };
+ assert.isTrue(element.$.syntaxLayer.enabled);
});
- test('merges marked addition chunk', () => {
- content[2].common = true;
- assert.deepEqual(element._translateChunksToIgnore({content}), {
- content: [
- {ab: ['one', 'two']},
- {a: ['three'], b: ['different three']},
- {ab: ['four', 'five', 'six']},
- {a: ['seven']},
- {ab: ['eight', 'nine']},
- ],
+ test('rendering large diff disables syntax', () => {
+ // Before it renders, set the first diff line to 500 '*' characters.
+ element.diff = {
+ content: [{
+ a: [new Array(501).join('*')],
+ }],
+ };
+ assert.isFalse(element.$.syntaxLayer.enabled);
+ });
+
+ test('starts syntax layer processing on render event', done => {
+ sandbox.stub(element.$.syntaxLayer, 'process').returns(Promise.resolve());
+ sandbox.stub(element.$.restAPI, 'getDiff').returns(
+ Promise.resolve({content: []}));
+ element.reload();
+ setTimeout(() => {
+ element.dispatchEvent(
+ new CustomEvent('render', {bubbles: true, composed: true}));
+ assert.isTrue(element.$.syntaxLayer.process.called);
+ done();
});
});
+ });
+
+ suite('syntax layer with syntax_highlgihting off', () => {
+ setup(() => {
+ const prefs = {
+ line_length: 10,
+ show_tabs: true,
+ tab_size: 4,
+ context: -1,
+ };
+ element.diff = {
+ content: [{
+ a: ['foo'],
+ }],
+ };
+ element.patchRange = {};
+ element.prefs = prefs;
+ });
- test('merges multiple marked delta', () => {
- content[1].common = true;
- content[2].common = true;
- assert.deepEqual(element._translateChunksToIgnore({content}), {
- content: [
- {ab: ['one', 'two', 'different three', 'four', 'five', 'six']},
- {a: ['seven']},
- {ab: ['eight', 'nine']},
- ],
+ test('gr-diff-host provides syntax highlighting layer', () => {
+ element.reload();
+ assert.equal(element.$.diff.layers[0], element.$.syntaxLayer);
+ });
+
+ test('syntax layer should be disabled', () => {
+ assert.isFalse(element.$.syntaxLayer.enabled);
+ });
+
+ test('still disabled for large diff', () => {
+ // Before it renders, set the first diff line to 500 '*' characters.
+ element.diff = {
+ content: [{
+ a: [new Array(501).join('*')],
+ }],
+ };
+ assert.isFalse(element.$.syntaxLayer.enabled);
+ });
+ });
+
+ suite('coverage layer', () => {
+ let notifyStub;
+ setup(() => {
+ notifyStub = sinon.stub();
+ stub('gr-js-api-interface', {
+ getCoverageAnnotationApi() {
+ return Promise.resolve({
+ notify: notifyStub,
+ getCoverageProvider() {
+ return () => Promise.resolve([
+ {
+ type: 'COVERED',
+ side: 'right',
+ code_range: {
+ start_line: 1,
+ end_line: 2,
+ },
+ },
+ {
+ type: 'NOT_COVERED',
+ side: 'right',
+ code_range: {
+ start_line: 3,
+ end_line: 4,
+ },
+ },
+ ]);
+ },
+ });
+ },
});
+ element = fixture('basic');
+ const prefs = {
+ line_length: 10,
+ show_tabs: true,
+ tab_size: 4,
+ context: -1,
+ };
+ element.diff = {
+ content: [{
+ a: ['foo'],
+ }],
+ };
+ element.patchRange = {};
+ element.prefs = prefs;
});
- test('marked deletion chunks are omitted', () => {
- content[4].common = true;
- assert.deepEqual(element._translateChunksToIgnore({content}), {
- content: [
- {ab: ['one', 'two']},
- {a: ['three'], b: ['different three']},
- {b: ['four']},
- {ab: ['five', 'six', 'eight', 'nine']},
- ],
+ test('getCoverageAnnotationApi should be called', done => {
+ element.reload();
+ flush(() => {
+ assert.isTrue(element.$.jsAPI.getCoverageAnnotationApi.calledOnce);
+ done();
});
});
- test('marked deltas can start shared chunks', () => {
- content[0] = {a: ['one'], b: ['two'], common: true};
- assert.deepEqual(element._translateChunksToIgnore({content}), {
- content: [
- {ab: ['two']},
- {a: ['three'], b: ['different three']},
- {b: ['four']},
- {ab: ['five', 'six']},
- {a: ['seven']},
- {ab: ['eight', 'nine']},
- ],
+ test('coverageRangeChanged should be called', done => {
+ element.reload();
+ flush(() => {
+ assert.equal(notifyStub.callCount, 2);
+ done();
});
});
});
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.html b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.html
index 8251e53ffb..47cf7711a0 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -41,7 +42,7 @@ limitations under the License.
has-tooltip
class$="[[_computeSelectedClass(mode, _VIEW_MODES.SIDE_BY_SIDE)]]"
title="Side-by-side diff"
- on-tap="_handleSideBySideTap">
+ on-click="_handleSideBySideTap">
<iron-icon icon="gr-icons:side-by-side"></iron-icon>
</gr-button>
<gr-button
@@ -50,7 +51,7 @@ limitations under the License.
has-tooltip
title="Unified diff"
class$="[[_computeSelectedClass(mode, _VIEW_MODES.UNIFIED)]]"
- on-tap="_handleUnifiedTap">
+ on-click="_handleUnifiedTap">
<iron-icon icon="gr-icons:unified"></iron-icon>
</gr-button>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
index e2d6a28415..88dd91a8ca 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-diff-mode-selector',
- _legacyUndefinedCheck: true,
properties: {
mode: {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html
index c0111067f2..adeaa1509b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-mode-selector</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<script src="../../../scripts/util.js"></script>
<link rel="import" href="gr-diff-mode-selector.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
index b850f2cc8c..b0167b3636 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-diff-preferences/gr-diff-preferences.html">
@@ -26,7 +27,7 @@ limitations under the License.
<style include="shared-styles">
.diffHeader,
.diffActions {
- padding: 1em 1.5em;
+ padding: var(--spacing-l) var(--spacing-xl);
}
.diffHeader,
.diffActions {
@@ -42,7 +43,7 @@ limitations under the License.
justify-content: flex-end;
}
.diffPrefsOverlay gr-button {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
div.edited:after {
color: var(--deemphasized-text-color);
@@ -50,7 +51,7 @@ limitations under the License.
}
#diffPreferences {
display: flex;
- padding: .35em 1.5em;
+ padding: var(--spacing-s) var(--spacing-xl);
}
</style>
<gr-overlay id="diffPrefsOverlay" with-backdrop>
@@ -63,13 +64,13 @@ limitations under the License.
<gr-button
id="cancelButton"
link
- on-tap="_handleCancelDiff">
+ on-click="_handleCancelDiff">
Cancel
</gr-button>
<gr-button
id="saveButton"
link primary
- on-tap="_handleSaveDiffPreferences"
+ on-click="_handleSaveDiffPreferences"
disabled$="[[!_diffPrefsChanged]]">
Save
</gr-button>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
index 7f7cd733a7..9b723bdee5 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-diff-preferences-dialog',
- _legacyUndefinedCheck: true,
properties: {
/** @type {?} */
@@ -39,15 +38,19 @@
_diffPrefsChanged: Boolean,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
getFocusStops() {
return {
- start: this.$.contextSelect,
+ start: this.$.diffPreferences.$.contextSelect,
end: this.$.saveButton,
};
},
resetFocus() {
- this.$.contextSelect.focus();
+ this.$.diffPreferences.$.contextSelect.focus();
},
_computeHeaderClass(changed) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.html b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.html
index 663cf258a6..922ac87086 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-diff-processor">
<script src="../gr-diff/gr-diff-line.js"></script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
index 2cc69e664d..e052a8fddf 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
@@ -24,12 +24,6 @@
RIGHT: 'right',
};
- const DiffGroupType = {
- ADDED: 'b',
- BOTH: 'ab',
- REMOVED: 'a',
- };
-
const DiffHighlights = {
ADDED: 'edit_b',
REMOVED: 'edit_a',
@@ -48,19 +42,30 @@
/**
* Converts the API's `DiffContent`s to `GrDiffGroup`s for rendering.
*
- * This includes a number of tasks:
+ * Glossary:
+ * - "chunk": A single `DiffContent` as returned by the API.
+ * - "group": A single `GrDiffGroup` as used for rendering.
+ * - "common" chunk/group: A chunk/group that should be considered unchanged
+ * for diffing purposes. This can mean its either actually unchanged, or it
+ * has only whitespace changes.
+ * - "key location": A line number and side of the diff that should not be
+ * collapsed e.g. because a comment is attached to it, or because it was
+ * provided in the URL and thus should be visible
+ * - "uncollapsible" chunk/group: A chunk/group that is either not "common",
+ * or cannot be collapsed because it contains a key location
+ *
+ * Here a a number of tasks this processor performs:
+ * - splitting large chunks to allow more granular async rendering
* - adding a group for the "File" pseudo line that file-level comments can
* be attached to
- * - replacing unchanged parts of the diff that are outside the user's
+ * - replacing common parts of the diff that are outside the user's
* context setting and do not have comments with a group representing the
- * "expand context" widget. This may require splitting a `DiffContent` so
+ * "expand context" widget. This may require splitting a chunk/group so
* that the part that is within the context or has comments is shown, while
* the rest is not.
- * - splitting large `DiffContent`s to allow more granular async rendering
*/
Polymer({
is: 'gr-diff-processor',
- _legacyUndefinedCheck: true,
properties: {
@@ -127,13 +132,16 @@
},
/**
- * Asynchronously process the diff object into groups. As it processes, it
+ * Asynchronously process the diff chunks into groups. As it processes, it
* will splice groups into the `groups` property of the component.
*
- * @return {Promise} A promise that resolves when the diff is completely
- * processed.
+ * @param {!Array<!Gerrit.DiffChunk>} chunks
+ * @param {boolean} isBinary
+ *
+ * @return {!Promise<!Array<!Object>>} A promise that resolves with an
+ * array of GrDiffGroups when the diff is completely processed.
*/
- process(content, isBinary) {
+ process(chunks, isBinary) {
// Cancel any still running process() calls, because they append to the
// same groups field.
this.cancel();
@@ -150,11 +158,11 @@
new Promise(resolve => {
const state = {
lineNums: {left: 0, right: 0},
- sectionIndex: 0,
+ chunkIndex: 0,
};
- content = this._splitLargeChunks(content);
- content = this._splitUnchangedChunksWithComments(content);
+ chunks = this._splitLargeChunks(chunks);
+ chunks = this._splitCommonChunksWithKeyLocations(chunks);
let currentBatch = 0;
const nextStep = () => {
@@ -163,23 +171,23 @@
return;
}
// If we are done, resolve the promise.
- if (state.sectionIndex >= content.length) {
- resolve(this.groups);
+ if (state.chunkIndex >= chunks.length) {
+ resolve();
this._nextStepHandle = null;
return;
}
- // Process the next section and incorporate the result.
- const result = this._processNext(state, content);
- for (const group of result.groups) {
+ // Process the next chunk and incorporate the result.
+ const stateUpdate = this._processNext(state, chunks);
+ for (const group of stateUpdate.groups) {
this.push('groups', group);
currentBatch += group.lines.length;
}
- state.lineNums.left += result.lineDelta.left;
- state.lineNums.right += result.lineDelta.right;
+ state.lineNums.left += stateUpdate.lineDelta.left;
+ state.lineNums.right += stateUpdate.lineDelta.right;
// Increment the index and recurse.
- state.sectionIndex++;
+ state.chunkIndex = stateUpdate.newChunkIndex;
if (currentBatch >= this._asyncThreshold) {
currentBatch = 0;
this._nextStepHandle = this.async(nextStep, 1);
@@ -208,178 +216,197 @@
},
/**
- * Process the next section of the diff.
+ * Process the next uncollapsible chunk, or the next collapsible chunks.
+ *
+ * @param {!Object} state
+ * @param {!Array<!Object>} chunks
+ * @return {{lineDelta: {left: number, right: number}, groups: !Array<!Object>, newChunkIndex: number}}
*/
- _processNext(state, content) {
- const section = content[state.sectionIndex];
-
- const rows = {
- both: section[DiffGroupType.BOTH] || null,
- added: section[DiffGroupType.ADDED] || null,
- removed: section[DiffGroupType.REMOVED] || null,
- };
-
- const highlights = {
- added: section[DiffHighlights.ADDED] || null,
- removed: section[DiffHighlights.REMOVED] || null,
- };
-
- if (rows.both) { // If it's a shared section.
- let sectionEnd = null;
- if (state.sectionIndex === 0) {
- sectionEnd = 'first';
- } else if (state.sectionIndex === content.length - 1) {
- sectionEnd = 'last';
- }
-
- const sharedGroups = this._sharedGroupsFromRows(
- rows.both,
- content.length > 1 ? this.context : WHOLE_FILE,
- state.lineNums.left,
- state.lineNums.right,
- sectionEnd);
-
+ _processNext(state, chunks) {
+ const firstUncollapsibleChunkIndex =
+ this._firstUncollapsibleChunkIndex(chunks, state.chunkIndex);
+ if (firstUncollapsibleChunkIndex === state.chunkIndex) {
+ const chunk = chunks[state.chunkIndex];
return {
lineDelta: {
- left: rows.both.length,
- right: rows.both.length,
+ left: this._linesLeft(chunk).length,
+ right: this._linesRight(chunk).length,
},
- groups: sharedGroups,
+ groups: [this._chunkToGroup(
+ chunk, state.lineNums.left + 1, state.lineNums.right + 1)],
+ newChunkIndex: state.chunkIndex + 1,
};
- } else { // Otherwise it's a delta section.
- const deltaGroup = this._deltaGroupFromRows(
- rows.added,
- rows.removed,
- state.lineNums.left,
- state.lineNums.right,
- highlights);
- deltaGroup.dueToRebase = section.due_to_rebase;
+ }
- return {
- lineDelta: {
- left: rows.removed ? rows.removed.length : 0,
- right: rows.added ? rows.added.length : 0,
- },
- groups: [deltaGroup],
- };
+ return this._processCollapsibleChunks(
+ state, chunks, firstUncollapsibleChunkIndex);
+ },
+
+ _linesLeft(chunk) {
+ return chunk.ab || chunk.a || [];
+ },
+
+ _linesRight(chunk) {
+ return chunk.ab || chunk.b || [];
+ },
+
+ _firstUncollapsibleChunkIndex(chunks, offset) {
+ let chunkIndex = offset;
+ while (chunkIndex < chunks.length &&
+ this._isCollapsibleChunk(chunks[chunkIndex])) {
+ chunkIndex++;
}
+ return chunkIndex;
+ },
+
+ _isCollapsibleChunk(chunk) {
+ return (chunk.ab || chunk.common) && !chunk.keyLocation;
},
/**
- * Take rows of a shared diff section and produce an array of corresponding
- * (potentially collapsed) groups.
+ * Process a stretch of collapsible chunks.
*
- * @param {!Array<string>} rows
- * @param {number} context
- * @param {number} startLineNumLeft
- * @param {number} startLineNumRight
- * @param {?string=} opt_sectionEnd String representing whether this is the
- * first section or the last section or neither. Use the values 'first',
- * 'last' and null respectively.
- * @return {!Array<!Object>} Array of GrDiffGroup
+ * Outputs up to three groups:
+ * 1) Visible context before the hidden common code, unless it's the
+ * very beginning of the file.
+ * 2) Context hidden behind a context bar, unless empty.
+ * 3) Visible context after the hidden common code, unless it's the very
+ * end of the file.
+ *
+ * @param {!Object} state
+ * @param {!Array<Object>} chunks
+ * @param {number} firstUncollapsibleChunkIndex
+ * @return {{lineDelta: {left: number, right: number}, groups: !Array<!Object>, newChunkIndex: number}}
*/
- _sharedGroupsFromRows(rows, context, startLineNumLeft,
- startLineNumRight, opt_sectionEnd) {
- const result = [];
- const lines = [];
- let line;
-
- // Map each row to a GrDiffLine.
- for (let i = 0; i < rows.length; i++) {
- line = new GrDiffLine(GrDiffLine.Type.BOTH);
- line.text = rows[i];
- line.beforeNumber = ++startLineNumLeft;
- line.afterNumber = ++startLineNumRight;
- lines.push(line);
- }
-
- // Find the hidden range based on the user's context preference. If this
- // is the first or the last section of the diff, make sure the collapsed
- // part of the section extends to the edge of the file.
- const hiddenRange = [context, rows.length - context];
- if (opt_sectionEnd === 'first') {
- hiddenRange[0] = 0;
- } else if (opt_sectionEnd === 'last') {
- hiddenRange[1] = rows.length;
+ _processCollapsibleChunks(
+ state, chunks, firstUncollapsibleChunkIndex) {
+ const collapsibleChunks = chunks.slice(
+ state.chunkIndex, firstUncollapsibleChunkIndex);
+ const lineCount = collapsibleChunks.reduce(
+ (sum, chunk) => sum + this._commonChunkLength(chunk), 0);
+
+ let groups = this._chunksToGroups(
+ collapsibleChunks,
+ state.lineNums.left + 1,
+ state.lineNums.right + 1);
+
+ if (this.context !== WHOLE_FILE) {
+ const hiddenStart = state.chunkIndex === 0 ? 0 : this.context;
+ const hiddenEnd = lineCount - (
+ firstUncollapsibleChunkIndex === chunks.length ?
+ 0 : this.context);
+ groups = GrDiffGroup.hideInContextControl(
+ groups, hiddenStart, hiddenEnd);
}
- // If there is a range to hide.
- if (context !== WHOLE_FILE && hiddenRange[1] - hiddenRange[0] > 1) {
- const linesBeforeCtx = lines.slice(0, hiddenRange[0]);
- const hiddenLines = lines.slice(hiddenRange[0], hiddenRange[1]);
- const linesAfterCtx = lines.slice(hiddenRange[1]);
-
- if (linesBeforeCtx.length > 0) {
- result.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, linesBeforeCtx));
- }
-
- const ctxLine = new GrDiffLine(GrDiffLine.Type.CONTEXT_CONTROL);
- ctxLine.contextGroup =
- new GrDiffGroup(GrDiffGroup.Type.BOTH, hiddenLines);
- result.push(new GrDiffGroup(GrDiffGroup.Type.CONTEXT_CONTROL,
- [ctxLine]));
+ return {
+ lineDelta: {
+ left: lineCount,
+ right: lineCount,
+ },
+ groups,
+ newChunkIndex: firstUncollapsibleChunkIndex,
+ };
+ },
- if (linesAfterCtx.length > 0) {
- result.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, linesAfterCtx));
- }
- } else {
- result.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, lines));
- }
+ _commonChunkLength(chunk) {
+ console.assert(chunk.ab || chunk.common);
+ console.assert(
+ !chunk.a || (chunk.b && chunk.a.length === chunk.b.length),
+ `common chunk needs same number of a and b lines: `, chunk);
+ return this._linesLeft(chunk).length;
+ },
- return result;
+ /**
+ * @param {!Array<!Object>} chunks
+ * @param {number} offsetLeft
+ * @param {number} offsetRight
+ * @return {!Array<!Object>} (GrDiffGroup)
+ */
+ _chunksToGroups(chunks, offsetLeft, offsetRight) {
+ return chunks.map(chunk => {
+ const group = this._chunkToGroup(chunk, offsetLeft, offsetRight);
+ const chunkLength = this._commonChunkLength(chunk);
+ offsetLeft += chunkLength;
+ offsetRight += chunkLength;
+ return group;
+ });
},
/**
- * Take the rows of a delta diff section and produce the corresponding
- * group.
- *
- * @param {!Array<string>} rowsAdded
- * @param {!Array<string>} rowsRemoved
- * @param {number} startLineNumLeft
- * @param {number} startLineNumRight
- * @return {!Object} (Gr-Diff-Group)
+ * @param {!Object} chunk
+ * @param {number} offsetLeft
+ * @param {number} offsetRight
+ * @return {!Object} (GrDiffGroup)
*/
- _deltaGroupFromRows(rowsAdded, rowsRemoved, startLineNumLeft,
- startLineNumRight, highlights) {
+ _chunkToGroup(chunk, offsetLeft, offsetRight) {
+ const type = chunk.ab ? GrDiffGroup.Type.BOTH : GrDiffGroup.Type.DELTA;
+ const lines = this._linesFromChunk(chunk, offsetLeft, offsetRight);
+ const group = new GrDiffGroup(type, lines);
+ group.keyLocation = chunk.keyLocation;
+ group.dueToRebase = chunk.due_to_rebase;
+ group.ignoredWhitespaceOnly = chunk.common;
+ return group;
+ },
+
+ _linesFromChunk(chunk, offsetLeft, offsetRight) {
+ if (chunk.ab) {
+ return chunk.ab.map((row, i) => this._lineFromRow(
+ GrDiffLine.Type.BOTH, offsetLeft, offsetRight, row, i));
+ }
let lines = [];
- if (rowsRemoved) {
- lines = lines.concat(this._deltaLinesFromRows(GrDiffLine.Type.REMOVE,
- rowsRemoved, startLineNumLeft, highlights.removed));
+ if (chunk.a) {
+ // Avoiding a.push(...b) because that causes callstack overflows for
+ // large b, which can occur when large files are added removed.
+ lines = lines.concat(this._linesFromRows(
+ GrDiffLine.Type.REMOVE, chunk.a, offsetLeft,
+ chunk[DiffHighlights.REMOVED]));
}
- if (rowsAdded) {
- lines = lines.concat(this._deltaLinesFromRows(GrDiffLine.Type.ADD,
- rowsAdded, startLineNumRight, highlights.added));
+ if (chunk.b) {
+ // Avoiding a.push(...b) because that causes callstack overflows for
+ // large b, which can occur when large files are added removed.
+ lines = lines.concat(this._linesFromRows(
+ GrDiffLine.Type.ADD, chunk.b, offsetRight,
+ chunk[DiffHighlights.ADDED]));
}
- return new GrDiffGroup(GrDiffGroup.Type.DELTA, lines);
+ return lines;
},
/**
- * @return {!Array<!Object>} Array of GrDiffLines
+ * @param {string} lineType (GrDiffLine.Type)
+ * @param {!Array<string>} rows
+ * @param {number} offset
+ * @param {?Array<!Gerrit.IntralineInfo>=} opt_intralineInfos
+ * @return {!Array<!Object>} (GrDiffLine)
*/
- _deltaLinesFromRows(lineType, rows, startLineNum,
- opt_highlights) {
- // Normalize highlights if they have been passed.
- if (opt_highlights) {
- opt_highlights = this._normalizeIntralineHighlights(rows,
- opt_highlights);
- }
+ _linesFromRows(lineType, rows, offset, opt_intralineInfos) {
+ const grDiffHighlights = opt_intralineInfos ?
+ this._convertIntralineInfos(rows, opt_intralineInfos) : undefined;
+ return rows.map((row, i) => this._lineFromRow(
+ lineType, offset, offset, row, i, grDiffHighlights));
+ },
- const lines = [];
- let line;
- for (let i = 0; i < rows.length; i++) {
- line = new GrDiffLine(lineType);
- line.text = rows[i];
- if (lineType === GrDiffLine.Type.ADD) {
- line.afterNumber = ++startLineNum;
- } else {
- line.beforeNumber = ++startLineNum;
- }
- if (opt_highlights) {
- line.highlights = opt_highlights.filter(hl => hl.contentIndex === i);
- }
- lines.push(line);
+ /**
+ * @param {string} type (GrDiffLine.Type)
+ * @param {number} offsetLeft
+ * @param {number} offsetRight
+ * @param {string} row
+ * @param {number} i
+ * @param {!Array<!Object>=} opt_highlights
+ * @return {!Object} (GrDiffLine)
+ */
+ _lineFromRow(type, offsetLeft, offsetRight, row, i, opt_highlights) {
+ const line = new GrDiffLine(type);
+ line.text = row;
+ if (type !== GrDiffLine.Type.ADD) line.beforeNumber = offsetLeft + i;
+ if (type !== GrDiffLine.Type.REMOVE) line.afterNumber = offsetRight + i;
+ if (opt_highlights) {
+ line.hasIntralineInfo = true;
+ line.highlights = opt_highlights.filter(hl => hl.contentIndex === i);
+ } else {
+ line.hasIntralineInfo = false;
}
- return lines;
+ return line;
},
_makeFileComments() {
@@ -402,16 +429,16 @@
* into 2 chunks, one max sized one and the rest (for reasons that are
* unclear to me).
*
- * @param {!Array<!Object>} chunks Chunks as returned from the server
- * @return {!Array<!Object>} Finer grained chunks.
+ * @param {!Array<!Gerrit.DiffChunk>} chunks Chunks as returned from the server
+ * @return {!Array<!Gerrit.DiffChunk>} Finer grained chunks.
*/
_splitLargeChunks(chunks) {
const newChunks = [];
for (const chunk of chunks) {
if (!chunk.ab) {
- for (const group of this._breakdownGroup(chunk)) {
- newChunks.push(group);
+ for (const subChunk of this._breakdownChunk(chunk)) {
+ newChunks.push(subChunk);
}
continue;
}
@@ -421,7 +448,7 @@
// enabled for any other context preference because manipulating the
// chunks in this way violates assumptions by the context grouper logic.
if (this.context === -1 && chunk.ab.length > MAX_GROUP_SIZE * 2) {
- // Split large shared groups in two, where the first is the maximum
+ // Split large shared chunks in two, where the first is the maximum
// group size.
newChunks.push({ab: chunk.ab.slice(0, MAX_GROUP_SIZE)});
newChunks.push({ab: chunk.ab.slice(MAX_GROUP_SIZE)});
@@ -433,21 +460,21 @@
},
/**
- * In order to show comments out of the bounds of the selected context,
- * treat them as separate chunks within the model so that the content (and
- * context surrounding it) renders correctly.
+ * In order to show key locations, such as comments, out of the bounds of
+ * the selected context, treat them as separate chunks within the model so
+ * that the content (and context surrounding it) renders correctly.
*
* @param {!Array<!Object>} chunks DiffContents as returned from server.
* @return {!Array<!Object>} Finer grained DiffContents.
*/
- _splitUnchangedChunksWithComments(chunks) {
+ _splitCommonChunksWithKeyLocations(chunks) {
const result = [];
- let leftLineNum = 0;
- let rightLineNum = 0;
+ let leftLineNum = 1;
+ let rightLineNum = 1;
for (const chunk of chunks) {
// If it isn't a common chunk, append it as-is and update line numbers.
- if (!chunk.ab) {
+ if (!chunk.ab && !chunk.common) {
if (chunk.a) {
leftLineNum += chunk.a.length;
}
@@ -458,87 +485,111 @@
continue;
}
- let currentChunk = {ab: []};
-
- // For each line in the common group.
- for (const line of chunk.ab) {
- leftLineNum++;
- rightLineNum++;
-
- // If this line should not be collapsed.
- if (this.keyLocations[DiffSide.LEFT][leftLineNum] ||
- this.keyLocations[DiffSide.RIGHT][rightLineNum]) {
- // If any lines have been accumulated into the chunk leading up to
- // this non-collapse line, then add them as a chunk and start a new
- // one.
- if (currentChunk.ab && currentChunk.ab.length > 0) {
- result.push(currentChunk);
- currentChunk = {ab: []};
- }
+ if (chunk.common && chunk.a.length != chunk.b.length) {
+ throw new Error(
+ 'DiffContent with common=true must always have equal length');
+ }
+ const numLines = this._commonChunkLength(chunk);
+ const chunkEnds = this._findChunkEndsAtKeyLocations(
+ numLines, leftLineNum, rightLineNum);
+ leftLineNum += numLines;
+ rightLineNum += numLines;
+
+ if (chunk.ab) {
+ result.push(...this._splitAtChunkEnds(chunk.ab, chunkEnds)
+ .map(({lines, keyLocation}) =>
+ Object.assign({}, chunk, {ab: lines, keyLocation})));
+ } else if (chunk.common) {
+ const aChunks = this._splitAtChunkEnds(chunk.a, chunkEnds);
+ const bChunks = this._splitAtChunkEnds(chunk.b, chunkEnds);
+ result.push(...aChunks.map(({lines, keyLocation}, i) =>
+ Object.assign(
+ {}, chunk, {a: lines, b: bChunks[i].lines, keyLocation})));
+ }
+ }
+
+ return result;
+ },
- // Add the non-collapse line as its own chunk.
- result.push({ab: [line]});
- } else {
- // Append the current line to the current chunk.
- currentChunk.ab.push(line);
+ /**
+ * @return {!Array<{offset: number, keyLocation: boolean}>} Offsets of the
+ * new chunk ends, including whether it's a key location.
+ */
+ _findChunkEndsAtKeyLocations(numLines, leftOffset, rightOffset) {
+ const result = [];
+ let lastChunkEnd = 0;
+ for (let i=0; i<numLines; i++) {
+ // If this line should not be collapsed.
+ if (this.keyLocations[DiffSide.LEFT][leftOffset + i] ||
+ this.keyLocations[DiffSide.RIGHT][rightOffset + i]) {
+ // If any lines have been accumulated into the chunk leading up to
+ // this non-collapse line, then add them as a chunk and start a new
+ // one.
+ if (i > lastChunkEnd) {
+ result.push({offset: i, keyLocation: false});
+ lastChunkEnd = i;
}
- }
- if (currentChunk.ab && currentChunk.ab.length > 0) {
- result.push(currentChunk);
+ // Add the non-collapse line as its own chunk.
+ result.push({offset: i + 1, keyLocation: true});
}
}
+ if (numLines > lastChunkEnd) {
+ result.push({offset: numLines, keyLocation: false});
+ }
+
+ return result;
+ },
+
+ _splitAtChunkEnds(lines, chunkEnds) {
+ const result = [];
+ let lastChunkEndOffset = 0;
+ for (const {offset, keyLocation} of chunkEnds) {
+ result.push(
+ {lines: lines.slice(lastChunkEndOffset, offset), keyLocation});
+ lastChunkEndOffset = offset;
+ }
return result;
},
/**
- * The `highlights` array consists of a list of <skip length, mark length>
- * pairs, where the skip length is the number of characters between the
- * end of the previous edit and the start of this edit, and the mark
- * length is the number of edited characters following the skip. The start
- * of the edits is from the beginning of the related diff content lines.
+ * Converts `IntralineInfo`s return by the API to `GrLineHighlights` used
+ * for rendering.
*
- * Note that the implied newline character at the end of each line is
- * included in the length calculation, and thus it is possible for the
- * edits to span newlines.
- *
- * A line highlight object consists of three fields:
- * - contentIndex: The index of the diffChunk `content` field (the line
- * being referred to).
- * - startIndex: Where the highlight should begin.
- * - endIndex: (optional) Where the highlight should end. If omitted, the
- * highlight is meant to be a continuation onto the next line.
+ * @param {!Array<string>} rows
+ * @param {!Array<!Gerrit.IntralineInfo>} intralineInfos
+ * @return {!Array<!Object>} (GrDiffLine.Highlight)
*/
- _normalizeIntralineHighlights(content, highlights) {
- let contentIndex = 0;
+ _convertIntralineInfos(rows, intralineInfos) {
+ let rowIndex = 0;
let idx = 0;
const normalized = [];
- for (const hl of highlights) {
- let line = content[contentIndex] + '\n';
+ for (const [skipLength, markLength] of intralineInfos) {
+ let line = rows[rowIndex] + '\n';
let j = 0;
- while (j < hl[0]) {
+ while (j < skipLength) {
if (idx === line.length) {
idx = 0;
- line = content[++contentIndex] + '\n';
+ line = rows[++rowIndex] + '\n';
continue;
}
idx++;
j++;
}
let lineHighlight = {
- contentIndex,
+ contentIndex: rowIndex,
startIndex: idx,
};
j = 0;
- while (line && j < hl[1]) {
+ while (line && j < markLength) {
if (idx === line.length) {
idx = 0;
- line = content[++contentIndex] + '\n';
+ line = rows[++rowIndex] + '\n';
normalized.push(lineHighlight);
lineHighlight = {
- contentIndex,
+ contentIndex: rowIndex,
startIndex: idx,
};
continue;
@@ -554,32 +605,32 @@
/**
* If a group is an addition or a removal, break it down into smaller groups
- * of that type using the MAX_GROUP_SIZE. If the group is a shared section
+ * of that type using the MAX_GROUP_SIZE. If the group is a shared chunk
* or a delta it is returned as the single element of the result array.
*
- * @param {!Object} group A raw chunk from a diff response.
+ * @param {!Gerrit.DiffChunk} chunk A raw chunk from a diff response.
* @return {!Array<!Array<!Object>>}
*/
- _breakdownGroup(group) {
+ _breakdownChunk(chunk) {
let key = null;
- if (group.a && !group.b) {
+ if (chunk.a && !chunk.b) {
key = 'a';
- } else if (group.b && !group.a) {
+ } else if (chunk.b && !chunk.a) {
key = 'b';
- } else if (group.ab) {
+ } else if (chunk.ab) {
key = 'ab';
}
- if (!key) { return [group]; }
+ if (!key) { return [chunk]; }
- return this._breakdown(group[key], MAX_GROUP_SIZE)
- .map(subgroupLines => {
- const subGroup = {};
- subGroup[key] = subgroupLines;
- if (group.due_to_rebase) {
- subGroup.due_to_rebase = true;
+ return this._breakdown(chunk[key], MAX_GROUP_SIZE)
+ .map(subChunkLines => {
+ const subChunk = {};
+ subChunk[key] = subChunkLines;
+ if (chunk.due_to_rebase) {
+ subChunk.due_to_rebase = true;
}
- return subGroup;
+ return subChunk;
});
},
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
index 186a49e6a7..c04b066689 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-processor test</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-diff-processor.html">
@@ -60,7 +62,7 @@ limitations under the License.
element.context = 4;
});
- test('process loaded content', done => {
+ test('process loaded content', () => {
const content = [
{
ab: [
@@ -86,7 +88,7 @@ limitations under the License.
},
];
- element.process(content).then(() => {
+ return element.process(content).then(() => {
const groups = element.groups;
assert.equal(groups.length, 4);
@@ -139,31 +141,15 @@ limitations under the License.
'everyone pretend to shower.',
'Fry: Same as every day. Got it.',
]);
-
- done();
});
});
- test('insert context groups', done => {
+ test('first group is for file', () => {
const content = [
- {ab: []},
- {a: ['all work and no play make andybons a dull boy']},
- {ab: []},
- {b: ['elgoog elgoog elgoog']},
- {ab: []},
+ {b: ['foo']},
];
- for (let i = 0; i < 100; i++) {
- content[0].ab.push('all work and no play make jack a dull boy');
- content[4].ab.push('all work and no play make jill a dull girl');
- }
- for (let i = 0; i < 5; i++) {
- content[2].ab.push('no tv and no beer make homer go crazy');
- }
-
- const context = 10;
- element.context = context;
-
- element.process(content).then(() => {
+
+ return element.process(content).then(() => {
const groups = element.groups;
assert.equal(groups[0].type, GrDiffGroup.Type.BOTH);
@@ -171,107 +157,278 @@ limitations under the License.
assert.equal(groups[0].lines[0].text, '');
assert.equal(groups[0].lines[0].beforeNumber, GrDiffLine.FILE);
assert.equal(groups[0].lines[0].afterNumber, GrDiffLine.FILE);
-
- assert.equal(groups[1].type, GrDiffGroup.Type.CONTEXT_CONTROL);
- assert.instanceOf(groups[1].lines[0].contextGroup, GrDiffGroup);
- assert.equal(groups[1].lines[0].contextGroup.lines.length, 90);
- for (const l of groups[1].lines[0].contextGroup.lines) {
- assert.equal(l.text, content[0].ab[0]);
- }
-
- assert.equal(groups[2].type, GrDiffGroup.Type.BOTH);
- assert.equal(groups[2].lines.length, context);
- for (const l of groups[2].lines) {
- assert.equal(l.text, content[0].ab[0]);
- }
-
- assert.equal(groups[3].type, GrDiffGroup.Type.DELTA);
- assert.equal(groups[3].lines.length, 1);
- assert.equal(groups[3].removes.length, 1);
- assert.equal(groups[3].removes[0].text,
- 'all work and no play make andybons a dull boy');
-
- assert.equal(groups[4].type, GrDiffGroup.Type.BOTH);
- assert.equal(groups[4].lines.length, 5);
- for (const l of groups[4].lines) {
- assert.equal(l.text, content[2].ab[0]);
- }
-
- assert.equal(groups[5].type, GrDiffGroup.Type.DELTA);
- assert.equal(groups[5].lines.length, 1);
- assert.equal(groups[5].adds.length, 1);
- assert.equal(groups[5].adds[0].text, 'elgoog elgoog elgoog');
-
- assert.equal(groups[6].type, GrDiffGroup.Type.BOTH);
- assert.equal(groups[6].lines.length, context);
- for (const l of groups[6].lines) {
- assert.equal(l.text, content[4].ab[0]);
- }
-
- assert.equal(groups[7].type, GrDiffGroup.Type.CONTEXT_CONTROL);
- assert.instanceOf(groups[7].lines[0].contextGroup, GrDiffGroup);
- assert.equal(groups[7].lines[0].contextGroup.lines.length, 90);
- for (const l of groups[7].lines[0].contextGroup.lines) {
- assert.equal(l.text, content[4].ab[0]);
- }
-
- done();
});
});
- test('insert context groups', done => {
- const content = [
- {a: ['all work and no play make andybons a dull boy']},
- {ab: []},
- {b: ['elgoog elgoog elgoog']},
- ];
- for (let i = 0; i < 50; i++) {
- content[1].ab.push('no tv and no beer make homer go crazy');
- }
-
- const context = 10;
- element.context = context;
-
- element.process(content).then(() => {
- const groups = element.groups;
-
- assert.equal(groups[0].type, GrDiffGroup.Type.BOTH);
- assert.equal(groups[0].lines.length, 1);
- assert.equal(groups[0].lines[0].text, '');
- assert.equal(groups[0].lines[0].beforeNumber, GrDiffLine.FILE);
- assert.equal(groups[0].lines[0].afterNumber, GrDiffLine.FILE);
+ suite('context groups', () => {
+ test('at the beginning, larger than context', () => {
+ element.context = 10;
+ const content = [
+ {ab: new Array(100)
+ .fill('all work and no play make jack a dull boy')},
+ {a: ['all work and no play make andybons a dull boy']},
+ ];
+
+ return element.process(content).then(() => {
+ const groups = element.groups;
+
+ // group[0] is the file group
+
+ assert.equal(groups[1].type, GrDiffGroup.Type.CONTEXT_CONTROL);
+ assert.instanceOf(groups[1].lines[0].contextGroups[0], GrDiffGroup);
+ assert.equal(groups[1].lines[0].contextGroups[0].lines.length, 90);
+ for (const l of groups[1].lines[0].contextGroups[0].lines) {
+ assert.equal(l.text, 'all work and no play make jack a dull boy');
+ }
+
+ assert.equal(groups[2].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[2].lines.length, 10);
+ for (const l of groups[2].lines) {
+ assert.equal(l.text, 'all work and no play make jack a dull boy');
+ }
+ });
+ });
- assert.equal(groups[1].type, GrDiffGroup.Type.DELTA);
- assert.equal(groups[1].lines.length, 1);
- assert.equal(groups[1].removes.length, 1);
- assert.equal(groups[1].removes[0].text,
- 'all work and no play make andybons a dull boy');
+ test('at the beginning, smaller than context', () => {
+ element.context = 10;
+ const content = [
+ {ab: new Array(5)
+ .fill('all work and no play make jack a dull boy')},
+ {a: ['all work and no play make andybons a dull boy']},
+ ];
+
+ return element.process(content).then(() => {
+ const groups = element.groups;
+
+ // group[0] is the file group
+
+ assert.equal(groups[1].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[1].lines.length, 5);
+ for (const l of groups[1].lines) {
+ assert.equal(l.text, 'all work and no play make jack a dull boy');
+ }
+ });
+ });
- assert.equal(groups[2].type, GrDiffGroup.Type.BOTH);
- assert.equal(groups[2].lines.length, context);
- for (const l of groups[2].lines) {
- assert.equal(l.text, content[1].ab[0]);
- }
+ test('at the end, larger than context', () => {
+ element.context = 10;
+ const content = [
+ {a: ['all work and no play make andybons a dull boy']},
+ {ab: new Array(100)
+ .fill('all work and no play make jill a dull girl')},
+ ];
+
+ return element.process(content).then(() => {
+ const groups = element.groups;
+
+ // group[0] is the file group
+ // group[1] is the "a" group
+
+ assert.equal(groups[2].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[2].lines.length, 10);
+ for (const l of groups[2].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+
+ assert.equal(groups[3].type, GrDiffGroup.Type.CONTEXT_CONTROL);
+ assert.instanceOf(groups[3].lines[0].contextGroups[0], GrDiffGroup);
+ assert.equal(groups[3].lines[0].contextGroups[0].lines.length, 90);
+ for (const l of groups[3].lines[0].contextGroups[0].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+ });
+ });
- assert.equal(groups[3].type, GrDiffGroup.Type.CONTEXT_CONTROL);
- assert.instanceOf(groups[3].lines[0].contextGroup, GrDiffGroup);
- assert.equal(groups[3].lines[0].contextGroup.lines.length, 30);
- for (const l of groups[3].lines[0].contextGroup.lines) {
- assert.equal(l.text, content[1].ab[0]);
- }
+ test('at the end, smaller than context', () => {
+ element.context = 10;
+ const content = [
+ {a: ['all work and no play make andybons a dull boy']},
+ {ab: new Array(5)
+ .fill('all work and no play make jill a dull girl')},
+ ];
+
+ return element.process(content).then(() => {
+ const groups = element.groups;
+
+ // group[0] is the file group
+ // group[1] is the "a" group
+
+ assert.equal(groups[2].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[2].lines.length, 5);
+ for (const l of groups[2].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+ });
+ });
- assert.equal(groups[4].type, GrDiffGroup.Type.BOTH);
- assert.equal(groups[4].lines.length, context);
- for (const l of groups[4].lines) {
- assert.equal(l.text, content[1].ab[0]);
- }
+ test('for interleaved ab and common: true chunks', () => {
+ element.context = 10;
+ const content = [
+ {a: ['all work and no play make andybons a dull boy']},
+ {ab: new Array(3)
+ .fill('all work and no play make jill a dull girl')},
+ {
+ a: new Array(3).fill(
+ 'all work and no play make jill a dull girl'),
+ b: new Array(3).fill(
+ ' all work and no play make jill a dull girl'),
+ common: true,
+ },
+ {ab: new Array(3)
+ .fill('all work and no play make jill a dull girl')},
+ {
+ a: new Array(3).fill(
+ 'all work and no play make jill a dull girl'),
+ b: new Array(3).fill(
+ ' all work and no play make jill a dull girl'),
+ common: true,
+ },
+ {ab: new Array(3)
+ .fill('all work and no play make jill a dull girl')},
+ ];
+
+ return element.process(content).then(() => {
+ const groups = element.groups;
+
+ // group[0] is the file group
+ // group[1] is the "a" group
+
+ // The first three interleaved chunks are completely shown because
+ // they are part of the context (3 * 3 <= 10)
+
+ assert.equal(groups[2].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[2].lines.length, 3);
+ for (const l of groups[2].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+
+ assert.equal(groups[3].type, GrDiffGroup.Type.DELTA);
+ assert.equal(groups[3].lines.length, 6);
+ assert.equal(groups[3].adds.length, 3);
+ assert.equal(groups[3].removes.length, 3);
+ for (const l of groups[3].removes) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+ for (const l of groups[3].adds) {
+ assert.equal(
+ l.text, ' all work and no play make jill a dull girl');
+ }
+
+ assert.equal(groups[4].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[4].lines.length, 3);
+ for (const l of groups[4].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+
+ // The next chunk is partially shown, so it results in two groups
+
+ assert.equal(groups[5].type, GrDiffGroup.Type.DELTA);
+ assert.equal(groups[5].lines.length, 2);
+ assert.equal(groups[5].adds.length, 1);
+ assert.equal(groups[5].removes.length, 1);
+ for (const l of groups[5].removes) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+ for (const l of groups[5].adds) {
+ assert.equal(
+ l.text, ' all work and no play make jill a dull girl');
+ }
+
+ assert.equal(groups[6].type, GrDiffGroup.Type.CONTEXT_CONTROL);
+ assert.equal(groups[6].lines[0].contextGroups.length, 2);
+
+ assert.equal(groups[6].lines[0].contextGroups[0].lines.length, 4);
+ assert.equal(groups[6].lines[0].contextGroups[0].removes.length, 2);
+ assert.equal(groups[6].lines[0].contextGroups[0].adds.length, 2);
+ for (const l of groups[6].lines[0].contextGroups[0].removes) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+ for (const l of groups[6].lines[0].contextGroups[0].adds) {
+ assert.equal(
+ l.text, ' all work and no play make jill a dull girl');
+ }
+
+ // The final chunk is completely hidden
+ assert.equal(
+ groups[6].lines[0].contextGroups[1].type,
+ GrDiffGroup.Type.BOTH);
+ assert.equal(groups[6].lines[0].contextGroups[1].lines.length, 3);
+ for (const l of groups[6].lines[0].contextGroups[1].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+ });
+ });
- assert.equal(groups[5].type, GrDiffGroup.Type.DELTA);
- assert.equal(groups[5].lines.length, 1);
- assert.equal(groups[5].adds.length, 1);
- assert.equal(groups[5].adds[0].text, 'elgoog elgoog elgoog');
+ test('in the middle, larger than context', () => {
+ element.context = 10;
+ const content = [
+ {a: ['all work and no play make andybons a dull boy']},
+ {ab: new Array(100)
+ .fill('all work and no play make jill a dull girl')},
+ {a: ['all work and no play make andybons a dull boy']},
+ ];
+
+ return element.process(content).then(() => {
+ const groups = element.groups;
+
+ // group[0] is the file group
+ // group[1] is the "a" group
+
+ assert.equal(groups[2].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[2].lines.length, 10);
+ for (const l of groups[2].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+
+ assert.equal(groups[3].type, GrDiffGroup.Type.CONTEXT_CONTROL);
+ assert.instanceOf(groups[3].lines[0].contextGroups[0], GrDiffGroup);
+ assert.equal(groups[3].lines[0].contextGroups[0].lines.length, 80);
+ for (const l of groups[3].lines[0].contextGroups[0].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+
+ assert.equal(groups[4].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[4].lines.length, 10);
+ for (const l of groups[4].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+ });
+ });
- done();
+ test('in the middle, smaller than context', () => {
+ element.context = 10;
+ const content = [
+ {a: ['all work and no play make andybons a dull boy']},
+ {ab: new Array(5)
+ .fill('all work and no play make jill a dull girl')},
+ {a: ['all work and no play make andybons a dull boy']},
+ ];
+
+ return element.process(content).then(() => {
+ const groups = element.groups;
+
+ // group[0] is the file group
+ // group[1] is the "a" group
+
+ assert.equal(groups[2].type, GrDiffGroup.Type.BOTH);
+ assert.equal(groups[2].lines.length, 5);
+ for (const l of groups[2].lines) {
+ assert.equal(
+ l.text, 'all work and no play make jill a dull girl');
+ }
+ });
});
});
@@ -303,10 +460,11 @@ limitations under the License.
},
];
const result =
- element._splitUnchangedChunksWithComments(content);
+ element._splitCommonChunksWithKeyLocations(content);
assert.deepEqual(result, [
{
ab: ['Copyright (C) 2015 The Android Open Source Project'],
+ keyLocation: true,
},
{
ab: [
@@ -320,10 +478,12 @@ limitations under the License.
'',
'Unless required by applicable law or agreed to in writing, ',
],
+ keyLocation: false,
},
{
ab: [
'software distributed under the License is distributed on an '],
+ keyLocation: true,
},
{
ab: [
@@ -332,6 +492,7 @@ limitations under the License.
'language governing permissions and limitations under the ' +
'License.',
],
+ keyLocation: false,
},
]);
});
@@ -348,14 +509,16 @@ limitations under the License.
assert.deepEqual(result[1].ab, content[0].ab.slice(120));
});
- test('does not break-down shared chunks w/ context', () => {
+ test('does not break-down common chunks w/ context', () => {
const content = [{
ab: _.times(75, () => { return `${Math.random()}`; }),
}];
element.context = 4;
const result =
- element._splitUnchangedChunksWithComments(content);
- assert.deepEqual(result, content);
+ element._splitCommonChunksWithKeyLocations(content);
+ assert.equal(result.length, 1);
+ assert.deepEqual(result[0].ab, content[0].ab);
+ assert.isFalse(result[0].keyLocation);
});
test('intraline normalization', () => {
@@ -371,7 +534,7 @@ limitations under the License.
[31, 34], [42, 26],
];
- let results = element._normalizeIntralineHighlights(content,
+ let results = element._convertIntralineInfos(content,
highlights);
assert.deepEqual(results, [
{
@@ -412,7 +575,7 @@ limitations under the License.
[12, 67],
[14, 29],
];
- results = element._normalizeIntralineHighlights(content, highlights);
+ results = element._convertIntralineInfos(content, highlights);
assert.deepEqual(results, [
{
contentIndex: 0,
@@ -453,10 +616,13 @@ limitations under the License.
sandbox.stub(element, 'async');
element._isScrolling = true;
element.process(content);
+ // Just the files group - no more processing during scrolling.
assert.equal(element.groups.length, 1);
+
element._isScrolling = false;
element.process(content);
- assert.equal(element.groups.length, 33);
+ // More groups have been processed. How many does not matter here.
+ assert.isAtLeast(element.groups.length, 2);
});
test('image diffs', () => {
@@ -475,98 +641,210 @@ limitations under the License.
assert.equal(element.groups[0].lines.length, 1);
});
-
- suite('gr-diff-processor helpers', () => {
+ suite('_processNext', () => {
let rows;
setup(() => {
rows = loremIpsum.split(' ');
});
- test('_sharedGroupsFromRows WHOLE_FILE', () => {
- const context = WHOLE_FILE;
- const lineNumbers = {left: 10, right: 100};
- const result = element._sharedGroupsFromRows(
- rows, context, lineNumbers.left, lineNumbers.right, null);
+ test('WHOLE_FILE', () => {
+ element.context = WHOLE_FILE;
+ const state = {
+ lineNums: {left: 10, right: 100},
+ chunkIndex: 1,
+ };
+ const chunks = [
+ {a: ['foo']},
+ {ab: rows},
+ {a: ['bar']},
+ ];
+ const result = element._processNext(state, chunks);
// Results in one, uncollapsed group with all rows.
- assert.equal(result.length, 1);
- assert.equal(result[0].type, GrDiffGroup.Type.BOTH);
- assert.equal(result[0].lines.length, rows.length);
+ assert.equal(result.groups.length, 1);
+ assert.equal(result.groups[0].type, GrDiffGroup.Type.BOTH);
+ assert.equal(result.groups[0].lines.length, rows.length);
// Line numbers are set correctly.
- assert.equal(result[0].lines[0].beforeNumber, lineNumbers.left + 1);
- assert.equal(result[0].lines[0].afterNumber, lineNumbers.right + 1);
-
- assert.equal(result[0].lines[rows.length - 1].beforeNumber,
- lineNumbers.left + rows.length);
- assert.equal(result[0].lines[rows.length - 1].afterNumber,
- lineNumbers.right + rows.length);
+ assert.equal(
+ result.groups[0].lines[0].beforeNumber,
+ state.lineNums.left + 1);
+ assert.equal(
+ result.groups[0].lines[0].afterNumber,
+ state.lineNums.right + 1);
+
+ assert.equal(result.groups[0].lines[rows.length - 1].beforeNumber,
+ state.lineNums.left + rows.length);
+ assert.equal(result.groups[0].lines[rows.length - 1].afterNumber,
+ state.lineNums.right + rows.length);
});
- test('_sharedGroupsFromRows context', () => {
- const context = 10;
- const result = element._sharedGroupsFromRows(
- rows, context, 10, 100, null);
- const expectedCollapseSize = rows.length - 2 * context;
-
- assert.equal(result.length, 3, 'Results in three groups');
+ test('with context', () => {
+ element.context = 10;
+ const state = {
+ lineNums: {left: 10, right: 100},
+ chunkIndex: 1,
+ };
+ const chunks = [
+ {a: ['foo']},
+ {ab: rows},
+ {a: ['bar']},
+ ];
+ const result = element._processNext(state, chunks);
+ const expectedCollapseSize = rows.length - 2 * element.context;
+
+ assert.equal(result.groups.length, 3, 'Results in three groups');
// The first and last are uncollapsed context, whereas the middle has
// a single context-control line.
- assert.equal(result[0].lines.length, context);
- assert.equal(result[1].lines.length, 1);
- assert.equal(result[2].lines.length, context);
+ assert.equal(result.groups[0].lines.length, element.context);
+ assert.equal(result.groups[1].lines.length, 1);
+ assert.equal(result.groups[2].lines.length, element.context);
// The collapsed group has the hidden lines as its context group.
- assert.equal(result[1].lines[0].contextGroup.lines.length,
+ assert.equal(result.groups[1].lines[0].contextGroups[0].lines.length,
expectedCollapseSize);
});
- test('_sharedGroupsFromRows first', () => {
- const context = 10;
- const result = element._sharedGroupsFromRows(
- rows, context, 10, 100, 'first');
- const expectedCollapseSize = rows.length - context;
-
- assert.equal(result.length, 2, 'Results in two groups');
+ test('first', () => {
+ element.context = 10;
+ const state = {
+ lineNums: {left: 10, right: 100},
+ chunkIndex: 0,
+ };
+ const chunks = [
+ {ab: rows},
+ {a: ['foo']},
+ {a: ['bar']},
+ ];
+ const result = element._processNext(state, chunks);
+ const expectedCollapseSize = rows.length - element.context;
+
+ assert.equal(result.groups.length, 2, 'Results in two groups');
// Only the first group is collapsed.
- assert.equal(result[0].lines.length, 1);
- assert.equal(result[1].lines.length, context);
+ assert.equal(result.groups[0].lines.length, 1);
+ assert.equal(result.groups[1].lines.length, element.context);
// The collapsed group has the hidden lines as its context group.
- assert.equal(result[0].lines[0].contextGroup.lines.length,
+ assert.equal(result.groups[0].lines[0].contextGroups[0].lines.length,
expectedCollapseSize);
});
- test('_sharedGroupsFromRows few-rows', () => {
+ test('few-rows', () => {
// Only ten rows.
rows = rows.slice(0, 10);
- const context = 10;
- const result = element._sharedGroupsFromRows(
- rows, context, 10, 100, 'first');
+ element.context = 10;
+ const state = {
+ lineNums: {left: 10, right: 100},
+ chunkIndex: 0,
+ };
+ const chunks = [
+ {ab: rows},
+ {a: ['foo']},
+ {a: ['bar']},
+ ];
+ const result = element._processNext(state, chunks);
// Results in one uncollapsed group with all rows.
- assert.equal(result.length, 1, 'Results in one group');
- assert.equal(result[0].lines.length, rows.length);
+ assert.equal(result.groups.length, 1, 'Results in one group');
+ assert.equal(result.groups[0].lines.length, rows.length);
});
- test('_sharedGroupsFromRows no single line collapse', () => {
+ test('no single line collapse', () => {
rows = rows.slice(0, 7);
- const context = 3;
- const result = element._sharedGroupsFromRows(
- rows, context, 10, 100);
+ element.context = 3;
+ const state = {
+ lineNums: {left: 10, right: 100},
+ chunkIndex: 1,
+ };
+ const chunks = [
+ {a: ['foo']},
+ {ab: rows},
+ {a: ['bar']},
+ ];
+ const result = element._processNext(state, chunks);
// Results in one uncollapsed group with all rows.
- assert.equal(result.length, 1, 'Results in one group');
- assert.equal(result[0].lines.length, rows.length);
+ assert.equal(result.groups.length, 1, 'Results in one group');
+ assert.equal(result.groups[0].lines.length, rows.length);
+ });
+
+ suite('with key location', () => {
+ let state;
+ let chunks;
+
+ setup(() => {
+ state = {
+ lineNums: {left: 10, right: 100},
+ };
+ element.context = 10;
+ chunks = [
+ {ab: rows},
+ {ab: ['foo'], keyLocation: true},
+ {ab: rows},
+ ];
+ });
+
+ test('context before', () => {
+ state.chunkIndex = 0;
+ const result = element._processNext(state, chunks);
+
+ // The first chunk is split into two groups:
+ // 1) A context-control, hiding everything but the context before
+ // the key location.
+ // 2) The context before the key location.
+ // The key location is not processed in this call to _processNext
+ assert.equal(result.groups.length, 2);
+ assert.equal(result.groups[0].lines.length, 1);
+ // The collapsed group has the hidden lines as its context group.
+ assert.equal(result.groups[0].lines[0].contextGroups[0].lines.length,
+ rows.length - element.context);
+ assert.equal(result.groups[1].lines.length, element.context);
+ });
+
+ test('key location itself', () => {
+ state.chunkIndex = 1;
+ const result = element._processNext(state, chunks);
+
+ // The second chunk results in a single group, that is just the
+ // line with the key location
+ assert.equal(result.groups.length, 1);
+ assert.equal(result.groups[0].lines.length, 1);
+ assert.equal(result.lineDelta.left, 1);
+ assert.equal(result.lineDelta.right, 1);
+ });
+
+ test('context after', () => {
+ state.chunkIndex = 2;
+ const result = element._processNext(state, chunks);
+
+ // The last chunk is split into two groups:
+ // 1) The context after the key location.
+ // 1) A context-control, hiding everything but the context after the
+ // key location.
+ assert.equal(result.groups.length, 2);
+ assert.equal(result.groups[0].lines.length, element.context);
+ assert.equal(result.groups[1].lines.length, 1);
+ // The collapsed group has the hidden lines as its context group.
+ assert.equal(result.groups[1].lines[0].contextGroups[0].lines.length,
+ rows.length - element.context);
+ });
+ });
+ });
+
+ suite('gr-diff-processor helpers', () => {
+ let rows;
+
+ setup(() => {
+ rows = loremIpsum.split(' ');
});
- test('_deltaLinesFromRows', () => {
+ test('_linesFromRows', () => {
const startLineNum = 10;
- let result = element._deltaLinesFromRows(GrDiffLine.Type.ADD, rows,
- startLineNum);
+ let result = element._linesFromRows(GrDiffLine.Type.ADD, rows,
+ startLineNum + 1);
assert.equal(result.length, rows.length);
assert.equal(result[0].type, GrDiffLine.Type.ADD);
@@ -576,8 +854,8 @@ limitations under the License.
startLineNum + rows.length);
assert.notOk(result[result.length - 1].beforeNumber);
- result = element._deltaLinesFromRows(GrDiffLine.Type.REMOVE, rows,
- startLineNum);
+ result = element._linesFromRows(GrDiffLine.Type.REMOVE, rows,
+ startLineNum + 1);
assert.equal(result.length, rows.length);
assert.equal(result[0].type, GrDiffLine.Type.REMOVE);
@@ -590,19 +868,19 @@ limitations under the License.
});
suite('_breakdown*', () => {
- test('_breakdownGroup breaks down additions', () => {
+ test('_breakdownChunk breaks down additions', () => {
sandbox.spy(element, '_breakdown');
const chunk = {b: ['blah', 'blah', 'blah']};
- const result = element._breakdownGroup(chunk);
+ const result = element._breakdownChunk(chunk);
assert.deepEqual(result, [chunk]);
assert.isTrue(element._breakdown.called);
});
- test('_breakdownGroup keeps due_to_rebase for broken down additions',
+ test('_breakdownChunk keeps due_to_rebase for broken down additions',
() => {
sandbox.spy(element, '_breakdown');
const chunk = {b: ['blah', 'blah', 'blah'], due_to_rebase: true};
- const result = element._breakdownGroup(chunk);
+ const result = element._breakdownChunk(chunk);
for (const subResult of result) {
assert.isTrue(subResult.due_to_rebase);
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.html b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.html
index f9822f23ef..cfa46a0240 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.html
@@ -14,36 +14,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/dom-util-behavior/dom-util-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<script src="../../../scripts/util.js"></script>
<dom-module id="gr-diff-selection">
<template>
- <style include="shared-styles">
- .contentWrapper ::content .content,
- .contentWrapper ::content .contextControl,
- .contentWrapper ::content .blame {
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- }
-
- :host-context(.selected-left:not(.selected-comment)) .contentWrapper ::content .side-by-side .left + .content .contentText,
- :host-context(.selected-right:not(.selected-comment)) .contentWrapper ::content .side-by-side .right + .content .contentText,
- :host-context(.selected-left:not(.selected-comment)) .contentWrapper ::content .unified .left.lineNum ~ .content:not(.both) .contentText,
- :host-context(.selected-right:not(.selected-comment)) .contentWrapper ::content .unified .right.lineNum ~ .content .contentText,
- :host-context(.selected-left.selected-comment) .contentWrapper ::content .side-by-side .left + .content .message,
- :host-context(.selected-right.selected-comment) .contentWrapper ::content .side-by-side .right + .content .message :not(.collapsedContent),
- :host-context(.selected-comment) .contentWrapper ::content .unified .message :not(.collapsedContent),
- :host-context(.selected-blame) .contentWrapper ::content .blame {
- -webkit-user-select: text;
- -moz-user-select: text;
- -ms-user-select: text;
- user-select: text;
- }
- </style>
<div class="contentWrapper">
<slot></slot>
</div>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
index 3484693d9b..5caac74053 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
@@ -32,7 +32,6 @@
Polymer({
is: 'gr-diff-selection',
- _legacyUndefinedCheck: true,
properties: {
diff: Object,
@@ -73,7 +72,26 @@
this._linesCache = getNewCache();
},
+ _handleDownOnRangeComment(node) {
+ if (node &&
+ node.nodeName &&
+ node.nodeName.toLowerCase() === 'gr-comment-thread') {
+ this._setClasses([
+ SelectionClass.COMMENT,
+ node.commentSide === 'left' ?
+ SelectionClass.LEFT :
+ SelectionClass.RIGHT,
+ ]);
+ return true;
+ }
+ return false;
+ },
+
_handleDown(e) {
+ // Handle the down event on comment thread in Polymer 2
+ const handled = this._handleDownOnRangeComment(e.target);
+ if (handled) return;
+
const lineEl = this.diffBuilder.getLineElByChild(e.target);
const blameSelected = this._elementDescendedFromClass(e.target, 'blame');
if (!lineEl && !blameSelected) { return; }
@@ -161,7 +179,18 @@
},
/**
- * Get the text of the current window selection. If commentSelected is
+ * For Polymer 2, use shadowRoot.getSelection instead.
+ */
+ _getSelection() {
+ const diffHost = util.querySelector(document.body, 'gr-diff');
+ const selection = diffHost &&
+ diffHost.shadowRoot &&
+ diffHost.shadowRoot.getSelection();
+ return selection ? selection: window.getSelection();
+ },
+
+ /**
+ * Get the text of the current selection. If commentSelected is
* true, it returns only the text of comments within the selection.
* Otherwise it returns the text of the selected diff region.
*
@@ -170,7 +199,7 @@
* @return {string} The selected text.
*/
_getSelectedText(side, commentSelected) {
- const sel = window.getSelection();
+ const sel = this._getSelection();
if (sel.rangeCount != 1) {
return ''; // No multi-select support yet.
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
index 469a894ab5..0f5c6dde1a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-selection</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-diff-selection.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
index 5ebe73825f..17b8b4c5cf 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
@@ -15,12 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="/bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
@@ -70,7 +72,7 @@ limitations under the License.
justify-content: space-between;
}
header {
- padding: .75em var(--default-horizontal-margin);
+ padding: var(--spacing-s) var(--spacing-l);
}
.patchRangeLeft {
align-items: center;
@@ -85,11 +87,11 @@ limitations under the License.
white-space: nowrap;
}
.navLink {
- padding: 0 .25em;
+ padding: 0 var(--spacing-xs);
}
.reviewed {
display: inline-block;
- margin: 0 .25em;
+ margin: 0 var(--spacing-xs);
vertical-align: .15em;
}
.jumpToFileContainer {
@@ -99,22 +101,19 @@ limitations under the License.
display: none;
}
gr-button {
- padding: .3em 0;
+ padding: var(--spacing-s) 0;
text-decoration: none;
}
.loading {
color: var(--deemphasized-text-color);
- font-size: 2rem;
+ font-size: var(--font-size-h1);
height: 100%;
- padding: 1em var(--default-horizontal-margin);
+ padding: var(--spacing-l);
text-align: center;
}
.subHeader {
flex-wrap: wrap;
- margin: 0 var(--default-horizontal-margin) .75em;
- }
- .subHeader > div {
- margin-top: .25em;
+ padding: 0 var(--spacing-l) var(--spacing-s);
}
.prefsButton {
text-align: right;
@@ -145,7 +144,7 @@ limitations under the License.
}
.diffModeSelector span,
.editButton span {
- margin-right: .2rem;
+ margin-right: var(--spacing-xs);
}
.diffModeSelector.hide,
.separator.hide {
@@ -161,7 +160,7 @@ limitations under the License.
}
@media screen and (max-width: 50em) {
header {
- padding: .5em var(--default-horizontal-margin);
+ padding: var(--spacing-s) var(--spacing-l);
}
.dash {
display: none;
@@ -172,23 +171,23 @@ limitations under the License.
.fileNav {
align-items: flex-start;
display: flex;
- margin: 0 .25em;
+ margin: 0 var(--spacing-xs);
}
.fullFileName {
display: block;
font-style: italic;
min-width: 50%;
- padding: 0 .1em;
+ padding: 0 var(--spacing-xxs);
text-align: center;
width: 100%;
word-wrap: break-word;
}
.reviewed {
- vertical-align: -.1em;
+ vertical-align: -1px;
}
.mobileNavLink {
color: var(--primary-text-color);
- font-size: 1.5rem;
+ font-size: var(--font-size-h2);
font-weight: var(--font-weight-bold);
text-decoration: none;
}
@@ -287,7 +286,8 @@ limitations under the License.
<gr-button
link
disabled="[[_isBlameLoading]]"
- on-tap="_toggleBlame">[[_computeBlameToggleLabel(_isBlameLoaded, _isBlameLoading)]]</gr-button>
+ on-click="_toggleBlame">[[_computeBlameToggleLabel(_isBlameLoaded, _isBlameLoading)]]</gr-button>
+ <span class="separator"></span>
</span>
<template is="dom-if" if="[[_computeCanEdit(_loggedIn, _change.*)]]">
<span class="separator"></span>
@@ -314,13 +314,15 @@ limitations under the License.
class="prefsButton"
has-tooltip
title="Diff preferences"
- on-tap="_handlePrefsTap"><iron-icon icon="gr-icons:settings"></iron-icon></gr-button>
+ on-click="_handlePrefsTap"><iron-icon icon="gr-icons:settings"></iron-icon></gr-button>
</span>
</span>
<gr-endpoint-decorator name="annotation-toggler">
<span hidden id="annotation-span">
<label for="annotation-checkbox" id="annotation-label"></label>
- <input is="iron-input" type="checkbox" id="annotation-checkbox" disabled>
+ <iron-input type="checkbox" disabled>
+ <input is="iron-input" type="checkbox" id="annotation-checkbox" disabled>
+ </iron-input>
</span>
</gr-endpoint-decorator>
</div>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index d73042e5a9..bd0486feca 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -35,7 +35,6 @@
Polymer({
is: 'gr-diff-view',
- _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -102,14 +101,24 @@
// element for selected a file to view.
_formattedFiles: {
type: Array,
- computed: '_formatFilesForDropdown(_fileList, _patchRange.patchNum, ' +
+ computed: '_formatFilesForDropdown(_files, _patchRange.patchNum, ' +
'_changeComments)',
},
// An sorted array of files, as returned by the rest API.
_fileList: {
type: Array,
- value() { return []; },
+ computed: '_getSortedFileList(_files)',
},
+ /**
+ * Contains information about files as returned by the rest API.
+ *
+ * @type {{ sortedFileList: Array<string>, changeFilesByPath: Object }}
+ */
+ _files: {
+ type: Object,
+ value() { return {sortedFileList: [], changeFilesByPath: {}}; },
+ },
+
_path: {
type: String,
observer: '_pathChanged',
@@ -181,6 +190,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PatchSetBehavior,
Gerrit.PathListBehavior,
@@ -189,7 +199,7 @@
observers: [
'_getProjectConfig(_change.project)',
- '_getFiles(_changeNum, _patchRange.*)',
+ '_getFiles(_changeNum, _patchRange.*, _changeComments)',
'_setReviewedObserver(_loggedIn, params.*, _prefs)',
],
@@ -262,11 +272,31 @@
return this.$.restAPI.getChangeEdit(this._changeNum);
},
- _getFiles(changeNum, patchRangeRecord) {
+ _getSortedFileList(files) {
+ return files.sortedFileList;
+ },
+
+ _getFiles(changeNum, patchRangeRecord, changeComments) {
+ // Polymer 2: check for undefined
+ if ([changeNum, patchRangeRecord, patchRangeRecord.base, changeComments]
+ .some(arg => arg === undefined)) {
+ return Promise.resolve();
+ }
+
const patchRange = patchRangeRecord.base;
- return this.$.restAPI.getChangeFilePathsAsSpeciallySortedArray(
- changeNum, patchRange).then(files => {
- this._fileList = files;
+ return this.$.restAPI.getChangeFiles(
+ changeNum, patchRange).then(changeFiles => {
+ if (!changeFiles) return;
+ const commentedPaths = changeComments.getPaths(patchRange);
+ const files = Object.assign({}, changeFiles);
+ Object.keys(commentedPaths).forEach(commentedPath => {
+ if (files.hasOwnProperty(commentedPath)) { return; }
+ files[commentedPath] = {status: 'U'};
+ });
+ this._files = {
+ sortedFileList: Object.keys(files).sort(this.specialFilePathCompare),
+ changeFilesByPath: files,
+ };
});
},
@@ -562,7 +592,7 @@
* @return {?Object}
*/
_getNavLinkPath(path, fileList, direction, opt_noUp) {
- if (!path || fileList.length === 0) { return null; }
+ if (!path || !fileList || fileList.length === 0) { return null; }
let idx = fileList.indexOf(path);
if (idx === -1) {
@@ -608,11 +638,11 @@
this._initCursor(this.params);
this._changeNum = value.changeNum;
+ this._path = value.path;
this._patchRange = {
patchNum: value.patchNum,
basePatchNum: value.basePatchNum || PARENT,
};
- this._path = value.path;
// NOTE: This may be called before attachment (e.g. while parentElement is
// null). Fire title-change in an async so that, if attachment to the DOM
@@ -640,22 +670,24 @@
promises.push(this._getChangeDetail(this._changeNum).then(change => {
let commit;
let baseCommit;
- for (const commitSha in change.revisions) {
- if (!change.revisions.hasOwnProperty(commitSha)) continue;
- const revision = change.revisions[commitSha];
- const patchNum = revision._number.toString();
- if (patchNum === this._patchRange.patchNum) {
- commit = commitSha;
- const commitObj = revision.commit || {};
- const parents = commitObj.parents || [];
- if (this._patchRange.basePatchNum === PARENT && parents.length) {
- baseCommit = parents[parents.length - 1].commit;
+ if (change) {
+ for (const commitSha in change.revisions) {
+ if (!change.revisions.hasOwnProperty(commitSha)) continue;
+ const revision = change.revisions[commitSha];
+ const patchNum = revision._number.toString();
+ if (patchNum === this._patchRange.patchNum) {
+ commit = commitSha;
+ const commitObj = revision.commit || {};
+ const parents = commitObj.parents || [];
+ if (this._patchRange.basePatchNum === PARENT && parents.length) {
+ baseCommit = parents[parents.length - 1].commit;
+ }
+ } else if (patchNum === this._patchRange.basePatchNum) {
+ baseCommit = commitSha;
}
- } else if (patchNum === this._patchRange.basePatchNum) {
- baseCommit = commitSha;
}
+ this._commitRange = {commit, baseCommit};
}
- this._commitRange = {commit, baseCommit};
}));
promises.push(this._loadComments());
@@ -674,8 +706,10 @@
}
this._loading = false;
this.$.diffHost.comments = this._commentsForDiff;
- return this.$.diffHost.reload();
+ return this.$.diffHost.reload(true);
}).then(() => {
+ this.$.reporting.diffViewFullyLoaded();
+ // If diff view displayed has not ended yet, it ends here.
this.$.reporting.diffViewDisplayed();
});
},
@@ -690,6 +724,11 @@
},
_setReviewedObserver(_loggedIn, paramsRecord, _prefs) {
+ // Polymer 2: check for undefined
+ if ([_loggedIn, paramsRecord, _prefs].some(arg => arg === undefined)) {
+ return;
+ }
+
const params = paramsRecord.base || {};
if (!_loggedIn) { return; }
@@ -741,6 +780,9 @@
},
_getDiffUrl(change, patchRange, path) {
+ if ([change, patchRange, path].some(arg => arg === undefined)) {
+ return '';
+ }
return Gerrit.Nav.getUrlForDiff(change, path, patchRange.patchNum,
patchRange.basePatchNum);
},
@@ -779,6 +821,9 @@
},
_getChangePath(change, patchRange, revisions) {
+ if ([change, patchRange].some(arg => arg === undefined)) {
+ return '';
+ }
const range = this._getChangeUrlRange(patchRange, revisions);
return Gerrit.Nav.getUrlForChange(change, range.patchNum,
range.basePatchNum);
@@ -793,22 +838,31 @@
return this._getChangePath(change, patchRangeRecord.base, revisions);
},
- _formatFilesForDropdown(fileList, patchNum, changeComments) {
- if (!fileList) { return; }
+ _formatFilesForDropdown(files, patchNum, changeComments) {
+ // Polymer 2: check for undefined
+ if ([
+ files,
+ patchNum,
+ changeComments,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
+ if (!files) { return; }
const dropdownContent = [];
- for (const path of fileList) {
+ for (const path of files.sortedFileList) {
dropdownContent.push({
text: this.computeDisplayPath(path),
mobileText: this.computeTruncatedPath(path),
value: path,
bottomText: this._computeCommentString(changeComments, patchNum,
- path),
+ path, files.changeFilesByPath[path]),
});
}
return dropdownContent;
},
- _computeCommentString(changeComments, patchNum, path) {
+ _computeCommentString(changeComments, patchNum, path, changeFileInfo) {
const unresolvedCount = changeComments.computeUnresolvedNum(patchNum,
path);
const commentCount = changeComments.computeCommentCount(patchNum, path);
@@ -817,11 +871,13 @@
const unresolvedString = GrCountStringFormatter.computeString(
unresolvedCount, 'unresolved');
- return commentString +
- // Add a space if both comments and unresolved
- (commentString && unresolvedString ? ', ' : '') +
- // Add parentheses around unresolved if it exists.
- (unresolvedString ? `${unresolvedString}` : '');
+ const unmodifiedString = changeFileInfo.status === 'U' ? 'no changes': '';
+
+ return [
+ unmodifiedString,
+ commentString,
+ unresolvedString]
+ .filter(v => v && v.length > 0).join(', ');
},
_computePrefsButtonHidden(prefs, prefsDisabled) {
@@ -990,6 +1046,15 @@
},
_computeCommentSkips(commentMap, fileList, path) {
+ // Polymer 2: check for undefined
+ if ([
+ commentMap,
+ fileList,
+ path,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const skips = {previous: null, next: null};
if (!fileList.length) { return skips; }
const pathIndex = fileList.indexOf(path);
@@ -1070,6 +1135,11 @@
},
_computeFileNum(file, files) {
+ // Polymer 2: check for undefined
+ if ([file, files].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
return files.findIndex(({value}) => value === file) + 1;
},
@@ -1109,13 +1179,12 @@
this._getDiffPreferences();
},
- _computeIsLoggedIn(loggedIn) {
- return loggedIn ? true : false;
- },
-
_computeCanEdit(loggedIn, changeChangeRecord) {
- return this._computeIsLoggedIn(loggedIn) &&
- this.changeIsOpen(changeChangeRecord.base.status);
+ if ([changeChangeRecord, changeChangeRecord.base]
+ .some(arg => arg === undefined)) {
+ return false;
+ }
+ return loggedIn && this.changeIsOpen(changeChangeRecord.base);
},
});
})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
index b33c54cf22..9c59577da0 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<script src="../../../scripts/util.js"></script>
<link rel="import" href="gr-diff-view.html">
@@ -74,6 +76,17 @@ limitations under the License.
const PARENT = 'PARENT';
+ function getFilesFromFileList(fileList) {
+ const changeFilesByPath = fileList.reduce((files, path) => {
+ files[path] = {};
+ return files;
+ }, {});
+ return {
+ sortedFileList: fileList,
+ changeFilesByPath,
+ };
+ }
+
setup(() => {
sandbox = sinon.sandbox.create();
@@ -153,7 +166,8 @@ limitations under the License.
a: {_number: 10, commit: {parents: []}},
},
};
- element._fileList = ['chell.go', 'glados.txt', 'wheatley.md'];
+ element._files = getFilesFromFileList(
+ ['chell.go', 'glados.txt', 'wheatley.md']);
element._path = 'glados.txt';
element.changeViewState.selectedFileIndex = 1;
element._loggedIn = true;
@@ -260,7 +274,8 @@ limitations under the License.
b: {_number: 5, commit: {parents: []}},
},
};
- element._fileList = ['chell.go', 'glados.txt', 'wheatley.md'];
+ element._files = getFilesFromFileList(
+ ['chell.go', 'glados.txt', 'wheatley.md']);
element._path = 'glados.txt';
const diffNavStub = sandbox.stub(Gerrit.Nav, 'navigateToDiff');
@@ -324,7 +339,8 @@ limitations under the License.
b: {_number: 2, commit: {parents: []}},
},
};
- element._fileList = ['chell.go', 'glados.txt', 'wheatley.md'];
+ element._files = getFilesFromFileList(
+ ['chell.go', 'glados.txt', 'wheatley.md']);
element._path = 'glados.txt';
const diffNavStub = sandbox.stub(Gerrit.Nav, 'navigateToDiff');
@@ -517,13 +533,22 @@ limitations under the License.
unresolvedCountStub.withArgs(3, path).returns(2);
unresolvedCountStub.withArgs(4, path).returns(0);
- assert.equal(element._computeCommentString(comments, 1, path),
+ assert.equal(element._computeCommentString(comments, 1, path, {}),
'1 unresolved');
- assert.equal(element._computeCommentString(comments, 2, path),
+ assert.equal(
+ element._computeCommentString(comments, 2, path, {status: 'M'}),
'1 comment');
- assert.equal(element._computeCommentString(comments, 3, path),
+ assert.equal(
+ element._computeCommentString(comments, 2, path, {status: 'U'}),
+ 'no changes, 1 comment');
+ assert.equal(
+ element._computeCommentString(comments, 3, path, {status: 'A'}),
'2 comments, 2 unresolved');
- assert.equal(element._computeCommentString(comments, 4, path), '');
+ assert.equal(
+ element._computeCommentString(comments, 4, path, {status: 'M'}), '');
+ assert.equal(
+ element._computeCommentString(comments, 4, path, {status: 'U'}),
+ 'no changes');
done();
});
});
@@ -545,8 +570,9 @@ limitations under the License.
patchNum: '10',
};
element._change = {_number: 42};
- element._fileList = ['chell.go', 'glados.txt', 'wheatley.md',
- '/COMMIT_MSG', '/MERGE_LIST'];
+ element._files = getFilesFromFileList(
+ ['chell.go', 'glados.txt', 'wheatley.md',
+ '/COMMIT_MSG', '/MERGE_LIST']);
element._path = 'glados.txt';
const expectedFormattedFiles = [
{
@@ -595,7 +621,8 @@ limitations under the License.
a: {_number: 10, commit: {parents: []}},
},
};
- element._fileList = ['chell.go', 'glados.txt', 'wheatley.md'];
+ element._files = getFilesFromFileList(
+ ['chell.go', 'glados.txt', 'wheatley.md']);
element._path = 'glados.txt';
flushAsynchronousOperations();
const linkEls = Polymer.dom(element.root).querySelectorAll('.navLink');
@@ -637,7 +664,8 @@ limitations under the License.
b: {_number: 10, commit: {parents: []}},
},
};
- element._fileList = ['chell.go', 'glados.txt', 'wheatley.md'];
+ element._files = getFilesFromFileList(
+ ['chell.go', 'glados.txt', 'wheatley.md']);
element._path = 'glados.txt';
flushAsynchronousOperations();
const linkEls = Polymer.dom(element.root).querySelectorAll('.navLink');
@@ -1064,9 +1092,9 @@ limitations under the License.
setup(() => {
navToChangeStub = sandbox.stub(element, '_navToChangeView');
navToDiffStub = sandbox.stub(Gerrit.Nav, 'navigateToDiff');
- element._fileList = [
+ element._files = getFilesFromFileList([
'path/one.jpg', 'path/two.m4v', 'path/three.wav',
- ];
+ ]);
element._patchRange = {patchNum: '2', basePatchNum: '1'};
});
@@ -1217,7 +1245,7 @@ limitations under the License.
});
test('shift+m navigates to next unreviewed file', () => {
- element._fileList = ['file1', 'file2', 'file3'];
+ element._files = getFilesFromFileList(['file1', 'file2', 'file3']);
element._reviewedFiles = new Set(['file1', 'file2']);
element._path = 'file1';
const reviewedStub = sandbox.stub(element, '_setReviewed');
@@ -1233,6 +1261,39 @@ limitations under the License.
]);
});
+ test('File change should trigger navigateToDiff once', () => {
+ element._files = getFilesFromFileList(['file1', 'file2', 'file3']);
+ sandbox.stub(element, '_getLineOfInterest');
+ sandbox.stub(element, '_initCursor');
+ sandbox.stub(Gerrit.Nav, 'navigateToDiff');
+
+ // Load file1
+ element._paramsChanged({
+ view: Gerrit.Nav.View.DIFF,
+ patchNum: 1,
+ changeNum: 101,
+ project: 'test-project',
+ path: 'file1',
+ });
+ assert.isTrue(Gerrit.Nav.navigateToDiff.notCalled);
+
+ // Switch to file2
+ element.$.dropdown.value = 'file2';
+ assert.isTrue(Gerrit.Nav.navigateToDiff.calledOnce);
+
+ // This is to mock the param change triggered by above navigate
+ element._paramsChanged({
+ view: Gerrit.Nav.View.DIFF,
+ patchNum: 1,
+ changeNum: 101,
+ project: 'test-project',
+ path: 'file2',
+ });
+
+ // No extra call
+ assert.isTrue(Gerrit.Nav.navigateToDiff.calledOnce);
+ });
+
test('_computeDownloadDropdownLinks', () => {
const downloadLinks = [
{
@@ -1328,4 +1389,54 @@ limitations under the License.
'/changes/test~12/revisions/1/patch?zip&path=index.php');
});
});
+
+ suite('gr-diff-view tests unmodified files with comments', () => {
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ const changedFiles = {
+ 'file1.txt': {},
+ 'a/b/test.c': {},
+ };
+ stub('gr-rest-api-interface', {
+ getConfig() { return Promise.resolve({change: {}}); },
+ getLoggedIn() { return Promise.resolve(false); },
+ getProjectConfig() { return Promise.resolve({}); },
+ getDiffChangeDetail() { return Promise.resolve({}); },
+ getChangeFiles() { return Promise.resolve(changedFiles); },
+ saveFileReviewed() { return Promise.resolve(); },
+ getDiffComments() { return Promise.resolve({}); },
+ getDiffRobotComments() { return Promise.resolve({}); },
+ getDiffDrafts() { return Promise.resolve({}); },
+ getReviewedFiles() { return Promise.resolve([]); },
+ });
+ element = fixture('basic');
+ return element._loadComments();
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('_getFiles add files with comments without changes', () => {
+ const patchChangeRecord = {
+ base: {
+ basePatchNum: '5',
+ patchNum: '10',
+ },
+ };
+ const changeComments = {
+ getPaths: sandbox.stub().returns({'file2.txt': {}, 'file1.txt': {}}),
+ };
+ return element._getFiles(23, patchChangeRecord, changeComments).then(() => {
+ assert.deepEqual(element._files, {
+ sortedFileList: ['a/b/test.c', 'file1.txt', 'file2.txt'],
+ changeFilesByPath: {
+ 'file1.txt': {},
+ 'file2.txt': {status: 'U'},
+ 'a/b/test.c': {},
+ },
+ });
+ });
+ });
+ });
</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
index dd69724096..a7e391a7aa 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
@@ -22,20 +22,42 @@
/**
* A chunk of the diff that should be rendered together.
+ *
+ * @param {!GrDiffGroup.Type} type
+ * @param {!Array<!GrDiffLine>=} opt_lines
*/
function GrDiffGroup(type, opt_lines) {
+ /** @type {!GrDiffGroup.Type} */
this.type = type;
- /** @type{!Array<!GrDiffLine>} */
+ /** @type {boolean} */
+ this.dueToRebase = false;
+
+ /**
+ * True means all changes in this line are whitespace changes that should
+ * not be highlighted as changed as per the user settings.
+ *
+ * @type{boolean}
+ */
+ this.ignoredWhitespaceOnly = false;
+
+ /**
+ * True means it should not be collapsed (because it was in the URL, or
+ * there is a comment on that line)
+ */
+ this.keyLocation = false;
+
+ /** @type {?HTMLElement} */
+ this.element = null;
+
+ /** @type {!Array<!GrDiffLine>} */
this.lines = [];
- /** @type{!Array<!GrDiffLine>} */
+ /** @type {!Array<!GrDiffLine>} */
this.adds = [];
- /** @type{!Array<!GrDiffLine>} */
+ /** @type {!Array<!GrDiffLine>} */
this.removes = [];
- /** @type{boolean|undefined} */
- this.dueToRebase = undefined;
-
+ /** Both start and end line are inclusive. */
this.lineRange = {
left: {start: null, end: null},
right: {start: null, end: null},
@@ -46,8 +68,7 @@
}
}
- GrDiffGroup.prototype.element = null;
-
+ /** @enum {string} */
GrDiffGroup.Type = {
/** Unchanged context. */
BOTH: 'both',
@@ -59,6 +80,139 @@
DELTA: 'delta',
};
+
+ /**
+ * Hides lines in the given range behind a context control group.
+ *
+ * Groups that would be partially visible are split into their visible and
+ * hidden parts, respectively.
+ * The groups need to be "common groups", meaning they have to have either
+ * originated from an `ab` chunk, or from an `a`+`b` chunk with
+ * `common: true`.
+ *
+ * If the hidden range is 1 line or less, nothing is hidden and no context
+ * control group is created.
+ *
+ * @param {!Array<!GrDiffGroup>} groups Common groups, ordered by their line
+ * ranges.
+ * @param {number} hiddenStart The first element to be hidden, as a
+ * non-negative line number offset relative to the first group's start
+ * line, left and right respectively.
+ * @param {number} hiddenEnd The first visible element after the hidden range,
+ * as a non-negative line number offset relative to the first group's
+ * start line, left and right respectively.
+ * @return {!Array<!GrDiffGroup>}
+ */
+ GrDiffGroup.hideInContextControl = function(groups, hiddenStart, hiddenEnd) {
+ if (groups.length === 0) return [];
+ // Clamp hiddenStart and hiddenEnd - inspired by e.g. substring
+ hiddenStart = Math.max(hiddenStart, 0);
+ hiddenEnd = Math.max(hiddenEnd, hiddenStart);
+
+ let before = [];
+ let hidden = groups;
+ let after = [];
+
+ const numHidden = hiddenEnd - hiddenStart;
+
+ // Only collapse if there is more than 1 line to be hidden.
+ if (numHidden > 1) {
+ if (hiddenStart) {
+ [before, hidden] = GrDiffGroup._splitCommonGroups(hidden, hiddenStart);
+ }
+ if (hiddenEnd) {
+ [hidden, after] = GrDiffGroup._splitCommonGroups(
+ hidden, hiddenEnd - hiddenStart);
+ }
+ } else {
+ [hidden, after] = [[], hidden];
+ }
+
+ const result = [...before];
+ if (hidden.length) {
+ const ctxLine = new GrDiffLine(GrDiffLine.Type.CONTEXT_CONTROL);
+ ctxLine.contextGroups = hidden;
+ const ctxGroup = new GrDiffGroup(
+ GrDiffGroup.Type.CONTEXT_CONTROL, [ctxLine]);
+ result.push(ctxGroup);
+ }
+ result.push(...after);
+ return result;
+ };
+
+ /**
+ * Splits a list of common groups into two lists of groups.
+ *
+ * Groups where all lines are before or all lines are after the split will be
+ * retained as is and put into the first or second list respectively. Groups
+ * with some lines before and some lines after the split will be split into
+ * two groups, which will be put into the first and second list.
+ *
+ * @param {!Array<!GrDiffGroup>} groups
+ * @param {number} split A line number offset relative to the first group's
+ * start line at which the groups should be split.
+ * @return {!Array<!Array<!GrDiffGroup>>} The outer array has 2 elements, the
+ * list of groups before and the list of groups after the split.
+ */
+ GrDiffGroup._splitCommonGroups = function(groups, split) {
+ if (groups.length === 0) return [[], []];
+ const leftSplit = groups[0].lineRange.left.start + split;
+ const rightSplit = groups[0].lineRange.right.start + split;
+
+ const beforeGroups = [];
+ const afterGroups = [];
+ for (const group of groups) {
+ if (group.lineRange.left.end < leftSplit ||
+ group.lineRange.right.end < rightSplit) {
+ beforeGroups.push(group);
+ continue;
+ }
+ if (leftSplit <= group.lineRange.left.start ||
+ rightSplit <= group.lineRange.right.start) {
+ afterGroups.push(group);
+ continue;
+ }
+
+ const before = [];
+ const after = [];
+ for (const line of group.lines) {
+ if ((line.beforeNumber && line.beforeNumber < leftSplit) ||
+ (line.afterNumber && line.afterNumber < rightSplit)) {
+ before.push(line);
+ } else {
+ after.push(line);
+ }
+ }
+
+ if (before.length) {
+ beforeGroups.push(before.length === group.lines.length ?
+ group : group.cloneWithLines(before));
+ }
+ if (after.length) {
+ afterGroups.push(after.length === group.lines.length ?
+ group : group.cloneWithLines(after));
+ }
+ }
+ return [beforeGroups, afterGroups];
+ };
+
+ /**
+ * Creates a new group with the same properties but different lines.
+ *
+ * The element property is not copied, because the original element is still a
+ * rendering of the old lines, so that would not make sense.
+ *
+ * @param {!Array<!GrDiffLine>} lines
+ * @return {!GrDiffGroup}
+ */
+ GrDiffGroup.prototype.cloneWithLines = function(lines) {
+ const group = new GrDiffGroup(this.type, lines);
+ group.dueToRebase = this.dueToRebase;
+ group.ignoredWhitespaceOnly = this.ignoredWhitespaceOnly;
+ return group;
+ };
+
+ /** @param {!GrDiffLine} line */
GrDiffGroup.prototype.addLine = function(line) {
this.lines.push(line);
@@ -77,6 +231,7 @@
this._updateRange(line);
};
+ /** @return {!Array<{left: GrDiffLine, right: GrDiffLine}>} */
GrDiffGroup.prototype.getSideBySidePairs = function() {
if (this.type === GrDiffGroup.Type.BOTH ||
this.type === GrDiffGroup.Type.CONTEXT_CONTROL) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
index 9dc5311700..16e8036ea5 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
@@ -18,8 +18,10 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-group</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="gr-diff-line.js"></script>
<script src="gr-diff-group.js"></script>
@@ -28,12 +30,9 @@ limitations under the License.
suite('gr-diff-group tests', () => {
test('delta line pairs', () => {
let group = new GrDiffGroup(GrDiffGroup.Type.DELTA);
- const l1 = new GrDiffLine(GrDiffLine.Type.ADD);
- const l2 = new GrDiffLine(GrDiffLine.Type.ADD);
- const l3 = new GrDiffLine(GrDiffLine.Type.REMOVE);
- l1.afterNumber = 128;
- l2.afterNumber = 129;
- l3.beforeNumber = 64;
+ const l1 = new GrDiffLine(GrDiffLine.Type.ADD, 0, 128);
+ const l2 = new GrDiffLine(GrDiffLine.Type.ADD, 0, 129);
+ const l3 = new GrDiffLine(GrDiffLine.Type.REMOVE, 64, 0);
group.addLine(l1);
group.addLine(l2);
group.addLine(l3);
@@ -64,17 +63,9 @@ limitations under the License.
});
test('group/header line pairs', () => {
- const l1 = new GrDiffLine(GrDiffLine.Type.BOTH);
- l1.beforeNumber = 64;
- l1.afterNumber = 128;
-
- const l2 = new GrDiffLine(GrDiffLine.Type.BOTH);
- l2.beforeNumber = 65;
- l2.afterNumber = 129;
-
- const l3 = new GrDiffLine(GrDiffLine.Type.BOTH);
- l3.beforeNumber = 66;
- l3.afterNumber = 130;
+ const l1 = new GrDiffLine(GrDiffLine.Type.BOTH, 64, 128);
+ const l2 = new GrDiffLine(GrDiffLine.Type.BOTH, 65, 129);
+ const l3 = new GrDiffLine(GrDiffLine.Type.BOTH, 66, 130);
let group = new GrDiffGroup(GrDiffGroup.Type.BOTH, [l1, l2, l3]);
@@ -122,6 +113,97 @@ limitations under the License.
assert.throws(group.addLine.bind(group, l2));
assert.doesNotThrow(group.addLine.bind(group, l3));
});
+
+ suite('hideInContextControl', () => {
+ let groups;
+ setup(() => {
+ groups = [
+ new GrDiffGroup(GrDiffGroup.Type.BOTH, [
+ new GrDiffLine(GrDiffLine.Type.BOTH, 5, 7),
+ new GrDiffLine(GrDiffLine.Type.BOTH, 6, 8),
+ new GrDiffLine(GrDiffLine.Type.BOTH, 7, 9),
+ ]),
+ new GrDiffGroup(GrDiffGroup.Type.DELTA, [
+ new GrDiffLine(GrDiffLine.Type.REMOVE, 8),
+ new GrDiffLine(GrDiffLine.Type.ADD, 0, 10),
+ new GrDiffLine(GrDiffLine.Type.REMOVE, 9),
+ new GrDiffLine(GrDiffLine.Type.ADD, 0, 11),
+ new GrDiffLine(GrDiffLine.Type.REMOVE, 10),
+ new GrDiffLine(GrDiffLine.Type.ADD, 0, 12),
+ ]),
+ new GrDiffGroup(GrDiffGroup.Type.BOTH, [
+ new GrDiffLine(GrDiffLine.Type.BOTH, 11, 13),
+ new GrDiffLine(GrDiffLine.Type.BOTH, 12, 14),
+ new GrDiffLine(GrDiffLine.Type.BOTH, 13, 15),
+ ]),
+ ];
+ });
+
+ test('hides hidden groups in context control', () => {
+ const collapsedGroups = GrDiffGroup.hideInContextControl(groups, 3, 6);
+ assert.equal(collapsedGroups.length, 3);
+
+ assert.equal(collapsedGroups[0], groups[0]);
+
+ assert.equal(collapsedGroups[1].type, GrDiffGroup.Type.CONTEXT_CONTROL);
+ assert.equal(collapsedGroups[1].lines.length, 1);
+ assert.equal(
+ collapsedGroups[1].lines[0].type, GrDiffLine.Type.CONTEXT_CONTROL);
+ assert.equal(
+ collapsedGroups[1].lines[0].contextGroups.length, 1);
+ assert.equal(
+ collapsedGroups[1].lines[0].contextGroups[0], groups[1]);
+
+ assert.equal(collapsedGroups[2], groups[2]);
+ });
+
+ test('splits partially hidden groups', () => {
+ const collapsedGroups = GrDiffGroup.hideInContextControl(groups, 4, 7);
+ assert.equal(collapsedGroups.length, 4);
+ assert.equal(collapsedGroups[0], groups[0]);
+
+ assert.equal(collapsedGroups[1].type, GrDiffGroup.Type.DELTA);
+ assert.deepEqual(collapsedGroups[1].adds, [groups[1].adds[0]]);
+ assert.deepEqual(collapsedGroups[1].removes, [groups[1].removes[0]]);
+
+ assert.equal(collapsedGroups[2].type, GrDiffGroup.Type.CONTEXT_CONTROL);
+ assert.equal(collapsedGroups[2].lines.length, 1);
+ assert.equal(
+ collapsedGroups[2].lines[0].type, GrDiffLine.Type.CONTEXT_CONTROL);
+ assert.equal(
+ collapsedGroups[2].lines[0].contextGroups.length, 2);
+
+ assert.equal(
+ collapsedGroups[2].lines[0].contextGroups[0].type,
+ GrDiffGroup.Type.DELTA);
+ assert.deepEqual(
+ collapsedGroups[2].lines[0].contextGroups[0].adds,
+ groups[1].adds.slice(1));
+ assert.deepEqual(
+ collapsedGroups[2].lines[0].contextGroups[0].removes,
+ groups[1].removes.slice(1));
+
+ assert.equal(
+ collapsedGroups[2].lines[0].contextGroups[1].type,
+ GrDiffGroup.Type.BOTH);
+ assert.deepEqual(
+ collapsedGroups[2].lines[0].contextGroups[1].lines,
+ [groups[2].lines[0]]);
+
+ assert.equal(collapsedGroups[3].type, GrDiffGroup.Type.BOTH);
+ assert.deepEqual(collapsedGroups[3].lines, groups[2].lines.slice(1));
+ });
+
+ test('groups unchanged if the hidden range is empty', () => {
+ assert.deepEqual(
+ GrDiffGroup.hideInContextControl(groups, 0, 0), groups);
+ });
+
+ test('groups unchanged if there is only 1 line to hide', () => {
+ assert.deepEqual(
+ GrDiffGroup.hideInContextControl(groups, 3, 4), groups);
+ });
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
index 44bb52a929..a29529375d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
@@ -20,20 +20,31 @@
// Prevent redefinition.
if (window.GrDiffLine) { return; }
- function GrDiffLine(type) {
+ /**
+ * @param {GrDiffLine.Type} type
+ * @param {number|string=} opt_beforeLine
+ * @param {number|string=} opt_afterLine
+ */
+ function GrDiffLine(type, opt_beforeLine, opt_afterLine) {
this.type = type;
- this.highlights = [];
- }
- /** @type {number|string} */
- GrDiffLine.prototype.afterNumber = 0;
+ /** @type {number|string} */
+ this.beforeNumber = opt_beforeLine || 0;
- /** @type {number|string} */
- GrDiffLine.prototype.beforeNumber = 0;
+ /** @type {number|string} */
+ this.afterNumber = opt_afterLine || 0;
- GrDiffLine.prototype.contextGroup = null;
+ /** @type {boolean} */
+ this.hasIntralineInfo = false;
+
+ /** @type {Array<GrDiffLine.Highlights>} */
+ this.highlights = [];
- GrDiffLine.prototype.text = '';
+ /** @type {?Array<Object>} ?Array<!GrDiffGroup> */
+ this.contextGroups = null;
+
+ this.text = '';
+ }
GrDiffLine.Type = {
ADD: 'add',
@@ -43,6 +54,23 @@
REMOVE: 'remove',
};
+ /**
+ * A line highlight object consists of three fields:
+ * - contentIndex: The index of the chunk `content` field (the line
+ * being referred to).
+ * - startIndex: Index of the character where the highlight should begin.
+ * - endIndex: (optional) Index of the character where the highlight should
+ * end. If omitted, the highlight is meant to be a continuation onto the
+ * next line.
+ *
+ * @typedef {{
+ * contentIndex: number,
+ * startIndex: number,
+ * endIndex: number
+ * }}
+ */
+ GrDiffLine.Highlights;
+
GrDiffLine.FILE = 'FILE';
GrDiffLine.BLANK_LINE = new GrDiffLine(GrDiffLine.Type.BLANK);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 72fc1ee469..1c36745b3f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -23,19 +24,31 @@ limitations under the License.
<link rel="import" href="../gr-diff-highlight/gr-diff-highlight.html">
<link rel="import" href="../gr-diff-selection/gr-diff-selection.html">
<link rel="import" href="../gr-syntax-themes/gr-syntax-theme.html">
+<link rel="import" href="../gr-ranged-comment-themes/gr-ranged-comment-theme.html">
<script src="../../../scripts/hiddenscroll.js"></script>
<dom-module id="gr-diff">
<template>
<style include="shared-styles">
- :host(.no-left) .sideBySide ::content .left,
- :host(.no-left) .sideBySide ::content .left + td,
- :host(.no-left) .sideBySide ::content .right:not([data-value]),
- :host(.no-left) .sideBySide ::content .right:not([data-value]) + td {
+ :host(.no-left) .sideBySide .left,
+ :host(.no-left) .sideBySide .left + td,
+ :host(.no-left) .sideBySide .right:not([data-value]),
+ :host(.no-left) .sideBySide .right:not([data-value]) + td {
display: none;
}
- .thread-group, ::slotted(*) .thread-group {
+ ::slotted(*) .thread-group {
+ display: block;
+ max-width: var(--content-width, 80ch);
+ white-space: normal;
+ }
+ :host {
+ font-family: var(--monospace-font-family, ''), 'Roboto Mono';
+ font-size: var(--font-size, var(--font-size-code, 12px));
+ line-height: var(--line-height-code, 1.334);
+ }
+
+ .thread-group {
display: block;
max-width: var(--content-width, 80ch);
white-space: normal;
@@ -46,7 +59,7 @@ limitations under the License.
@apply --diff-container-styles;
}
.diffContainer.hiddenscroll {
- margin-bottom: .8em;
+ margin-bottom: var(--spacing-m);
}
table {
border-collapse: collapse;
@@ -80,10 +93,12 @@ limitations under the License.
background-color: var(--diff-selection-background-color);
color: var(--primary-text-color);
}
- .blank,
.content {
background-color: var(--view-background-color);
}
+ .blank {
+ background-color: var(--diff-blank-background-color);
+ }
.image-diff .content {
background-color: var(--table-header-background-color);
}
@@ -96,8 +111,6 @@ limitations under the License.
}
.lineNum,
.content {
- /* Set font size based the user's diff preference. */
- font-size: var(--font-size, var(--font-size-normal));
vertical-align: top;
white-space: pre;
}
@@ -109,7 +122,7 @@ limitations under the License.
user-select: none;
color: var(--deemphasized-text-color);
- padding: 0 .5em;
+ padding: 0 var(--spacing-m);
text-align: right;
}
.canComment .lineNum {
@@ -123,6 +136,8 @@ limitations under the License.
width: var(--content-width, 80ch);
}
.content.add .intraline,
+ /* If there are no intraline info, consider everything changed */
+ .content.add.no-intraline-info,
.delta.total .content.add {
background-color: var(--dark-add-highlight-color);
}
@@ -130,12 +145,16 @@ limitations under the License.
background-color: var(--light-add-highlight-color);
}
.content.remove .intraline,
+ /* If there are no intraline info, consider everything changed */
+ .content.remove.no-intraline-info,
.delta.total .content.remove {
background-color: var(--dark-remove-highlight-color);
}
.content.remove {
background-color: var(--light-remove-highlight-color);
}
+
+ /* dueToRebase */
.dueToRebase .content.add .intraline,
.delta.total.dueToRebase .content.add {
background-color: var(--dark-rebased-add-highlight-color);
@@ -150,20 +169,32 @@ limitations under the License.
.dueToRebase .content.remove {
background-color: var(--light-remove-add-highlight-color);
}
+
+ /* ignoredWhitespaceOnly */
+ .ignoredWhitespaceOnly .content.add .intraline,
+ .delta.total.ignoredWhitespaceOnly .content.add,
+ .ignoredWhitespaceOnly .content.add,
+ .ignoredWhitespaceOnly .content.remove .intraline,
+ .delta.total.ignoredWhitespaceOnly .content.remove,
+ .ignoredWhitespaceOnly .content.remove {
+ background: none;
+ }
+
.content .contentText:empty:after {
/* Newline, to ensure empty lines are one line-height tall. */
content: '\A';
}
.contextControl {
- background-color: var(--diff-context-control-color);
+ background-color: var(--diff-context-control-background-color);
border: 1px solid var(--diff-context-control-border-color);
+ color: var(--diff-context-control-color);
}
.contextControl gr-button {
display: inline-block;
text-decoration: none;
--gr-button: {
- color: var(--deemphasized-text-color);
- padding: .2em;
+ color: var(--diff-context-control-color);
+ padding: var(--spacing-xs);
}
}
.contextControl td:not(.lineNum) {
@@ -191,21 +222,19 @@ limitations under the License.
.content .trailing-whitespace,
.trailing-whitespace .intraline,
.content .trailing-whitespace .intraline {
- border-radius: .4em;
+ border-radius: var(--border-radius, 4px);
background-color: var(--diff-trailing-whitespace-indicator);
}
#diffHeader {
background-color: var(--table-header-background-color);
border-bottom: 1px solid var(--border-color);
color: var(--link-color);
- font-family: var(--monospace-font-family);
- font-size: var(--font-size, var(--font-size-normal));
- padding: 0.5em 0 0.5em 4em;
+ padding: var(--spacing-m) 0 var(--spacing-m) 48px;
}
#loadingError,
#sizeWarning {
display: none;
- margin: 1em auto;
+ margin: var(--spacing-l) auto;
max-width: 60em;
text-align: center;
}
@@ -213,7 +242,7 @@ limitations under the License.
color: var(--error-text-color);
}
#sizeWarning gr-button {
- margin: 1em;
+ margin: var(--spacing-l);
}
#loadingError.showError,
#sizeWarning.warn {
@@ -227,9 +256,7 @@ limitations under the License.
}
td.blame {
display: none;
- font-family: var(--font-family);
- font-size: var(--font-size, var(--font-size-normal));
- padding: 0 .5em;
+ padding: 0 var(--spacing-m);
white-space: pre;
}
:host(.showBlame) col.blame {
@@ -251,11 +278,6 @@ limitations under the License.
overflow: hidden;
width: 200px;
}
- /** Since the line limit position is determined by charachter size, blank
- lines also need to have the same font size as everything else */
- .full-width .blank {
- font-size: var(--font-size, var(--font-size-normal));
- }
/** Support the line length indicator **/
.full-width td.content,
.full-width td.blank {
@@ -280,8 +302,50 @@ limitations under the License.
.lineNum.PARTIALLY_COVERED {
background: linear-gradient(to right bottom, #FFD1A4 0%, #FFD1A4 50%, #E0F2F1 50%, #E0F2F1 100%);
}
+
+ /** BEGIN: Select and copy for Polymer 2 */
+ /** Below was copied and modified from the original css in gr-diff-selection.html */
+ .content,
+ .contextControl,
+ .blame {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .selected-left:not(.selected-comment) .side-by-side .left + .content .contentText,
+ .selected-right:not(.selected-comment) .side-by-side .right + .content .contentText,
+ .selected-left:not(.selected-comment) .unified .left.lineNum ~ .content:not(.both) .contentText,
+ .selected-right:not(.selected-comment) .unified .right.lineNum ~ .content .contentText,
+ .selected-left.selected-comment .side-by-side .left + .content .message,
+ .selected-right.selected-comment .side-by-side .right + .content .message :not(.collapsedContent),
+ .selected-comment .unified .message :not(.collapsedContent),
+ .selected-blame .blame {
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ -ms-user-select: text;
+ user-select: text;
+ }
+
+ /** Make comments selectable when selected */
+ .selected-left.selected-comment ::slotted(gr-comment-thread[comment-side=left]),
+ .selected-right.selected-comment ::slotted(gr-comment-thread[comment-side=right]) {
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ -ms-user-select: text;
+ user-select: text;
+ }
+ /** END: Select and copy for Polymer 2 */
+
+ .whitespace-change-only-message {
+ background-color: var(--diff-context-control-background-color);
+ border: 1px solid var(--diff-context-control-border-color);
+ text-align: center;
+ }
</style>
<style include="gr-syntax-theme"></style>
+ <style include="gr-ranged-comment-theme"></style>
<div id="diffHeader" hidden$="[[_computeDiffHeaderHidden(_diffHeaderItems)]]">
<template
is="dom-repeat"
@@ -302,18 +366,26 @@ limitations under the License.
coverage-ranges="[[coverageRanges]]"
project-name="[[projectName]]"
diff="[[diff]]"
- diff-path="[[path]]"
+ path="[[path]]"
change-num="[[changeNum]]"
patch-num="[[patchRange.patchNum]]"
view-mode="[[viewMode]]"
line-wrapping="[[lineWrapping]]"
is-image-diff="[[isImageDiff]]"
base-image="[[baseImage]]"
+ layers="[[layers]]"
revision-image="[[revisionImage]]">
<table
id="diffTable"
class$="[[_diffTableClass]]"
role="presentation"></table>
+
+ <template is="dom-if" if="[[showNoChangeMessage(loading, prefs, _diffLength)]]">
+ <div class="whitespace-change-only-message">
+ This file only contains whitespace changes.
+ Modify the whitespace setting to see the changes.
+ </div>
+ </template>
</gr-diff-builder>
</gr-diff-highlight>
</gr-diff-selection>
@@ -329,10 +401,10 @@ limitations under the License.
Prevented render because "Whole file" is enabled and this diff is very
large (about [[_diffLength]] lines).
</p>
- <gr-button on-tap="_handleLimitedBypass">
+ <gr-button on-click="_handleLimitedBypass">
Render with limited context
</gr-button>
- <gr-button on-tap="_handleFullBypass">
+ <gr-button on-click="_handleFullBypass">
Render anyway (may be slow)
</gr-button>
</div>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 4e023d4e85..4f07664154 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -35,26 +35,10 @@
RIGHT: 'right',
};
- const Defs = {};
-
- /**
- * Special line number which should not be collapsed into a shared region.
- *
- * @typedef {{
- * number: number,
- * leftSide: boolean
- * }}
- */
- Defs.LineOfInterest;
-
const LARGE_DIFF_THRESHOLD_LINES = 10000;
const FULL_CONTEXT = -1;
const LIMITED_CONTEXT = 10;
- /** @typedef {{start_line: number, start_character: number,
- * end_line: number, end_character: number}} */
- Gerrit.Range;
-
/**
* Compare two ranges. Either argument may be falsy, but will only return
* true if both are falsy or if neither are falsy and have the same position
@@ -86,6 +70,9 @@
* implements the same behavior as the template parsing for imperative slots.
*/
Gerrit.slotToContent = function(slot) {
+ if (Polymer.Element) {
+ return slot;
+ }
const content = document.createElement('content');
content.name = slot.name;
content.setAttribute('select', `[slot='${slot.name}']`);
@@ -106,7 +93,6 @@
Polymer({
is: 'gr-diff',
- _legacyUndefinedCheck: true,
/**
* Fired when the user selects a line.
@@ -184,7 +170,7 @@
observer: '_viewModeObserver',
},
- /** @type {?Defs.LineOfInterest} */
+ /** @type {?Gerrit.LineOfInterest} */
lineOfInterest: Object,
loading: {
@@ -271,9 +257,11 @@
/** Set by Polymer. */
isAttached: Boolean,
+ layers: Array,
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.PatchSetBehavior,
],
@@ -295,7 +283,18 @@
this._unobserveNodes();
},
+ showNoChangeMessage(loading, prefs, diffLength) {
+ return !loading &&
+ prefs && prefs.ignore_whitespace !== 'IGNORE_NONE'
+ && diffLength === 0;
+ },
+
_enableSelectionObserver(loggedIn, isAttached) {
+ // Polymer 2: check for undefined
+ if ([loggedIn, isAttached].some(arg => arg === undefined)) {
+ return;
+ }
+
if (loggedIn && isAttached) {
this.listen(document, 'selectionchange', '_handleSelectionChange');
this.listen(document, 'mouseup', '_handleMouseUp');
@@ -361,7 +360,10 @@
});
this.splice('_commentRanges', i, 1);
}
- this.push('_commentRanges', ...addedCommentRanges);
+
+ if (addedCommentRanges && addedCommentRanges.length) {
+ this.push('_commentRanges', ...addedCommentRanges);
+ }
},
/**
@@ -384,7 +386,13 @@
const commentSide = threadEl.getAttribute('comment-side');
const lineNum = Number(threadEl.getAttribute('line-num')) ||
GrDiffLine.FILE;
+ const commentRange = threadEl.range || {};
keyLocations[commentSide][lineNum] = true;
+ // Add start_line as well if exists,
+ // the being and end of the range should not be collapsed.
+ if (commentRange.start_line) {
+ keyLocations[commentSide][commentRange.start_line] = true;
+ }
}
return keyLocations;
},
@@ -393,12 +401,12 @@
_redispatchHoverEvents(addedThreadEls) {
for (const threadEl of addedThreadEls) {
threadEl.addEventListener('mouseenter', () => {
- threadEl.dispatchEvent(
- new CustomEvent('comment-thread-mouseenter', {bubbles: true}));
+ threadEl.dispatchEvent(new CustomEvent(
+ 'comment-thread-mouseenter', {bubbles: true, composed: true}));
});
threadEl.addEventListener('mouseleave', () => {
- threadEl.dispatchEvent(
- new CustomEvent('comment-thread-mouseleave', {bubbles: true}));
+ threadEl.dispatchEvent(new CustomEvent(
+ 'comment-thread-mouseleave', {bubbles: true, composed: true}));
});
}
},
@@ -415,7 +423,6 @@
return [];
}
- // Polymer2: querySelectorAll returns NodeList instead of Array.
return Array.from(
Polymer.dom(this.root).querySelectorAll('.diff-row'));
},
@@ -554,6 +561,7 @@
this._getIsParentCommentByLineAndContent(lineEl, contentEl);
this.dispatchEvent(new CustomEvent('create-comment', {
bubbles: true,
+ composed: true,
detail: {
lineNum,
side,
@@ -713,7 +721,7 @@
_diffChanged(newValue) {
if (newValue) {
- this._diffLength = this.$.diffBuilder.getDiffLength();
+ this._diffLength = this.getDiffLength(newValue);
this._debounceRenderDiffTable();
}
},
@@ -736,14 +744,16 @@
_renderDiffTable() {
this._unobserveIncrementalNodes();
if (!this.prefs) {
- this.dispatchEvent(new CustomEvent('render', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('render', {bubbles: true, composed: true}));
return;
}
if (this.prefs.context === -1 &&
this._diffLength >= LARGE_DIFF_THRESHOLD_LINES &&
this._safetyBypass === null) {
this._showWarning = true;
- this.dispatchEvent(new CustomEvent('render', {bubbles: true}));
+ this.dispatchEvent(
+ new CustomEvent('render', {bubbles: true, composed: true}));
return;
}
@@ -753,7 +763,11 @@
this.$.diffBuilder.render(keyLocations, this._getBypassPrefs())
.then(() => {
this.dispatchEvent(
- new CustomEvent('render', {bubbles: true}));
+ new CustomEvent('render', {
+ bubbles: true,
+ composed: true,
+ detail: {contentRendered: true},
+ }));
});
},
@@ -765,6 +779,7 @@
// not hurt. It's probably a bigger performance cost to remove them than
// to keep them around. Medium term we can even consider to add one slot
// for each line from the start.
+ let lastEl;
for (const threadEl of addedThreadEls) {
const lineNumString = threadEl.getAttribute('line-num') || 'FILE';
const commentSide = threadEl.getAttribute('comment-side');
@@ -783,6 +798,14 @@
const slot = document.createElement('slot');
slot.name = threadEl.getAttribute('slot');
Polymer.dom(threadGroupEl).appendChild(Gerrit.slotToContent(slot));
+ lastEl = threadEl;
+ }
+
+ // Safari is not binding newly created comment-thread
+ // with the slot somehow, replace itself will rebind it
+ // @see Issue 11182
+ if (lastEl && lastEl.replaceWith) {
+ lastEl.replaceWith(lastEl);
}
});
},
@@ -886,7 +909,8 @@
!chunk.ab &&
// The chunk doesn't have the given side.
- ((leftSide && !chunk.a) || (!leftSide && !chunk.b)));
+ ((leftSide && (!chunk.a || !chunk.a.length)) ||
+ (!leftSide && (!chunk.b || !chunk.b.length))));
// If we reached the beginning of the diff and failed to find a chunk
// with the given side, return null.
@@ -944,5 +968,25 @@
if (loading || !warning) { return 'newlineWarning hidden'; }
return 'newlineWarning';
},
+
+ /**
+ * Get the approximate length of the diff as the sum of the maximum
+ * length of the chunks.
+ *
+ * @param {Object} diff object
+ * @return {number}
+ */
+ getDiffLength(diff) {
+ if (!diff) return 0;
+ return diff.content.reduce((sum, sec) => {
+ if (sec.hasOwnProperty('ab')) {
+ return sum + sec.ab.length;
+ } else {
+ return sum + Math.max(
+ sec.hasOwnProperty('a') ? sec.a.length : 0,
+ sec.hasOwnProperty('b') ? sec.b.length : 0);
+ }
+ }, 0);
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
index 594d60eda3..b177bade0d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
@@ -18,12 +18,15 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
<link rel="import" href="gr-diff.html">
@@ -85,14 +88,14 @@ limitations under the License.
element = fixture('basic');
element.prefs = Object.assign({}, MINIMAL_PREFS, {line_wrapping: true});
flushAsynchronousOperations();
- assert.equal(element.customStyle['--line-limit'], '80ch');
+ assert.equal(util.getComputedStyleValue('--line-limit', element), '80ch');
});
test('line limit without line_wrapping', () => {
element = fixture('basic');
element.prefs = Object.assign({}, MINIMAL_PREFS, {line_wrapping: false});
flushAsynchronousOperations();
- assert.isNotOk(element.customStyle['--line-limit']);
+ assert.isNotOk(util.getComputedStyleValue('--line-limit', element));
});
suite('_get{PatchNum|IsParentComment}ByLineAndContent', () => {
@@ -776,12 +779,12 @@ limitations under the License.
element = fixture('basic');
renderStub = sandbox.stub(element.$.diffBuilder, 'render',
() => {
- Promise.resolve();
element.$.diffBuilder.dispatchEvent(
- new CustomEvent('render', {bubbles: true}));
+ new CustomEvent('render', {bubbles: true, composed: true}));
+ return Promise.resolve({});
});
const mock = document.createElement('mock-diff-response');
- sandbox.stub(element.$.diffBuilder, 'getDiffLength').returns(10000);
+ sandbox.stub(element, 'getDiffLength').returns(10000);
element.diff = mock.diffResponse;
element.noRenderOnPrefsChange = true;
});
@@ -865,7 +868,7 @@ limitations under the License.
assert.equal(element._lastChunkForSide(diff, true), diff.content[3]);
});
- test('addition', () => {
+ test('addition with a undefined', () => {
const diff = {content: [
{b: ['foo', 'bar', 'baz']},
]};
@@ -873,7 +876,16 @@ limitations under the License.
assert.isNull(element._lastChunkForSide(diff, true));
});
- test('deletion', () => {
+ test('addition with a empty', () => {
+ const diff = {content: [
+ {a: [], b: ['foo', 'bar', 'baz']},
+ ]};
+ assert.equal(element._lastChunkForSide(diff, false), diff.content[0]);
+ assert.isNull(element._lastChunkForSide(diff, true));
+ });
+
+
+ test('deletion with b undefined', () => {
const diff = {content: [
{a: ['foo', 'bar', 'baz']},
]};
@@ -881,6 +893,14 @@ limitations under the License.
assert.equal(element._lastChunkForSide(diff, true), diff.content[0]);
});
+ test('deletion with b empty', () => {
+ const diff = {content: [
+ {a: ['foo', 'bar', 'baz'], b: []},
+ ]};
+ assert.isNull(element._lastChunkForSide(diff, false));
+ assert.equal(element._lastChunkForSide(diff, true), diff.content[0]);
+ });
+
test('empty', () => {
const diff = {content: []};
assert.isNull(element._lastChunkForSide(diff, false));
@@ -1039,6 +1059,117 @@ limitations under the License.
});
});
});
+
+ suite('whitespace changes only message', () => {
+ const setupDiff = function(ignore_whitespace, diffContent) {
+ element = fixture('basic');
+ element.prefs = {
+ ignore_whitespace,
+ auto_hide_diff_table_header: true,
+ context: 10,
+ cursor_blink_rate: 0,
+ font_size: 12,
+ intraline_difference: true,
+ line_length: 100,
+ line_wrapping: false,
+ show_line_endings: true,
+ show_tabs: true,
+ show_whitespace_errors: true,
+ syntax_highlighting: true,
+ tab_size: 8,
+ theme: 'DEFAULT',
+ };
+
+ element.diff = {
+ intraline_status: 'OK',
+ change_type: 'MODIFIED',
+ diff_header: [
+ 'diff --git a/carrot.js b/carrot.js',
+ 'index 2adc47d..f9c2f2c 100644',
+ '--- a/carrot.js',
+ '+++ b/carrot.jjs',
+ 'file differ',
+ ],
+ content: diffContent,
+ binary: true,
+ };
+
+ element._renderDiffTable();
+ flushAsynchronousOperations();
+ };
+
+ test('show the message if ignore_whitespace is criteria matches', () => {
+ setupDiff('IGNORE_ALL', [{skip: 100}]);
+ assert.isTrue(element.showNoChangeMessage(
+ /* loading= */ false,
+ element.prefs,
+ element._diffLength
+ ));
+ });
+
+ test('do not show the message if still loading', () => {
+ setupDiff('IGNORE_ALL', [{skip: 100}]);
+ assert.isFalse(element.showNoChangeMessage(
+ /* loading= */ true,
+ element.prefs,
+ element._diffLength
+ ));
+ });
+
+ test('do not show the message if contains valid changes', () => {
+ const content = [{
+ a: ['all work and no play make andybons a dull boy'],
+ b: ['elgoog elgoog elgoog'],
+ }, {
+ ab: [
+ 'Non eram nescius, Brute, cum, quae summis ingeniis ',
+ 'exquisitaque doctrina philosophi Graeco sermone tractavissent',
+ ],
+ }];
+ setupDiff('IGNORE_ALL', content);
+ assert.equal(element._diffLength, 3);
+ assert.isFalse(element.showNoChangeMessage(
+ /* loading= */ false,
+ element.prefs,
+ element._diffLength
+ ));
+ });
+
+ test('do not show message if ignore whitespace is disabled', () => {
+ const content = [{
+ a: ['all work and no play make andybons a dull boy'],
+ b: ['elgoog elgoog elgoog'],
+ }, {
+ ab: [
+ 'Non eram nescius, Brute, cum, quae summis ingeniis ',
+ 'exquisitaque doctrina philosophi Graeco sermone tractavissent',
+ ],
+ }];
+ setupDiff('IGNORE_NONE', content);
+ assert.isFalse(element.showNoChangeMessage(
+ /* loading= */ false,
+ element.prefs,
+ element._diffLength
+ ));
+ });
+ });
+
+ test('getDiffLength', () => {
+ const diff = document.createElement('mock-diff-response').diffResponse;
+ assert.equal(element.getDiffLength(diff), 52);
+ });
+
+ test('`render` event has contentRendered field in detail', done => {
+ element = fixture('basic');
+ element.prefs = {};
+ renderStub = sandbox.stub(element.$.diffBuilder, 'render')
+ .returns(Promise.resolve());
+ element.addEventListener('render', event => {
+ assert.isTrue(event.detail.contentRendered);
+ done();
+ });
+ element._renderDiffTable();
+ });
});
a11ySuite('basic');
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html
index 3de4284e80..ee1f5365d9 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -34,7 +34,7 @@ limitations under the License.
}
.arrow {
color: var(--deemphasized-text-color);
- margin: 0 .5em;
+ margin: 0 var(--spacing-m);
}
gr-dropdown-list {
--trigger-style: {
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
index f913080e30..c7bf4ec34f 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
@@ -31,7 +31,6 @@
Polymer({
is: 'gr-patch-range-select',
- _legacyUndefinedCheck: true,
properties: {
availablePatches: Array,
@@ -68,6 +67,17 @@
_computeBaseDropdownContent(availablePatches, patchNum, _sortedRevisions,
changeComments, revisionInfo) {
+ // Polymer 2: check for undefined
+ if ([
+ availablePatches,
+ patchNum,
+ _sortedRevisions,
+ changeComments,
+ revisionInfo,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const parentCounts = revisionInfo.getParentCountMap();
const currentParentCount = parentCounts.hasOwnProperty(patchNum) ?
parentCounts[patchNum] : 1;
@@ -111,6 +121,16 @@
_computePatchDropdownContent(availablePatches, basePatchNum,
_sortedRevisions, changeComments) {
+ // Polymer 2: check for undefined
+ if ([
+ availablePatches,
+ basePatchNum,
+ _sortedRevisions,
+ changeComments,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
const dropdownContent = [];
for (const patch of availablePatches) {
const patchNum = patch.num;
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
index f2b35a5300..ee893ee387 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-patch-range-select</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<link rel="import" href="../../diff/gr-comment-api/gr-comment-api.html">
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html
index c9e9f50fc8..17a4866ae7 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-ranged-comment-layer">
<template>
</template>
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
index 2183e7df05..5a035792bc 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
@@ -17,17 +17,14 @@
(function() {
'use strict';
- const HOVER_PATH_PATTERN = /^commentRanges\.\#(\d+)\.hovering$/;
+ // Polymer 1 adds # before array's key, while Polymer 2 doesn't
+ const HOVER_PATH_PATTERN = /^(commentRanges\.\#?\d+)\.hovering$/;
- const RANGE_HIGHLIGHT = 'range';
- const HOVER_HIGHLIGHT = 'rangeHighlight';
-
- /** @typedef {{side: string, range: Gerrit.Range, hovering: boolean}} */
- Gerrit.HoveredRange;
+ const RANGE_HIGHLIGHT = 'style-scope gr-diff range';
+ const HOVER_HIGHLIGHT = 'style-scope gr-diff rangeHighlight';
Polymer({
is: 'gr-ranged-comment-layer',
- _legacyUndefinedCheck: true,
/**
* Fired when the range in a range comment was malformed and had to be
@@ -55,6 +52,10 @@
'_handleCommentRangesChange(commentRanges.*)',
],
+ get styleModuleName() {
+ return 'gr-ranged-comment-styles';
+ },
+
/**
* Layer method to add annotations to a line.
*
@@ -130,8 +131,10 @@
// If the change only changed the `hovering` property of a comment.
const match = record.path.match(HOVER_PATH_PATTERN);
if (match) {
- const commentRangesIndex = match[1];
- const {side, range, hovering} = this.commentRanges[commentRangesIndex];
+ // The #number indicates the key of that item in the array
+ // not the index, especially in polymer 1.
+ const {side, range, hovering} = this.get(match[1]);
+
this._updateRangesMap(
side, range, hovering, (forLine, start, end, hovering) => {
const index = forLine.findIndex(lineRange =>
@@ -192,6 +195,7 @@
range.end = line.text.length;
this.dispatchEvent(new CustomEvent('normalize-range', {
bubbles: true,
+ composed: true,
detail: {lineNum, side},
}));
}
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
index 682c02633b..9d207a5a89 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-ranged-comment-layer</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../gr-diff/gr-diff-line.js"></script>
@@ -130,7 +132,7 @@ limitations under the License.
assert.equal(lastCall.args[0], el);
assert.equal(lastCall.args[1], expectedStart);
assert.equal(lastCall.args[2], expectedLength);
- assert.equal(lastCall.args[3], 'range');
+ assert.equal(lastCall.args[3], 'style-scope gr-diff range');
});
test('type=Remove has-comment hovering', () => {
@@ -148,7 +150,7 @@ limitations under the License.
assert.equal(lastCall.args[0], el);
assert.equal(lastCall.args[1], expectedStart);
assert.equal(lastCall.args[2], expectedLength);
- assert.equal(lastCall.args[3], 'rangeHighlight');
+ assert.equal(lastCall.args[3], 'style-scope gr-diff rangeHighlight');
});
test('type=Both has-comment', () => {
@@ -165,7 +167,7 @@ limitations under the License.
assert.equal(lastCall.args[0], el);
assert.equal(lastCall.args[1], expectedStart);
assert.equal(lastCall.args[2], expectedLength);
- assert.equal(lastCall.args[3], 'range');
+ assert.equal(lastCall.args[3], 'style-scope gr-diff range');
});
test('type=Both has-comment off side', () => {
@@ -193,7 +195,7 @@ limitations under the License.
assert.equal(lastCall.args[0], el);
assert.equal(lastCall.args[1], expectedStart);
assert.equal(lastCall.args[2], expectedLength);
- assert.equal(lastCall.args[3], 'range');
+ assert.equal(lastCall.args[3], 'style-scope gr-diff range');
});
});
@@ -207,6 +209,7 @@ limitations under the License.
test('_handleCommentRangesChange hovering', () => {
const notifyStub = sinon.stub();
element.addListener(notifyStub);
+ const updateRangesMapSpy = sandbox.spy(element, '_updateRangesMap');
element.set(['commentRanges', 1, 'hovering'], true);
@@ -215,6 +218,8 @@ limitations under the License.
assert.equal(lastCall.args[0], 10);
assert.equal(lastCall.args[1], 12);
assert.equal(lastCall.args[2], 'right');
+
+ assert.isTrue(updateRangesMapSpy.called);
});
test('_handleCommentRangesChange splice out', () => {
@@ -251,6 +256,31 @@ limitations under the License.
assert.equal(lastCall.args[2], 'left');
});
+ test('_handleCommentRangesChange mixed actions', () => {
+ const notifyStub = sinon.stub();
+ element.addListener(notifyStub);
+ const updateRangesMapSpy = sandbox.spy(element, '_updateRangesMap');
+
+ element.set(['commentRanges', 1, 'hovering'], true);
+ assert.isTrue(updateRangesMapSpy.callCount === 1);
+ element.splice('commentRanges', 1, 1);
+ assert.isTrue(updateRangesMapSpy.callCount === 2);
+ element.splice('commentRanges', 1, 1);
+ assert.isTrue(updateRangesMapSpy.callCount === 3);
+ element.splice('commentRanges', 1, 0, {
+ side: 'left',
+ range: {
+ end_character: 15,
+ end_line: 275,
+ start_character: 5,
+ start_line: 250,
+ },
+ });
+ assert.isTrue(updateRangesMapSpy.callCount === 4);
+ element.set(['commentRanges', 2, 'hovering'], true);
+ assert.isTrue(updateRangesMapSpy.callCount === 5);
+ });
+
test('_computeCommentMap creates maps correctly', () => {
// There is only one ranged comment on the left, but it spans ll.36-39.
const leftKeys = [];
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-themes/gr-ranged-comment-theme.html b/polygerrit-ui/app/elements/diff/gr-ranged-comment-themes/gr-ranged-comment-theme.html
new file mode 100644
index 0000000000..cefd241cc3
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-themes/gr-ranged-comment-theme.html
@@ -0,0 +1,30 @@
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<dom-module id="gr-ranged-comment-theme">
+ <template>
+ <style>
+ .range {
+ background-color: var(--diff-highlight-range-color);
+ display: inline;
+ }
+ .rangeHighlight {
+ background-color: var(--diff-highlight-range-hover-color);
+ display: inline;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
index 633530f47f..aa4d2e1c34 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-tooltip/gr-tooltip.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
index 26bf738ee1..b1b3e0f73a 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-selection-action-box',
- _legacyUndefinedCheck: true,
/**
* Fired when the comment creation action was taken (hotkey, click).
@@ -49,6 +48,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
],
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
index dece36600f..b950e7b236 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-selection-action-box</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-selection-action-box.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.html b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.html
index 67c32bb641..dd6bfeccc6 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.html
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-lib-loader/gr-lib-loader.html">
<dom-module id="gr-syntax-layer">
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
index 944fa494d2..50cf6b469b 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
@@ -134,7 +134,6 @@
Polymer({
is: 'gr-syntax-layer',
- _legacyUndefinedCheck: true,
properties: {
diff: {
@@ -179,6 +178,10 @@
this.push('_listeners', fn);
},
+ removeListener(fn) {
+ this._listeners = this._listeners.filter(f => f != fn);
+ },
+
/**
* Annotation layer method to add syntax annotations to the given element
* for the given line.
@@ -225,7 +228,7 @@
},
/**
- * Start processing symtax for the loaded diff and notify layer listeners
+ * Start processing syntax for the loaded diff and notify layer listeners
* as syntax info comes online.
*
* @return {Promise}
@@ -233,7 +236,7 @@
process() {
// Cancel any still running process() calls, because they append to the
// same _baseRanges and _revisionRanges fields.
- this.cancel();
+ this._cancel();
// Discard existing ranges.
this._baseRanges = [];
@@ -303,7 +306,7 @@
/**
* Cancel any asynchronous syntax processing jobs.
*/
- cancel() {
+ _cancel() {
if (this._processHandle != null) {
this.cancelAsync(this._processHandle);
this._processHandle = null;
@@ -314,7 +317,7 @@
},
_diffChanged() {
- this.cancel();
+ this._cancel();
this._baseRanges = [];
this._revisionRanges = [];
},
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
index b63675aafb..472db21ce3 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-syntax-layer</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
<link rel="import" href="gr-syntax-layer.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-themes/gr-syntax-theme.html b/polygerrit-ui/app/elements/diff/gr-syntax-themes/gr-syntax-theme.html
index 10710aeea4..e5ae06d4f8 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-themes/gr-syntax-theme.html
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-themes/gr-syntax-theme.html
@@ -41,7 +41,6 @@ limitations under the License.
.gr-syntax-keyword,
.gr-syntax-name {
color: var(--syntax-keyword-color);
- line-height: 1;
}
.gr-syntax-number {
color: var(--syntax-number-color);
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.html b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.html
index 720f353447..5072b9d0cc 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.html
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.html
@@ -14,11 +14,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/gr-table-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-list-view/gr-list-view.html">
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
index 995326f576..f850b9d2bb 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-documentation-search',
- _legacyUndefinedCheck: true,
properties: {
/**
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html
index 84addb0da5..84298e20f3 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-documentation-search</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-documentation-search.html">
diff --git a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.html b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.html
index 093e979f53..19a4e639a6 100644
--- a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.html
+++ b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-default-editor">
@@ -25,6 +25,8 @@ limitations under the License.
border: none;
box-sizing: border-box;
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-code);
+ line-height: var(--line-height-code);
min-height: 60vh;
resize: none;
white-space: pre;
diff --git a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
index 01cd9df808..ed96bb23eb 100644
--- a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
+++ b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-default-editor',
- _legacyUndefinedCheck: true,
/**
* Fired when the content of the editor changes.
@@ -32,8 +31,9 @@
},
_handleTextareaInput(e) {
- this.dispatchEvent(new CustomEvent('content-change',
- {detail: {value: e.target.value}, bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'content-change',
+ {detail: {value: e.target.value}, bubbles: true, composed: true}));
},
});
})();
diff --git a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html
index b79cd9d66b..c986e7c9b7 100644
--- a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor_test.html
@@ -17,9 +17,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-default-editor</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-default-editor.html">
@@ -50,7 +52,7 @@ limitations under the License.
element.addEventListener('content-change', contentChangedHandler);
textarea.value = 'test';
textarea.dispatchEvent(new CustomEvent('input',
- {target: textarea, bubbles: true}));
+ {target: textarea, bubbles: true, composed: true}));
});
});
</script>
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
index 81b3c076c3..52692a7431 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -43,7 +43,7 @@ limitations under the License.
display: none;
}
gr-button {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
text-decoration: none;
}
gr-dialog {
@@ -52,22 +52,23 @@ limitations under the License.
gr-dialog .main {
width: 100%;
}
+ gr-dialog .main > iron-input{
+ width: 100%;
+ }
gr-autocomplete {
--gr-autocomplete: {
border: 1px solid var(--border-color);
- border-radius: 2px;
- font-size: var(--font-size-normal);
+ border-radius: var(--border-radius);
height: 2em;
- padding: 0 .15em;
+ padding: 0 var(--spacing-xs);
}
}
input {
border: 1px solid var(--border-color);
- border-radius: 2px;
- font-size: var(--font-size-normal);
+ border-radius: var(--border-radius);
height: 2em;
- margin: .5em 0;
- padding: 0 .15em;
+ margin: var(--spacing-m) 0;
+ padding: 0 var(--spacing-xs);
width: 100%;
}
@media screen and (max-width: 50em) {
@@ -81,7 +82,7 @@ limitations under the License.
id$="[[action.id]]"
class$="[[_computeIsInvisible(action.id, hiddenActions)]]"
link
- on-tap="_handleTap">[[action.label]]</gr-button>
+ on-click="_handleTap">[[action.label]]</gr-button>
</template>
<gr-overlay id="overlay" with-backdrop>
<gr-dialog
@@ -132,11 +133,16 @@ limitations under the License.
placeholder="Enter an existing full file path."
query="[[_query]]"
text="{{_path}}"></gr-autocomplete>
- <input
- class="newPathInput"
- is="iron-input"
+ <iron-input
+ class="newPathIronInput"
bind-value="{{_newPath}}"
- placeholder="Enter the new path."/>
+ placeholder="Enter the new path.">
+ <input
+ class="newPathInput"
+ is="iron-input"
+ bind-value="{{_newPath}}"
+ placeholder="Enter the new path.">
+ </iron-input>
</div>
</gr-dialog>
<gr-dialog
@@ -148,10 +154,14 @@ limitations under the License.
on-cancel="_handleDialogCancel">
<div class="header" slot="header">Restore this file?</div>
<div class="main" slot="main">
- <input
- is="iron-input"
+ <iron-input
disabled
- bind-value="{{_path}}"/>
+ bind-value="{{_path}}">
+ <input
+ is="iron-input"
+ disabled
+ bind-value="{{_path}}">
+ </iron-input>
</div>
</gr-dialog>
</gr-overlay>
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
index 3567d06f10..b7e12fe3b6 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-edit-controls',
- _legacyUndefinedCheck: true,
+
properties: {
change: Object,
patchNum: String,
@@ -170,7 +170,8 @@
// just make two separate queries.
dialog.querySelectorAll('gr-autocomplete')
.forEach(input => { input.text = ''; });
- dialog.querySelectorAll('input')
+
+ dialog.querySelectorAll('iron-input')
.forEach(input => { input.bindValue = ''; });
}
@@ -190,28 +191,33 @@
},
_handleDeleteConfirm(e) {
+ // Get the dialog before the api call as the event will change during bubbling
+ // which will make Polymer.dom(e).path an emtpy array in polymer 2
+ const dialog = this._getDialogFromEvent(e);
this.$.restAPI.deleteFileInChangeEdit(this.change._number, this._path)
.then(res => {
if (!res.ok) { return; }
- this._closeDialog(this._getDialogFromEvent(e), true);
+ this._closeDialog(dialog, true);
Gerrit.Nav.navigateToChange(this.change);
});
},
_handleRestoreConfirm(e) {
+ const dialog = this._getDialogFromEvent(e);
this.$.restAPI.restoreFileInChangeEdit(this.change._number, this._path)
.then(res => {
if (!res.ok) { return; }
- this._closeDialog(this._getDialogFromEvent(e), true);
+ this._closeDialog(dialog, true);
Gerrit.Nav.navigateToChange(this.change);
});
},
_handleRenameConfirm(e) {
+ const dialog = this._getDialogFromEvent(e);
return this.$.restAPI.renameFileInChangeEdit(this.change._number,
this._path, this._newPath).then(res => {
if (!res.ok) { return; }
- this._closeDialog(this._getDialogFromEvent(e), true);
+ this._closeDialog(dialog, true);
Gerrit.Nav.navigateToChange(this.change);
});
},
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
index c67a2afdb8..dd8cb74841 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
@@ -17,9 +17,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-edit-controls</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-edit-controls.html">
@@ -189,6 +191,9 @@ suite('gr-edit-controls tests', () => {
let navStub;
let renameStub;
let renameAutocomplete;
+ const inputSelector = Polymer.Element ?
+ '.newPathIronInput' :
+ '.newPathInput';
setup(() => {
navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
@@ -208,7 +213,7 @@ suite('gr-edit-controls tests', () => {
assert.isTrue(queryStub.called);
assert.isTrue(element.$.renameDialog.disabled);
- element.$.renameDialog.querySelector('.newPathInput').bindValue =
+ element.$.renameDialog.querySelector(inputSelector).bindValue =
'src/test.newPath';
assert.isFalse(element.$.renameDialog.disabled);
@@ -236,7 +241,7 @@ suite('gr-edit-controls tests', () => {
assert.isTrue(queryStub.called);
assert.isTrue(element.$.renameDialog.disabled);
- element.$.renameDialog.querySelector('.newPathInput').bindValue =
+ element.$.renameDialog.querySelector(inputSelector).bindValue =
'src/test.newPath';
assert.isFalse(element.$.renameDialog.disabled);
@@ -258,7 +263,7 @@ suite('gr-edit-controls tests', () => {
assert.isTrue(element.$.renameDialog.disabled);
element.$.renameDialog.querySelector('gr-autocomplete').text =
'src/test.cpp';
- element.$.renameDialog.querySelector('.newPathInput').bindValue =
+ element.$.renameDialog.querySelector(inputSelector).bindValue =
'src/test.newPath';
assert.isFalse(element.$.renameDialog.disabled);
MockInteractions.tap(element.$.renameDialog.$$('gr-button'));
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.html b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.html
index c57a147c12..f6c7803194 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
@@ -32,7 +32,7 @@ limitations under the License.
justify-content: flex-end;
}
#actions {
- margin-right: 1em;
+ margin-right: var(--spacing-l);
}
gr-button,
gr-dropdown {
@@ -45,7 +45,6 @@ limitations under the License.
background-color: transparent;
border: none;
color: var(--link-color);
- font-size: inherit;
text-transform: uppercase;
}
}
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
index 9407f18ca6..250816b970 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-edit-file-controls',
- _legacyUndefinedCheck: true,
/**
* Fired when an action in the overflow menu is tapped.
@@ -46,8 +45,9 @@
},
_dispatchFileAction(action, path) {
- this.dispatchEvent(new CustomEvent('file-action-tap',
- {detail: {action, path}, bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'file-action-tap',
+ {detail: {action, path}, bubbles: true, composed: true}));
},
_computeFileActions(actions) {
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html
index 12d9e0b182..7979e57f33 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html
@@ -17,9 +17,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-edit-file-controls</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../gr-edit-constants.html">
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html
index b107221fed..ce90c69764 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html
@@ -15,8 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
@@ -47,23 +48,23 @@ limitations under the License.
align-items: center;
display: flex;
justify-content: space-between;
- padding: .75em var(--default-horizontal-margin);
+ padding: var(--spacing-m) var(--spacing-l);
}
header gr-editable-label {
- font-size: var(--font-size-large);
+ font-size: var(--font-size-h3);
--label-style: {
text-overflow: initial;
white-space: initial;
word-break: break-all;
}
--input-style: {
- margin-top: 1em;
+ margin-top: var(--spacing-l);
}
}
.textareaWrapper {
border: 1px solid var(--border-color);
- border-radius: 3px;
- margin: var(--default-horizontal-margin);
+ border-radius: var(--border-radius);
+ margin: var(--spacing-l);
}
.textareaWrapper .editButtons {
display: none;
@@ -71,7 +72,7 @@ limitations under the License.
.controlGroup {
align-items: center;
display: flex;
- font-size: var(--font-size-large);
+ font-size: var(--font-size-h3);
}
.rightControls {
justify-content: flex-end;
@@ -101,13 +102,13 @@ limitations under the License.
<gr-button
id="close"
link
- on-tap="_handleCloseTap">Close</gr-button>
+ on-click="_handleCloseTap">Close</gr-button>
<gr-button
id="save"
disabled$="[[_saveDisabled]]"
primary
link
- on-tap="_saveEdit">Save</gr-button>
+ on-click="_saveEdit">Save</gr-button>
</span>
</header>
</gr-fixed-panel>
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
index 3ed165e6b4..a21975d9b2 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
@@ -26,7 +26,6 @@
Polymer({
is: 'gr-editor-view',
- _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -75,6 +74,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PatchSetBehavior,
Gerrit.PathListBehavior,
@@ -105,7 +105,9 @@
},
_paramsChanged(value) {
- if (value.view !== Gerrit.Nav.View.EDIT) { return; }
+ if (value.view !== Gerrit.Nav.View.EDIT) {
+ return;
+ }
this._changeNum = value.changeNum;
this._path = value.path;
@@ -136,7 +138,9 @@
_handlePathChanged(e) {
const path = e.detail;
- if (path === this._path) { return Promise.resolve(); }
+ if (path === this._path) {
+ return Promise.resolve();
+ }
return this.$.restAPI.renameFileInChangeEdit(this._changeNum,
this._path, path).then(res => {
if (!res.ok) { return; }
@@ -160,8 +164,11 @@
.then(res => {
if (storedContent && storedContent.message &&
storedContent.message !== res.content) {
- this.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message: RESTORED_MESSAGE}, bubbles: true}));
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {message: RESTORED_MESSAGE},
+ bubbles: true,
+ composed: true,
+ }));
this._newContent = storedContent.message;
} else {
@@ -199,11 +206,23 @@
this.dispatchEvent(new CustomEvent('show-alert', {
detail: {message},
bubbles: true,
+ composed: true,
}));
},
_computeSaveDisabled(content, newContent, saving) {
- if (saving) { return true; }
+ // Polymer 2: check for undefined
+ if ([
+ content,
+ newContent,
+ saving,
+ ].some(arg => arg === undefined)) {
+ return true;
+ }
+
+ if (saving) {
+ return true;
+ }
return content === newContent;
},
@@ -226,7 +245,9 @@
_handleSaveShortcut(e) {
e.preventDefault();
- if (!this._saveDisabled) { this._saveEdit(); }
+ if (!this._saveDisabled) {
+ this._saveEdit();
+ }
},
});
})();
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html
index 010ff4adec..226472fae3 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html
@@ -17,9 +17,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-editor-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-editor-view.html">
@@ -121,7 +123,7 @@ suite('gr-editor-view tests', () => {
const storeStub = sandbox.spy(element.$.storage, 'setEditableContentItem');
element._newContent = 'test';
element.$.editorEndpoint.dispatchEvent(new CustomEvent('content-change', {
- bubbles: true,
+ bubbles: true, composed: true,
detail: {value: 'new content value'},
}));
element.flushDebouncer('store');
diff --git a/polygerrit-ui/app/elements/gr-app-element.html b/polygerrit-ui/app/elements/gr-app-element.html
new file mode 100644
index 0000000000..046e5ffd45
--- /dev/null
+++ b/polygerrit-ui/app/elements/gr-app-element.html
@@ -0,0 +1,238 @@
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<script src="/bower_components/moment/moment.js"></script>
+<script src="../scripts/util.js"></script>
+
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
+<link rel="import" href="../styles/shared-styles.html">
+<link rel="import" href="../styles/themes/app-theme.html">
+<link rel="import" href="./admin/gr-admin-view/gr-admin-view.html">
+<link rel="import" href="./documentation/gr-documentation-search/gr-documentation-search.html">
+<link rel="import" href="./change-list/gr-change-list-view/gr-change-list-view.html">
+<link rel="import" href="./change-list/gr-dashboard-view/gr-dashboard-view.html">
+<link rel="import" href="./change/gr-change-view/gr-change-view.html">
+<link rel="import" href="./core/gr-error-manager/gr-error-manager.html">
+<link rel="import" href="./core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html">
+<link rel="import" href="./core/gr-main-header/gr-main-header.html">
+<link rel="import" href="./core/gr-navigation/gr-navigation.html">
+<link rel="import" href="./core/gr-reporting/gr-reporting.html">
+<link rel="import" href="./core/gr-router/gr-router.html">
+<link rel="import" href="./core/gr-smart-search/gr-smart-search.html">
+<link rel="import" href="./diff/gr-diff-view/gr-diff-view.html">
+<link rel="import" href="./edit/gr-editor-view/gr-editor-view.html">
+<link rel="import" href="./plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
+<link rel="import" href="./plugins/gr-endpoint-param/gr-endpoint-param.html">
+<link rel="import" href="./plugins/gr-external-style/gr-external-style.html">
+<link rel="import" href="./plugins/gr-plugin-host/gr-plugin-host.html">
+<link rel="import" href="./settings/gr-cla-view/gr-cla-view.html">
+<link rel="import" href="./settings/gr-registration-dialog/gr-registration-dialog.html">
+<link rel="import" href="./settings/gr-settings-view/gr-settings-view.html">
+<link rel="import" href="./shared/gr-fixed-panel/gr-fixed-panel.html">
+<link rel="import" href="./shared/gr-lib-loader/gr-lib-loader.html">
+<link rel="import" href="./shared/gr-rest-api-interface/gr-rest-api-interface.html">
+
+<dom-module id="gr-app-element">
+ <template>
+ <style include="shared-styles">
+ :host {
+ background-color: var(--view-background-color);
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+ }
+ gr-fixed-panel {
+ /**
+ * This one should be greater that the z-index in gr-diff-view
+ * because gr-main-header contains overlay.
+ */
+ z-index: 10;
+ }
+ gr-main-header,
+ footer {
+ color: var(--primary-text-color);
+ }
+ gr-main-header {
+ background: var(--header-background, var(--header-background-color, #eee));
+ padding: var(--header-padding);
+ border-bottom: var(--header-border-bottom);
+ border-image: var(--header-border-image);
+ border-right: 0;
+ border-left: 0;
+ border-top: 0;
+ box-shadow: var(--header-box-shadow);
+ }
+ footer {
+ background: var(--footer-background, var(--footer-background-color, #eee));
+ border-top: var(--footer-border-top);
+ display: flex;
+ justify-content: space-between;
+ padding: var(--spacing-m) var(--spacing-l);
+ z-index: 100;
+ }
+ main {
+ flex: 1;
+ padding-bottom: var(--spacing-xxl);
+ position: relative;
+ }
+ .errorView {
+ align-items: center;
+ display: none;
+ flex-direction: column;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+ .errorView.show {
+ display: flex;
+ }
+ .errorEmoji {
+ font-size: 2.6rem;
+ }
+ .errorText,
+ .errorMoreInfo {
+ margin-top: var(--spacing-m);
+ }
+ .errorText {
+ font-size: var(--font-size-h3);
+ }
+ .errorMoreInfo {
+ color: var(--deemphasized-text-color);
+ }
+ .feedback {
+ color: var(--error-text-color);
+ }
+ </style>
+ <gr-endpoint-decorator name="banner"></gr-endpoint-decorator>
+ <gr-fixed-panel id="header">
+ <gr-main-header
+ id="mainHeader"
+ search-query="{{params.query}}"
+ on-mobile-search="_mobileSearchToggle">
+ </gr-main-header>
+ </gr-fixed-panel>
+ <main>
+ <gr-smart-search
+ id="search"
+ search-query="{{params.query}}"
+ hidden="[[!mobileSearch]]">
+ </gr-smart-search>
+ <template is="dom-if" if="[[_showChangeListView]]" restamp="true">
+ <gr-change-list-view
+ params="[[params]]"
+ account="[[_account]]"
+ view-state="{{_viewState.changeListView}}"></gr-change-list-view>
+ </template>
+ <template is="dom-if" if="[[_showDashboardView]]" restamp="true">
+ <gr-dashboard-view
+ account="[[_account]]"
+ params="[[params]]"
+ view-state="{{_viewState.dashboardView}}"></gr-dashboard-view>
+ </template>
+ <template is="dom-if" if="[[_showChangeView]]" restamp="true">
+ <gr-change-view
+ params="[[params]]"
+ view-state="{{_viewState.changeView}}"
+ back-page="[[_lastSearchPage]]"></gr-change-view>
+ </template>
+ <template is="dom-if" if="[[_showEditorView]]" restamp="true">
+ <gr-editor-view
+ params="[[params]]"></gr-editor-view>
+ </template>
+ <template is="dom-if" if="[[_showDiffView]]" restamp="true">
+ <gr-diff-view
+ params="[[params]]"
+ change-view-state="{{_viewState.changeView}}"></gr-diff-view>
+ </template>
+ <template is="dom-if" if="[[_showSettingsView]]" restamp="true">
+ <gr-settings-view
+ params="[[params]]"
+ on-account-detail-update="_handleAccountDetailUpdate">
+ </gr-settings-view>
+ </template>
+ <template is="dom-if" if="[[_showAdminView]]" restamp="true">
+ <gr-admin-view path="[[_path]]"
+ params=[[params]]></gr-admin-view>
+ </template>
+ <template is="dom-if" if="[[_showPluginScreen]]" restamp="true">
+ <gr-endpoint-decorator name="[[_pluginScreenName]]">
+ <gr-endpoint-param name="token" value="[[params.screen]]"></gr-endpoint-param>
+ </gr-endpoint-decorator>
+ </template>
+ <template is="dom-if" if="[[_showCLAView]]" restamp="true">
+ <gr-cla-view></gr-cla-view>
+ </template>
+ <template is="dom-if" if="[[_showDocumentationSearch]]" restamp="true">
+ <gr-documentation-search
+ params="[[params]]">
+ </gr-documentation-search>
+ </template>
+ <div id="errorView" class="errorView">
+ <div class="errorEmoji">[[_lastError.emoji]]</div>
+ <div class="errorText">[[_lastError.text]]</div>
+ <div class="errorMoreInfo">[[_lastError.moreInfo]]</div>
+ </div>
+ </main>
+ <footer r="contentinfo">
+ <div>
+ Powered by <a href="https://www.gerritcodereview.com/" rel="noopener"
+ target="_blank">Gerrit Code Review</a>
+ ([[_version]])
+ <gr-endpoint-decorator name="footer-left"></gr-endpoint-decorator>
+ </div>
+ <div>
+ <template is="dom-if" if="[[_feedbackUrl]]">
+ <a class="feedback"
+ href$="[[_feedbackUrl]]"
+ rel="noopener"
+ target="_blank">Report bug</a> |
+ </template>
+ Press &ldquo;?&rdquo; for keyboard shortcuts
+ <gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator>
+ </div>
+ </footer>
+ <gr-overlay id="keyboardShortcuts" with-backdrop>
+ <gr-keyboard-shortcuts-dialog
+ view="[[params.view]]"
+ on-close="_handleKeyboardShortcutDialogClose"></gr-keyboard-shortcuts-dialog>
+ </gr-overlay>
+ <gr-overlay id="registrationOverlay" with-backdrop>
+ <gr-registration-dialog
+ id="registrationDialog"
+ settings-url="[[_settingsUrl]]"
+ on-account-detail-update="_handleAccountDetailUpdate"
+ on-close="_handleRegistrationDialogClose">
+ </gr-registration-dialog>
+ </gr-overlay>
+ <gr-endpoint-decorator name="plugin-overlay"></gr-endpoint-decorator>
+ <gr-error-manager id="errorManager"></gr-error-manager>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ <gr-reporting id="reporting"></gr-reporting>
+ <gr-router id="router"></gr-router>
+ <gr-plugin-host id="plugins"
+ config="[[_serverConfig]]">
+ </gr-plugin-host>
+ <gr-lib-loader id="libLoader"></gr-lib-loader>
+ <gr-external-style id="externalStyleForAll" name="app-theme"></gr-external-style>
+ <gr-external-style id="externalStyleForTheme" name="[[getThemeEndpoint()]]"></gr-external-style>
+ </template>
+ <script src="gr-app-element.js" crossorigin="anonymous"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js
new file mode 100644
index 0000000000..ce6b98bedc
--- /dev/null
+++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -0,0 +1,468 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function() {
+ 'use strict';
+
+ Polymer({
+ is: 'gr-app-element',
+
+ /**
+ * Fired when the URL location changes.
+ *
+ * @event location-change
+ */
+
+ properties: {
+ /**
+ * @type {{ query: string, view: string, screen: string }}
+ */
+ params: Object,
+ keyEventTarget: {
+ type: Object,
+ value() { return document.body; },
+ },
+
+ _account: {
+ type: Object,
+ observer: '_accountChanged',
+ },
+
+ /**
+ * The last time the g key was pressed in milliseconds (or a keydown event
+ * was handled if the key is held down).
+ *
+ * @type {number|null}
+ */
+ _lastGKeyPressTimestamp: {
+ type: Number,
+ value: null,
+ },
+
+ /**
+ * @type {{ plugin: Object }}
+ */
+ _serverConfig: Object,
+ _version: String,
+ _showChangeListView: Boolean,
+ _showDashboardView: Boolean,
+ _showChangeView: Boolean,
+ _showDiffView: Boolean,
+ _showSettingsView: Boolean,
+ _showAdminView: Boolean,
+ _showCLAView: Boolean,
+ _showEditorView: Boolean,
+ _showPluginScreen: Boolean,
+ _showDocumentationSearch: Boolean,
+ /** @type {?} */
+ _viewState: Object,
+ /** @type {?} */
+ _lastError: Object,
+ _lastSearchPage: String,
+ _path: String,
+ _pluginScreenName: {
+ type: String,
+ computed: '_computePluginScreenName(params)',
+ },
+ _settingsUrl: String,
+ _feedbackUrl: String,
+ // Used to allow searching on mobile
+ mobileSearch: {
+ type: Boolean,
+ value: false,
+ },
+ },
+
+ listeners: {
+ 'page-error': '_handlePageError',
+ 'title-change': '_handleTitleChange',
+ 'location-change': '_handleLocationChange',
+ 'rpc-log': '_handleRpcLog',
+ },
+
+ observers: [
+ '_viewChanged(params.view)',
+ '_paramsChanged(params.*)',
+ ],
+
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ Gerrit.KeyboardShortcutBehavior,
+ ],
+
+ keyboardShortcuts() {
+ return {
+ [this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG]: '_showKeyboardShortcuts',
+ [this.Shortcut.GO_TO_USER_DASHBOARD]: '_goToUserDashboard',
+ [this.Shortcut.GO_TO_OPENED_CHANGES]: '_goToOpenedChanges',
+ [this.Shortcut.GO_TO_MERGED_CHANGES]: '_goToMergedChanges',
+ [this.Shortcut.GO_TO_ABANDONED_CHANGES]: '_goToAbandonedChanges',
+ [this.Shortcut.GO_TO_WATCHED_CHANGES]: '_goToWatchedChanges',
+ };
+ },
+
+ created() {
+ this._bindKeyboardShortcuts();
+ },
+
+ ready() {
+ this.$.reporting.appStarted(document.visibilityState === 'hidden');
+ this.$.router.start();
+
+ this.$.restAPI.getAccount().then(account => {
+ this._account = account;
+ });
+ this.$.restAPI.getConfig().then(config => {
+ this._serverConfig = config;
+
+ if (config && config.gerrit && config.gerrit.report_bug_url) {
+ this._feedbackUrl = config.gerrit.report_bug_url;
+ }
+ });
+ this.$.restAPI.getVersion().then(version => {
+ this._version = version;
+ this._logWelcome();
+ });
+
+ if (window.localStorage.getItem('dark-theme')) {
+ // No need to add the style module to element again as it's imported
+ // by importHref already
+ this.$.libLoader.getDarkTheme();
+ }
+
+ // Note: this is evaluated here to ensure that it only happens after the
+ // router has been initialized. @see Issue 7837
+ this._settingsUrl = Gerrit.Nav.getUrlForSettings();
+
+ this._viewState = {
+ changeView: {
+ changeNum: null,
+ patchRange: null,
+ selectedFileIndex: 0,
+ showReplyDialog: false,
+ diffMode: null,
+ numFilesShown: null,
+ scrollTop: 0,
+ },
+ changeListView: {
+ query: null,
+ offset: 0,
+ selectedChangeIndex: 0,
+ },
+ dashboardView: {
+ selectedChangeIndex: 0,
+ },
+ };
+ },
+
+ _bindKeyboardShortcuts() {
+ this.bindShortcut(this.Shortcut.SEND_REPLY,
+ this.DOC_ONLY, 'ctrl+enter', 'meta+enter');
+ this.bindShortcut(this.Shortcut.EMOJI_DROPDOWN,
+ this.DOC_ONLY, ':');
+
+ this.bindShortcut(
+ this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG, '?');
+ this.bindShortcut(
+ this.Shortcut.GO_TO_USER_DASHBOARD, this.GO_KEY, 'i');
+ this.bindShortcut(
+ this.Shortcut.GO_TO_OPENED_CHANGES, this.GO_KEY, 'o');
+ this.bindShortcut(
+ this.Shortcut.GO_TO_MERGED_CHANGES, this.GO_KEY, 'm');
+ this.bindShortcut(
+ this.Shortcut.GO_TO_ABANDONED_CHANGES, this.GO_KEY, 'a');
+ this.bindShortcut(
+ this.Shortcut.GO_TO_WATCHED_CHANGES, this.GO_KEY, 'w');
+
+ this.bindShortcut(
+ this.Shortcut.CURSOR_NEXT_CHANGE, 'j');
+ this.bindShortcut(
+ this.Shortcut.CURSOR_PREV_CHANGE, 'k');
+ this.bindShortcut(
+ this.Shortcut.OPEN_CHANGE, 'o');
+ this.bindShortcut(
+ this.Shortcut.NEXT_PAGE, 'n', ']');
+ this.bindShortcut(
+ this.Shortcut.PREV_PAGE, 'p', '[');
+ this.bindShortcut(
+ this.Shortcut.TOGGLE_CHANGE_REVIEWED, 'r:keyup');
+ this.bindShortcut(
+ this.Shortcut.TOGGLE_CHANGE_STAR, 's:keyup');
+ this.bindShortcut(
+ this.Shortcut.REFRESH_CHANGE_LIST, 'shift+r:keyup');
+ this.bindShortcut(
+ this.Shortcut.EDIT_TOPIC, 't');
+
+ this.bindShortcut(
+ this.Shortcut.OPEN_REPLY_DIALOG, 'a');
+ this.bindShortcut(
+ this.Shortcut.OPEN_DOWNLOAD_DIALOG, 'd');
+ this.bindShortcut(
+ this.Shortcut.EXPAND_ALL_MESSAGES, 'x');
+ this.bindShortcut(
+ this.Shortcut.COLLAPSE_ALL_MESSAGES, 'z');
+ this.bindShortcut(
+ this.Shortcut.REFRESH_CHANGE, 'shift+r:keyup');
+ this.bindShortcut(
+ this.Shortcut.UP_TO_DASHBOARD, 'u');
+ this.bindShortcut(
+ this.Shortcut.UP_TO_CHANGE, 'u');
+ this.bindShortcut(
+ this.Shortcut.TOGGLE_DIFF_MODE, 'm:keyup');
+
+ this.bindShortcut(
+ this.Shortcut.NEXT_LINE, 'j', 'down');
+ this.bindShortcut(
+ this.Shortcut.PREV_LINE, 'k', 'up');
+ this.bindShortcut(
+ this.Shortcut.NEXT_CHUNK, 'n');
+ this.bindShortcut(
+ this.Shortcut.PREV_CHUNK, 'p');
+ this.bindShortcut(
+ this.Shortcut.EXPAND_ALL_DIFF_CONTEXT, 'shift+x');
+ this.bindShortcut(
+ this.Shortcut.NEXT_COMMENT_THREAD, 'shift+n');
+ this.bindShortcut(
+ this.Shortcut.PREV_COMMENT_THREAD, 'shift+p');
+ this.bindShortcut(
+ this.Shortcut.EXPAND_ALL_COMMENT_THREADS, this.DOC_ONLY, 'e');
+ this.bindShortcut(
+ this.Shortcut.COLLAPSE_ALL_COMMENT_THREADS,
+ this.DOC_ONLY, 'shift+e');
+ this.bindShortcut(
+ this.Shortcut.LEFT_PANE, 'shift+left');
+ this.bindShortcut(
+ this.Shortcut.RIGHT_PANE, 'shift+right');
+ this.bindShortcut(
+ this.Shortcut.TOGGLE_LEFT_PANE, 'shift+a');
+ this.bindShortcut(
+ this.Shortcut.NEW_COMMENT, 'c');
+ this.bindShortcut(
+ this.Shortcut.SAVE_COMMENT,
+ 'ctrl+enter', 'meta+enter', 'ctrl+s', 'meta+s');
+ this.bindShortcut(
+ this.Shortcut.OPEN_DIFF_PREFS, ',');
+ this.bindShortcut(
+ this.Shortcut.TOGGLE_DIFF_REVIEWED, 'r:keyup');
+
+ this.bindShortcut(
+ this.Shortcut.NEXT_FILE, ']');
+ this.bindShortcut(
+ this.Shortcut.PREV_FILE, '[');
+ this.bindShortcut(
+ this.Shortcut.NEXT_FILE_WITH_COMMENTS, 'shift+j');
+ this.bindShortcut(
+ this.Shortcut.PREV_FILE_WITH_COMMENTS, 'shift+k');
+ this.bindShortcut(
+ this.Shortcut.CURSOR_NEXT_FILE, 'j', 'down');
+ this.bindShortcut(
+ this.Shortcut.CURSOR_PREV_FILE, 'k', 'up');
+ this.bindShortcut(
+ this.Shortcut.OPEN_FILE, 'o', 'enter');
+ this.bindShortcut(
+ this.Shortcut.TOGGLE_FILE_REVIEWED, 'r:keyup');
+ this.bindShortcut(
+ this.Shortcut.NEXT_UNREVIEWED_FILE, 'shift+m');
+ this.bindShortcut(
+ this.Shortcut.TOGGLE_ALL_INLINE_DIFFS, 'shift+i:keyup');
+ this.bindShortcut(
+ this.Shortcut.TOGGLE_INLINE_DIFF, 'i:keyup');
+
+ this.bindShortcut(
+ this.Shortcut.OPEN_FIRST_FILE, ']');
+ this.bindShortcut(
+ this.Shortcut.OPEN_LAST_FILE, '[');
+
+ this.bindShortcut(
+ this.Shortcut.SEARCH, '/');
+ },
+
+ _accountChanged(account) {
+ if (!account) { return; }
+
+ // Preferences are cached when a user is logged in; warm them.
+ this.$.restAPI.getPreferences();
+ this.$.restAPI.getDiffPreferences();
+ this.$.restAPI.getEditPreferences();
+ this.$.errorManager.knownAccountId =
+ this._account && this._account._account_id || null;
+ },
+
+ _viewChanged(view) {
+ this.$.errorView.classList.remove('show');
+ this.set('_showChangeListView', view === Gerrit.Nav.View.SEARCH);
+ this.set('_showDashboardView', view === Gerrit.Nav.View.DASHBOARD);
+ this.set('_showChangeView', view === Gerrit.Nav.View.CHANGE);
+ this.set('_showDiffView', view === Gerrit.Nav.View.DIFF);
+ this.set('_showSettingsView', view === Gerrit.Nav.View.SETTINGS);
+ this.set('_showAdminView', view === Gerrit.Nav.View.ADMIN ||
+ view === Gerrit.Nav.View.GROUP || view === Gerrit.Nav.View.REPO);
+ this.set('_showCLAView', view === Gerrit.Nav.View.AGREEMENTS);
+ this.set('_showEditorView', view === Gerrit.Nav.View.EDIT);
+ const isPluginScreen = view === Gerrit.Nav.View.PLUGIN_SCREEN;
+ this.set('_showPluginScreen', false);
+ // Navigation within plugin screens does not restamp gr-endpoint-decorator
+ // because _showPluginScreen value does not change. To force restamp,
+ // change _showPluginScreen value between true and false.
+ if (isPluginScreen) {
+ this.async(() => this.set('_showPluginScreen', true), 1);
+ }
+ this.set('_showDocumentationSearch',
+ view === Gerrit.Nav.View.DOCUMENTATION_SEARCH);
+ if (this.params.justRegistered) {
+ this.$.registrationOverlay.open();
+ this.$.registrationDialog.loadData().then(() => {
+ this.$.registrationOverlay.refit();
+ });
+ }
+ this.$.header.unfloat();
+ },
+
+ _handlePageError(e) {
+ const props = [
+ '_showChangeListView',
+ '_showDashboardView',
+ '_showChangeView',
+ '_showDiffView',
+ '_showSettingsView',
+ '_showAdminView',
+ ];
+ for (const showProp of props) {
+ this.set(showProp, false);
+ }
+
+ this.$.errorView.classList.add('show');
+ const response = e.detail.response;
+ const err = {text: [response.status, response.statusText].join(' ')};
+ if (response.status === 404) {
+ err.emoji = '¯\\_(ツ)_/¯';
+ this._lastError = err;
+ } else {
+ err.emoji = 'o_O';
+ response.text().then(text => {
+ err.moreInfo = text;
+ this._lastError = err;
+ });
+ }
+ },
+
+ _handleLocationChange(e) {
+ const hash = e.detail.hash.substring(1);
+ let pathname = e.detail.pathname;
+ if (pathname.startsWith('/c/') && parseInt(hash, 10) > 0) {
+ pathname += '@' + hash;
+ }
+ this.set('_path', pathname);
+ },
+
+ _paramsChanged(paramsRecord) {
+ const params = paramsRecord.base;
+ const viewsToCheck = [Gerrit.Nav.View.SEARCH, Gerrit.Nav.View.DASHBOARD];
+ if (viewsToCheck.includes(params.view)) {
+ this.set('_lastSearchPage', location.pathname);
+ }
+ },
+
+ _handleTitleChange(e) {
+ if (e.detail.title) {
+ document.title = e.detail.title + ' · Gerrit Code Review';
+ } else {
+ document.title = '';
+ }
+ },
+
+ _showKeyboardShortcuts(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ this.$.keyboardShortcuts.open();
+ },
+
+ _handleKeyboardShortcutDialogClose() {
+ this.$.keyboardShortcuts.close();
+ },
+
+ _handleAccountDetailUpdate(e) {
+ this.$.mainHeader.reload();
+ if (this.params.view === Gerrit.Nav.View.SETTINGS) {
+ this.$$('gr-settings-view').reloadAccountDetail();
+ }
+ },
+
+ _handleRegistrationDialogClose(e) {
+ this.params.justRegistered = false;
+ this.$.registrationOverlay.close();
+ },
+
+ _goToOpenedChanges() {
+ Gerrit.Nav.navigateToStatusSearch('open');
+ },
+
+ _goToUserDashboard() {
+ Gerrit.Nav.navigateToUserDashboard();
+ },
+
+ _goToMergedChanges() {
+ Gerrit.Nav.navigateToStatusSearch('merged');
+ },
+
+ _goToAbandonedChanges() {
+ Gerrit.Nav.navigateToStatusSearch('abandoned');
+ },
+
+ _goToWatchedChanges() {
+ // The query is hardcoded, and doesn't respect custom menu entries
+ Gerrit.Nav.navigateToSearchQuery('is:watched is:open');
+ },
+
+ _computePluginScreenName({plugin, screen}) {
+ if (!plugin || !screen) return '';
+ return `${plugin}-screen-${screen}`;
+ },
+
+ _logWelcome() {
+ console.group('Runtime Info');
+ console.log('Gerrit UI (PolyGerrit)');
+ console.log(`Gerrit Server Version: ${this._version}`);
+ if (window.VERSION_INFO) {
+ console.log(`UI Version Info: ${window.VERSION_INFO}`);
+ }
+ if (this._feedbackUrl) {
+ console.log(`Please file bugs and feedback at: ${this._feedbackUrl}`);
+ }
+ console.groupEnd();
+ },
+
+ /**
+ * Intercept RPC log events emitted by REST API interfaces.
+ * Note: the REST API interface cannot use gr-reporting directly because
+ * that would create a cyclic dependency.
+ */
+ _handleRpcLog(e) {
+ this.$.reporting.reportRpcTiming(e.detail.anonymizedUrl,
+ e.detail.elapsed);
+ },
+
+ _mobileSearchToggle(e) {
+ this.mobileSearch = !this.mobileSearch;
+ },
+
+ getThemeEndpoint() {
+ // For now, we only have dark mode and light mode
+ return window.localStorage.getItem('dark-theme') ?
+ 'app-theme-dark' :
+ 'app-theme-light';
+ },
+ });
+})();
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 8c50358992..f49d8aaea0 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -15,28 +15,18 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
- if (!window.POLYMER2) {
- // This must be set prior to loading Polymer for the first time.
- if (localStorage.getItem('USE_SHADOW_DOM') === 'true') {
- window.Polymer = {
- dom: 'shadow',
- passiveTouchGestures: true,
- };
- } else if (!window.Polymer) {
- window.Polymer = {
- passiveTouchGestures: true,
- };
- }
+ if (!window.Polymer) {
+ window.Polymer = {
+ passiveTouchGestures: true,
+ lazyRegister: true,
+ };
}
- // Needed for JSCompiler to understand it's global.
- // eslint-disable-next-line no-unused-vars, prefer-const
- let Gerrit = window.Gerrit || {};
- window.Gerrit = Gerrit;
+ window.Gerrit = window.Gerrit || {};
</script>
-<link rel="import" href="../bower_components/polymer/polymer.html">
-<link rel="import" href="../bower_components/polymer-resin/standalone/polymer-resin.html">
-<link rel="import" href="../bower_components/polymer/lib/legacy/legacy-data-mixin.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer-resin/standalone/polymer-resin.html">
+<!-- TODO(taoalpha): Remove once all legacyUndefinedCheck removed. -->
<link rel="import" href="../behaviors/safe-types-behavior/safe-types-behavior.html">
<script>
security.polymer_resin.install({
@@ -45,228 +35,11 @@ limitations under the License.
safeTypesBridge: Gerrit.SafeTypes.safeTypesBridge,
});
</script>
-<script src="../bower_components/moment/moment.js"></script>
-
-<link rel="import" href="../behaviors/base-url-behavior/base-url-behavior.html">
-<link rel="import" href="../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../styles/shared-styles.html">
-<link rel="import" href="../styles/themes/app-theme.html">
-<link rel="import" href="./admin/gr-admin-view/gr-admin-view.html">
-<link rel="import" href="./documentation/gr-documentation-search/gr-documentation-search.html">
-<link rel="import" href="./change-list/gr-change-list-view/gr-change-list-view.html">
-<link rel="import" href="./change-list/gr-dashboard-view/gr-dashboard-view.html">
-<link rel="import" href="./change/gr-change-view/gr-change-view.html">
-<link rel="import" href="./core/gr-error-manager/gr-error-manager.html">
-<link rel="import" href="./core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html">
-<link rel="import" href="./core/gr-main-header/gr-main-header.html">
-<link rel="import" href="./core/gr-navigation/gr-navigation.html">
-<link rel="import" href="./core/gr-reporting/gr-reporting.html">
-<link rel="import" href="./core/gr-router/gr-router.html">
-<link rel="import" href="./core/gr-smart-search/gr-smart-search.html">
-<link rel="import" href="./diff/gr-diff-view/gr-diff-view.html">
-<link rel="import" href="./edit/gr-editor-view/gr-editor-view.html">
-<link rel="import" href="./plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
-<link rel="import" href="./plugins/gr-endpoint-param/gr-endpoint-param.html">
-<link rel="import" href="./plugins/gr-external-style/gr-external-style.html">
-<link rel="import" href="./plugins/gr-plugin-host/gr-plugin-host.html">
-<link rel="import" href="./settings/gr-cla-view/gr-cla-view.html">
-<link rel="import" href="./settings/gr-registration-dialog/gr-registration-dialog.html">
-<link rel="import" href="./settings/gr-settings-view/gr-settings-view.html">
-<link rel="import" href="./shared/gr-fixed-panel/gr-fixed-panel.html">
-<link rel="import" href="./shared/gr-lib-loader/gr-lib-loader.html">
-<link rel="import" href="./shared/gr-rest-api-interface/gr-rest-api-interface.html">
-
-<script src="../scripts/util.js"></script>
+<link rel="import" href="./gr-app-element.html">
<dom-module id="gr-app">
<template>
- <style include="shared-styles">
- :host {
- background-color: var(--view-background-color);
- display: flex;
- flex-direction: column;
- min-height: 100%;
- }
- gr-fixed-panel {
- /**
- * This one should be greater that the z-index in gr-diff-view
- * because gr-main-header contains overlay.
- */
- z-index: 10;
- }
- gr-main-header,
- footer {
- color: var(--primary-text-color);
- }
- gr-main-header {
- background: var(--header-background, var(--header-background-color, #eee));
- padding: 0 var(--default-horizontal-margin);
- border-bottom: var(--header-border-bottom);
- border-image: var(--header-border-image);
- border-right: 0;
- border-left: 0;
- border-top: 0;
- }
- gr-main-header.shadow {
- /* Make it obvious for shadow dom testing */
- border-bottom: 1px solid pink;
- }
- footer {
- background: var(--footer-background, var(--footer-background-color, #eee));
- border-top: var(--footer-border-top);
- display: flex;
- justify-content: space-between;
- padding: .5rem var(--default-horizontal-margin);
- z-index: 100;
- }
- main {
- flex: 1;
- padding-bottom: 2em;
- position: relative;
- }
- .errorView {
- align-items: center;
- display: none;
- flex-direction: column;
- justify-content: center;
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- }
- .errorView.show {
- display: flex;
- }
- .errorEmoji {
- font-size: 2.6rem;
- }
- .errorText,
- .errorMoreInfo {
- margin-top: .75em;
- }
- .errorText {
- font-size: 1.2rem;
- }
- .errorMoreInfo {
- color: var(--deemphasized-text-color);
- }
- .feedback {
- color: var(--error-text-color);
- }
- </style>
- <gr-endpoint-decorator name="banner"></gr-endpoint-decorator>
- <gr-fixed-panel id="header">
- <gr-main-header
- id="mainHeader"
- search-query="{{params.query}}"
- class$="[[_computeShadowClass(_isShadowDom)]]"
- on-mobile-search="_mobileSearchToggle">
- </gr-main-header>
- </gr-fixed-panel>
- <main>
- <gr-smart-search
- id="search"
- search-query="{{params.query}}"
- hidden="[[!mobileSearch]]">
- </gr-smart-search>
- <template is="dom-if" if="[[_showChangeListView]]" restamp="true">
- <gr-change-list-view
- params="[[params]]"
- account="[[_account]]"
- view-state="{{_viewState.changeListView}}"></gr-change-list-view>
- </template>
- <template is="dom-if" if="[[_showDashboardView]]" restamp="true">
- <gr-dashboard-view
- account="[[_account]]"
- params="[[params]]"
- view-state="{{_viewState.dashboardView}}"></gr-dashboard-view>
- </template>
- <template is="dom-if" if="[[_showChangeView]]" restamp="true">
- <gr-change-view
- params="[[params]]"
- view-state="{{_viewState.changeView}}"
- back-page="[[_lastSearchPage]]"></gr-change-view>
- </template>
- <template is="dom-if" if="[[_showEditorView]]" restamp="true">
- <gr-editor-view
- params="[[params]]"></gr-editor-view>
- </template>
- <template is="dom-if" if="[[_showDiffView]]" restamp="true">
- <gr-diff-view
- params="[[params]]"
- change-view-state="{{_viewState.changeView}}"></gr-diff-view>
- </template>
- <template is="dom-if" if="[[_showSettingsView]]" restamp="true">
- <gr-settings-view
- params="[[params]]"
- on-account-detail-update="_handleAccountDetailUpdate">
- </gr-settings-view>
- </template>
- <template is="dom-if" if="[[_showAdminView]]" restamp="true">
- <gr-admin-view path="[[_path]]"
- params=[[params]]></gr-admin-view>
- </template>
- <template is="dom-if" if="[[_showPluginScreen]]" restamp="true">
- <gr-endpoint-decorator name="[[_pluginScreenName]]">
- <gr-endpoint-param name="token" value="[[params.screen]]"></gr-endpoint-param>
- </gr-endpoint-decorator>
- </template>
- <template is="dom-if" if="[[_showCLAView]]" restamp="true">
- <gr-cla-view></gr-cla-view>
- </template>
- <template is="dom-if" if="[[_showDocumentationSearch]]" restamp="true">
- <gr-documentation-search
- params="[[params]]">
- </gr-documentation-search>
- </template>
- <div id="errorView" class="errorView">
- <div class="errorEmoji">[[_lastError.emoji]]</div>
- <div class="errorText">[[_lastError.text]]</div>
- <div class="errorMoreInfo">[[_lastError.moreInfo]]</div>
- </div>
- </main>
- <footer r="contentinfo" class$="[[_computeShadowClass(_isShadowDom)]]">
- <div>
- Powered by <a href="https://www.gerritcodereview.com/" rel="noopener"
- target="_blank">Gerrit Code Review</a>
- ([[_version]])
- <gr-endpoint-decorator name="footer-left"></gr-endpoint-decorator>
- </div>
- <div>
- <template is="dom-if" if="[[_feedbackUrl]]">
- <a class="feedback"
- href$="[[_feedbackUrl]]"
- rel="noopener"
- target="_blank">Send feedback</a> |
- </template>
- Press &ldquo;?&rdquo; for keyboard shortcuts
- <gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator>
- </div>
- </footer>
- <gr-overlay id="keyboardShortcuts" with-backdrop>
- <gr-keyboard-shortcuts-dialog
- view="[[params.view]]"
- on-close="_handleKeyboardShortcutDialogClose"></gr-keyboard-shortcuts-dialog>
- </gr-overlay>
- <gr-overlay id="registrationOverlay" with-backdrop>
- <gr-registration-dialog
- id="registrationDialog"
- settings-url="[[_settingsUrl]]"
- on-account-detail-update="_handleAccountDetailUpdate"
- on-close="_handleRegistrationDialogClose">
- </gr-registration-dialog>
- </gr-overlay>
- <gr-endpoint-decorator name="plugin-overlay"></gr-endpoint-decorator>
- <gr-error-manager id="errorManager"></gr-error-manager>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
- <gr-reporting id="reporting"></gr-reporting>
- <gr-router id="router"></gr-router>
- <gr-plugin-host id="plugins"
- config="[[_serverConfig]]">
- </gr-plugin-host>
- <gr-lib-loader id="libLoader"></gr-lib-loader>
- <gr-external-style id="externalStyle" name="app-theme"></gr-external-style>
+ <gr-app-element id="app-element"></gr-app-element>
</template>
<script src="gr-app.js" crossorigin="anonymous"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index b89d90ede7..ac8ea1a260 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -17,461 +17,7 @@
(function() {
'use strict';
- // Eagerly render Polymer components when backgrounded. (Skips
- // requestAnimationFrame.)
- // @see https://github.com/Polymer/polymer/issues/3851
- // @see Issue 4699
- if (!window.POLYMER2) {
- Polymer.RenderStatus._makeReady();
- }
-
Polymer({
is: 'gr-app',
- _legacyUndefinedCheck: true,
-
- /**
- * Fired when the URL location changes.
- *
- * @event location-change
- */
-
- properties: {
- /**
- * @type {{ query: string, view: string, screen: string }}
- */
- params: Object,
- keyEventTarget: {
- type: Object,
- value() { return document.body; },
- },
-
- _account: {
- type: Object,
- observer: '_accountChanged',
- },
-
- /**
- * The last time the g key was pressed in milliseconds (or a keydown event
- * was handled if the key is held down).
- *
- * @type {number|null}
- */
- _lastGKeyPressTimestamp: {
- type: Number,
- value: null,
- },
-
- /**
- * @type {{ plugin: Object }}
- */
- _serverConfig: Object,
- _version: String,
- _showChangeListView: Boolean,
- _showDashboardView: Boolean,
- _showChangeView: Boolean,
- _showDiffView: Boolean,
- _showSettingsView: Boolean,
- _showAdminView: Boolean,
- _showCLAView: Boolean,
- _showEditorView: Boolean,
- _showPluginScreen: Boolean,
- _showDocumentationSearch: Boolean,
- /** @type {?} */
- _viewState: Object,
- /** @type {?} */
- _lastError: Object,
- _lastSearchPage: String,
- _path: String,
- _isShadowDom: Boolean,
- _pluginScreenName: {
- type: String,
- computed: '_computePluginScreenName(params)',
- },
- _settingsUrl: String,
- _feedbackUrl: String,
- // Used to allow searching on mobile
- mobileSearch: {
- type: Boolean,
- value: false,
- },
- },
-
- listeners: {
- 'page-error': '_handlePageError',
- 'title-change': '_handleTitleChange',
- 'location-change': '_handleLocationChange',
- 'rpc-log': '_handleRpcLog',
- },
-
- observers: [
- '_viewChanged(params.view)',
- '_paramsChanged(params.*)',
- ],
-
- behaviors: [
- Gerrit.BaseUrlBehavior,
- Gerrit.KeyboardShortcutBehavior,
- ],
-
- keyboardShortcuts() {
- return {
- [this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG]: '_showKeyboardShortcuts',
- [this.Shortcut.GO_TO_USER_DASHBOARD]: '_goToUserDashboard',
- [this.Shortcut.GO_TO_OPENED_CHANGES]: '_goToOpenedChanges',
- [this.Shortcut.GO_TO_MERGED_CHANGES]: '_goToMergedChanges',
- [this.Shortcut.GO_TO_ABANDONED_CHANGES]: '_goToAbandonedChanges',
- [this.Shortcut.GO_TO_WATCHED_CHANGES]: '_goToWatchedChanges',
- };
- },
-
- created() {
- this._bindKeyboardShortcuts();
- },
-
- ready() {
- this._isShadowDom = Polymer.Settings.useShadow;
- this.$.router.start();
-
- this.$.restAPI.getAccount().then(account => {
- this._account = account;
- });
- this.$.restAPI.getConfig().then(config => {
- this._serverConfig = config;
-
- if (config && config.gerrit && config.gerrit.report_bug_url) {
- this._feedbackUrl = config.gerrit.report_bug_url;
- }
- });
- this.$.restAPI.getVersion().then(version => {
- this._version = version;
- this._logWelcome();
- });
-
- if (window.localStorage.getItem('dark-theme')) {
- this.$.libLoader.getDarkTheme().then(module => {
- Polymer.dom(this.root).appendChild(module);
- });
- }
-
- // Note: this is evaluated here to ensure that it only happens after the
- // router has been initialized. @see Issue 7837
- this._settingsUrl = Gerrit.Nav.getUrlForSettings();
-
- this.$.reporting.appStarted(document.visibilityState === 'hidden');
-
- this._viewState = {
- changeView: {
- changeNum: null,
- patchRange: null,
- selectedFileIndex: 0,
- showReplyDialog: false,
- diffMode: null,
- numFilesShown: null,
- scrollTop: 0,
- },
- changeListView: {
- query: null,
- offset: 0,
- selectedChangeIndex: 0,
- },
- dashboardView: {
- selectedChangeIndex: 0,
- },
- };
- },
-
- _bindKeyboardShortcuts() {
- this.bindShortcut(this.Shortcut.SEND_REPLY,
- this.DOC_ONLY, 'ctrl+enter', 'meta+enter');
-
- this.bindShortcut(
- this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG, '?');
- this.bindShortcut(
- this.Shortcut.GO_TO_USER_DASHBOARD, this.GO_KEY, 'i');
- this.bindShortcut(
- this.Shortcut.GO_TO_OPENED_CHANGES, this.GO_KEY, 'o');
- this.bindShortcut(
- this.Shortcut.GO_TO_MERGED_CHANGES, this.GO_KEY, 'm');
- this.bindShortcut(
- this.Shortcut.GO_TO_ABANDONED_CHANGES, this.GO_KEY, 'a');
- this.bindShortcut(
- this.Shortcut.GO_TO_WATCHED_CHANGES, this.GO_KEY, 'w');
-
- this.bindShortcut(
- this.Shortcut.CURSOR_NEXT_CHANGE, 'j');
- this.bindShortcut(
- this.Shortcut.CURSOR_PREV_CHANGE, 'k');
- this.bindShortcut(
- this.Shortcut.OPEN_CHANGE, 'o');
- this.bindShortcut(
- this.Shortcut.NEXT_PAGE, 'n', ']');
- this.bindShortcut(
- this.Shortcut.PREV_PAGE, 'p', '[');
- this.bindShortcut(
- this.Shortcut.TOGGLE_CHANGE_REVIEWED, 'r');
- this.bindShortcut(
- this.Shortcut.TOGGLE_CHANGE_STAR, 's');
- this.bindShortcut(
- this.Shortcut.REFRESH_CHANGE_LIST, 'shift+r');
- this.bindShortcut(
- this.Shortcut.EDIT_TOPIC, 't');
-
- this.bindShortcut(
- this.Shortcut.OPEN_REPLY_DIALOG, 'a');
- this.bindShortcut(
- this.Shortcut.OPEN_DOWNLOAD_DIALOG, 'd');
- this.bindShortcut(
- this.Shortcut.EXPAND_ALL_MESSAGES, 'x');
- this.bindShortcut(
- this.Shortcut.COLLAPSE_ALL_MESSAGES, 'z');
- this.bindShortcut(
- this.Shortcut.REFRESH_CHANGE, 'shift+r');
- this.bindShortcut(
- this.Shortcut.UP_TO_DASHBOARD, 'u');
- this.bindShortcut(
- this.Shortcut.UP_TO_CHANGE, 'u');
- this.bindShortcut(
- this.Shortcut.TOGGLE_DIFF_MODE, 'm');
-
- this.bindShortcut(
- this.Shortcut.NEXT_LINE, 'j', 'down');
- this.bindShortcut(
- this.Shortcut.PREV_LINE, 'k', 'up');
- this.bindShortcut(
- this.Shortcut.NEXT_CHUNK, 'n');
- this.bindShortcut(
- this.Shortcut.PREV_CHUNK, 'p');
- this.bindShortcut(
- this.Shortcut.EXPAND_ALL_DIFF_CONTEXT, 'shift+x');
- this.bindShortcut(
- this.Shortcut.NEXT_COMMENT_THREAD, 'shift+n');
- this.bindShortcut(
- this.Shortcut.PREV_COMMENT_THREAD, 'shift+p');
- this.bindShortcut(
- this.Shortcut.EXPAND_ALL_COMMENT_THREADS, this.DOC_ONLY, 'e');
- this.bindShortcut(
- this.Shortcut.COLLAPSE_ALL_COMMENT_THREADS,
- this.DOC_ONLY, 'shift+e');
- this.bindShortcut(
- this.Shortcut.LEFT_PANE, 'shift+left');
- this.bindShortcut(
- this.Shortcut.RIGHT_PANE, 'shift+right');
- this.bindShortcut(
- this.Shortcut.TOGGLE_LEFT_PANE, 'shift+a');
- this.bindShortcut(
- this.Shortcut.NEW_COMMENT, 'c');
- this.bindShortcut(
- this.Shortcut.SAVE_COMMENT,
- 'ctrl+enter', 'meta+enter', 'ctrl+s', 'meta+s');
- this.bindShortcut(
- this.Shortcut.OPEN_DIFF_PREFS, ',');
- this.bindShortcut(
- this.Shortcut.TOGGLE_DIFF_REVIEWED, 'r');
-
- this.bindShortcut(
- this.Shortcut.NEXT_FILE, ']');
- this.bindShortcut(
- this.Shortcut.PREV_FILE, '[');
- this.bindShortcut(
- this.Shortcut.NEXT_FILE_WITH_COMMENTS, 'shift+j');
- this.bindShortcut(
- this.Shortcut.PREV_FILE_WITH_COMMENTS, 'shift+k');
- this.bindShortcut(
- this.Shortcut.CURSOR_NEXT_FILE, 'j', 'down');
- this.bindShortcut(
- this.Shortcut.CURSOR_PREV_FILE, 'k', 'up');
- this.bindShortcut(
- this.Shortcut.OPEN_FILE, 'o', 'enter');
- this.bindShortcut(
- this.Shortcut.TOGGLE_FILE_REVIEWED, 'r');
- this.bindShortcut(
- this.Shortcut.NEXT_UNREVIEWED_FILE, 'shift+m');
- this.bindShortcut(
- this.Shortcut.TOGGLE_ALL_INLINE_DIFFS, 'shift+i:keyup');
- this.bindShortcut(
- this.Shortcut.TOGGLE_INLINE_DIFF, 'i:keyup');
-
- this.bindShortcut(
- this.Shortcut.OPEN_FIRST_FILE, ']');
- this.bindShortcut(
- this.Shortcut.OPEN_LAST_FILE, '[');
-
- this.bindShortcut(
- this.Shortcut.SEARCH, '/');
- },
-
- _accountChanged(account) {
- if (!account) { return; }
-
- // Preferences are cached when a user is logged in; warm them.
- this.$.restAPI.getPreferences();
- this.$.restAPI.getDiffPreferences();
- this.$.restAPI.getEditPreferences();
- this.$.errorManager.knownAccountId =
- this._account && this._account._account_id || null;
- },
-
- _viewChanged(view) {
- this.$.errorView.classList.remove('show');
- this.set('_showChangeListView', view === Gerrit.Nav.View.SEARCH);
- this.set('_showDashboardView', view === Gerrit.Nav.View.DASHBOARD);
- this.set('_showChangeView', view === Gerrit.Nav.View.CHANGE);
- this.set('_showDiffView', view === Gerrit.Nav.View.DIFF);
- this.set('_showSettingsView', view === Gerrit.Nav.View.SETTINGS);
- this.set('_showAdminView', view === Gerrit.Nav.View.ADMIN ||
- view === Gerrit.Nav.View.GROUP || view === Gerrit.Nav.View.REPO);
- this.set('_showCLAView', view === Gerrit.Nav.View.AGREEMENTS);
- this.set('_showEditorView', view === Gerrit.Nav.View.EDIT);
- const isPluginScreen = view === Gerrit.Nav.View.PLUGIN_SCREEN;
- this.set('_showPluginScreen', false);
- // Navigation within plugin screens does not restamp gr-endpoint-decorator
- // because _showPluginScreen value does not change. To force restamp,
- // change _showPluginScreen value between true and false.
- if (isPluginScreen) {
- this.async(() => this.set('_showPluginScreen', true), 1);
- }
- this.set('_showDocumentationSearch',
- view === Gerrit.Nav.View.DOCUMENTATION_SEARCH);
- if (this.params.justRegistered) {
- this.$.registrationOverlay.open();
- this.$.registrationDialog.loadData().then(() => {
- this.$.registrationOverlay.refit();
- });
- }
- this.$.header.unfloat();
- },
-
- _handlePageError(e) {
- const props = [
- '_showChangeListView',
- '_showDashboardView',
- '_showChangeView',
- '_showDiffView',
- '_showSettingsView',
- '_showAdminView',
- ];
- for (const showProp of props) {
- this.set(showProp, false);
- }
-
- this.$.errorView.classList.add('show');
- const response = e.detail.response;
- const err = {text: [response.status, response.statusText].join(' ')};
- if (response.status === 404) {
- err.emoji = '¯\\_(ツ)_/¯';
- this._lastError = err;
- } else {
- err.emoji = 'o_O';
- response.text().then(text => {
- err.moreInfo = text;
- this._lastError = err;
- });
- }
- },
-
- _handleLocationChange(e) {
- const hash = e.detail.hash.substring(1);
- let pathname = e.detail.pathname;
- if (pathname.startsWith('/c/') && parseInt(hash, 10) > 0) {
- pathname += '@' + hash;
- }
- this.set('_path', pathname);
- },
-
- _paramsChanged(paramsRecord) {
- const params = paramsRecord.base;
- const viewsToCheck = [Gerrit.Nav.View.SEARCH, Gerrit.Nav.View.DASHBOARD];
- if (viewsToCheck.includes(params.view)) {
- this.set('_lastSearchPage', location.pathname);
- }
- },
-
- _handleTitleChange(e) {
- if (e.detail.title) {
- document.title = e.detail.title + ' · Gerrit Code Review';
- } else {
- document.title = '';
- }
- },
-
- _showKeyboardShortcuts(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
- this.$.keyboardShortcuts.open();
- },
-
- _handleKeyboardShortcutDialogClose() {
- this.$.keyboardShortcuts.close();
- },
-
- _handleAccountDetailUpdate(e) {
- this.$.mainHeader.reload();
- if (this.params.view === Gerrit.Nav.View.SETTINGS) {
- this.$$('gr-settings-view').reloadAccountDetail();
- }
- },
-
- _handleRegistrationDialogClose(e) {
- this.params.justRegistered = false;
- this.$.registrationOverlay.close();
- },
-
- _computeShadowClass(isShadowDom) {
- return isShadowDom ? 'shadow' : '';
- },
-
- _goToUserDashboard() {
- Gerrit.Nav.navigateToUserDashboard();
- },
-
- _goToOpenedChanges() {
- Gerrit.Nav.navigateToStatusSearch('open');
- },
-
- _goToMergedChanges() {
- Gerrit.Nav.navigateToStatusSearch('merged');
- },
-
- _goToAbandonedChanges() {
- Gerrit.Nav.navigateToStatusSearch('abandoned');
- },
-
- _goToWatchedChanges() {
- // The query is hardcoded, and doesn't respect custom menu entries
- Gerrit.Nav.navigateToSearchQuery('is:watched is:open');
- },
-
- _computePluginScreenName({plugin, screen}) {
- return Gerrit._getPluginScreenName(plugin, screen);
- },
-
- _logWelcome() {
- console.group('Runtime Info');
- console.log('Gerrit UI (PolyGerrit)');
- console.log(`Gerrit Server Version: ${this._version}`);
- if (window.VERSION_INFO) {
- console.log(`UI Version Info: ${window.VERSION_INFO}`);
- }
- const renderTime = new Date(window.performance.timing.loadEventStart);
- console.log(`Document loaded at: ${renderTime}`);
- if (this._feedbackUrl) {
- console.log(`Please file bugs and feedback at: ${this._feedbackUrl}`);
- }
- console.groupEnd();
- },
-
- /**
- * Intercept RPC log events emitted by REST API interfaces.
- * Note: the REST API interface cannot use gr-reporting directly because
- * that would create a cyclic dependency.
- */
- _handleRpcLog(e) {
- this.$.reporting.reportRpcTiming(e.detail.anonymizedUrl,
- e.detail.elapsed);
- },
-
- _mobileSearchToggle(e) {
- this.mobileSearch = !this.mobileSearch;
- },
-
});
})();
diff --git a/polygerrit-ui/app/elements/gr-app_test.html b/polygerrit-ui/app/elements/gr-app_test.html
index 71ceab4750..9f1b7f88f8 100644
--- a/polygerrit-ui/app/elements/gr-app_test.html
+++ b/polygerrit-ui/app/elements/gr-app_test.html
@@ -18,11 +18,19 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-app</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../test/common-test-setup.html"/>
-<link rel="import" href="gr-app.html">
+
+<script>
+ const link = document.createElement('link');
+ link.setAttribute('rel', 'import');
+ link.setAttribute('href', 'gr-app.html');
+ document.head.appendChild(link);
+</script>
<script>void(0);</script>
@@ -45,12 +53,18 @@ limitations under the License.
stub('gr-account-dropdown', {
_getTopContent: sinon.stub(),
});
+ stub('gr-router', {
+ start: sandbox.stub(),
+ });
stub('gr-rest-api-interface', {
getAccount() { return Promise.resolve({}); },
getAccountCapabilities() { return Promise.resolve({}); },
getConfig() {
return Promise.resolve({
plugin: {},
+ auth: {
+ auth_type: undefined,
+ },
});
},
getPreferences() { return Promise.resolve({my: []}); },
@@ -68,21 +82,33 @@ limitations under the License.
sandbox.restore();
});
+ appElement = () => {
+ return element.$['app-element'];
+ };
+
test('reporting', () => {
- assert.isTrue(element.$.reporting.appStarted.calledOnce);
+ assert.isTrue(appElement().$.reporting.appStarted.calledOnce);
+ });
+
+ test('reporting called before router start', () => {
+ const element = appElement();
+ const appStartedStub = element.$.reporting.appStarted;
+ const routerStartStub = element.$.router.start;
+ sinon.assert.callOrder(appStartedStub, routerStartStub);
});
test('passes config to gr-plugin-host', () => {
- return element.$.restAPI.getConfig.lastCall.returnValue.then(config => {
- assert.deepEqual(element.$.plugins.config, config);
+ const config = appElement().$.restAPI.getConfig;
+ return config.lastCall.returnValue.then(config => {
+ assert.deepEqual(appElement().$.plugins.config, config);
});
});
test('_paramsChanged sets search page', () => {
- element._paramsChanged({base: {view: Gerrit.Nav.View.CHANGE}});
- assert.notOk(element._lastSearchPage);
- element._paramsChanged({base: {view: Gerrit.Nav.View.SEARCH}});
- assert.ok(element._lastSearchPage);
+ appElement()._paramsChanged({base: {view: Gerrit.Nav.View.CHANGE}});
+ assert.notOk(appElement()._lastSearchPage);
+ appElement()._paramsChanged({base: {view: Gerrit.Nav.View.SEARCH}});
+ assert.ok(appElement()._lastSearchPage);
});
});
</script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html b/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html
index 159f50a603..2537a37d9e 100644
--- a/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-admin-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="gr-admin-api.html">
@@ -37,7 +39,7 @@ limitations under the License.
let plugin;
Gerrit.install(p => { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
- sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
+ Gerrit._loadPlugins([]);
adminApi = plugin.admin();
});
diff --git a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.html b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.html
index 208f1e8d24..ece8677c64 100644
--- a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.html
+++ b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-attribute-helper">
<script src="gr-attribute-helper.js"></script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
index 86238cf96f..0c4149c206 100644
--- a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-attribute-helper</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-attribute-helper.html"/>
@@ -30,7 +32,6 @@ limitations under the License.
<script>
Polymer({
is: 'some-element',
- _legacyUndefinedCheck: true,
properties: {
fooBar: {
type: Object,
diff --git a/polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.html b/polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.html
index eddb52b9e0..dd532e1f8a 100644
--- a/polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.html
+++ b/polygerrit-ui/app/elements/plugins/gr-change-metadata-api/gr-change-metadata-api.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-change-metadata-api">
<script src="gr-change-metadata-api.js"></script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.html b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.html
index 252e8129ea..8b9000fe6b 100644
--- a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.html
+++ b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.html
@@ -15,8 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-dom-hooks">
<script src="gr-dom-hooks.js"></script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
index c6d3e0f764..c0f111aff9 100644
--- a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
+++ b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
@@ -59,7 +59,6 @@
GrDomHook.prototype._createPlaceholder = function(hookName) {
Polymer({
is: hookName,
- _legacyUndefinedCheck: true,
properties: {
plugin: Object,
content: Object,
diff --git a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.html b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.html
index 3dde458220..9e657fa200 100644
--- a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks_test.html
@@ -18,11 +18,14 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-dom-hooks</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-dom-hooks.html"/>
+<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<script>void(0);</script>
@@ -116,7 +119,7 @@ limitations under the License.
hookInternal.handleInstanceAttached(el1);
hookInternal.handleInstanceAttached(el2);
assert.deepEqual([el1, el2], hook.getAllAttached());
- hookI.handleInstanceDetached(el1);
+ hookInternal.handleInstanceDetached(el1);
assert.deepEqual([el2], hook.getAllAttached());
});
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.html b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.html
index 50b80d5139..ab892ac958 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.html
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<dom-module id="gr-endpoint-decorator">
@@ -23,4 +23,4 @@ limitations under the License.
<slot></slot>
</template>
<script src="gr-endpoint-decorator.js"></script>
-</dom-module>
+</dom-module> \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
index f3d6eb4ac0..b38107eb81 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-endpoint-decorator',
- _legacyUndefinedCheck: true,
properties: {
name: String,
@@ -50,9 +49,12 @@
Gerrit._endpoints.onDetachedEndpoint(this.name, this._endpointCallBack);
},
+ /**
+ * @suppress {checkTypes}
+ */
_import(url) {
return new Promise((resolve, reject) => {
- this.importHref(url, resolve, reject);
+ (this.importHref || Polymer.importHref)(url, resolve, reject);
});
},
@@ -74,7 +76,6 @@
},
_getEndpointParams() {
- // Polymer2: querySelectorAll returns NodeList instead of Array.
return Array.from(
Polymer.dom(this).querySelectorAll('gr-endpoint-param'));
},
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html
index 2e9b266f17..b0ad58565d 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-endpoint-decorator</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-endpoint-decorator.html">
<link rel="import" href="../gr-endpoint-param/gr-endpoint-param.html">
@@ -56,7 +58,7 @@ limitations under the License.
stub('gr-endpoint-decorator', {
_import: sandbox.stub().returns(Promise.resolve()),
});
- Gerrit._resetPlugins();
+ Gerrit._testOnly_resetPlugins();
container = fixture('basic');
Gerrit.install(p => plugin = p, '0.1', 'http://some/plugin/url.html');
// Decoration
@@ -65,7 +67,7 @@ limitations under the License.
replacementHook = plugin.registerCustomComponent(
'second', 'other-module', {replace: true});
// Mimic all plugins loaded.
- Gerrit._setPluginsPending([]);
+ Gerrit._loadPlugins([]);
flush(done);
});
@@ -86,7 +88,7 @@ limitations under the License.
test('decoration', () => {
const element =
container.querySelector('gr-endpoint-decorator[name="first"]');
- const modules = Polymer.dom(element.root).children.filter(
+ const modules = Array.from(Polymer.dom(element.root).children).filter(
element => element.nodeName === 'SOME-MODULE');
assert.equal(modules.length, 1);
const [module] = modules;
@@ -103,7 +105,7 @@ limitations under the License.
test('replacement', () => {
const element =
container.querySelector('gr-endpoint-decorator[name="second"]');
- const module = Polymer.dom(element.root).children.find(
+ const module = Array.from(Polymer.dom(element.root).children).find(
element => element.nodeName === 'OTHER-MODULE');
assert.isOk(module);
assert.equal(module['someparam'], 'foofoo');
@@ -120,7 +122,7 @@ limitations under the License.
flush(() => {
const element =
container.querySelector('gr-endpoint-decorator[name="banana"]');
- const module = Polymer.dom(element.root).children.find(
+ const module = Array.from(Polymer.dom(element.root).children).find(
element => element.nodeName === 'NOOB-NOOB');
assert.isOk(module);
done();
@@ -133,10 +135,10 @@ limitations under the License.
flush(() => {
const element =
container.querySelector('gr-endpoint-decorator[name="banana"]');
- const module1 = Polymer.dom(element.root).children.find(
+ const module1 = Array.from(Polymer.dom(element.root).children).find(
element => element.nodeName === 'MOD-ONE');
assert.isOk(module1);
- const module2 = Polymer.dom(element.root).children.find(
+ const module2 = Array.from(Polymer.dom(element.root).children).find(
element => element.nodeName === 'MOD-TWO');
assert.isOk(module2);
done();
@@ -150,14 +152,14 @@ limitations under the License.
param['value'] = undefined;
plugin.registerCustomComponent('banana', 'noob-noob');
flush(() => {
- let module = Polymer.dom(element.root).children.find(
+ let module = Array.from(Polymer.dom(element.root).children).find(
element => element.nodeName === 'NOOB-NOOB');
// Module waits for param to be defined.
assert.isNotOk(module);
const value = {abc: 'def'};
param.value = value;
flush(() => {
- module = Polymer.dom(element.root).children.find(
+ module = Array.from(Polymer.dom(element.root).children).find(
element => element.nodeName === 'NOOB-NOOB');
assert.isOk(module);
assert.strictEqual(module['someParam'], value);
@@ -175,7 +177,7 @@ limitations under the License.
param.value = value1;
plugin.registerCustomComponent('banana', 'noob-noob');
flush(() => {
- const module = Polymer.dom(element.root).children.find(
+ const module = Array.from(Polymer.dom(element.root).children).find(
element => element.nodeName === 'NOOB-NOOB');
assert.strictEqual(module['someParam'], value1);
param.value = value2;
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.html b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.html
index 9d28ac3acf..6a5b558486 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.html
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-endpoint-param">
<script src="gr-endpoint-param.js"></script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
index e21fc7294e..c7a2d9a0a0 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
@@ -19,13 +19,29 @@
Polymer({
is: 'gr-endpoint-param',
- _legacyUndefinedCheck: true,
+
properties: {
name: String,
value: {
type: Object,
notify: true,
+ observer: '_valueChanged',
},
},
+
+ _valueChanged(newValue, oldValue) {
+ /* In polymer 2 the following change was made:
+ "Property change notifications (property-changed events) aren't fired when
+ the value changes as a result of a binding from the host"
+ (see https://polymer-library.polymer-project.org/2.0/docs/about_20).
+ To workaround this problem, we fire the event from the observer.
+ In some cases this fire the event twice, but our code is
+ ready for it.
+ */
+ const detail = {
+ value: newValue,
+ };
+ this.dispatchEvent(new CustomEvent('value-changed', {detail}));
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html
index d34bdef108..15db861fb1 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<dom-module id="gr-event-helper">
<script src="gr-event-helper.js"></script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
index 81b59b6a38..845c1e1794 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
@@ -35,17 +35,35 @@
};
/**
+ * Alias of onClick
+ *
+ * @see onClick
+ */
+ GrEventHelper.prototype.onTap = function(callback) {
+ return this._listen(this.element, callback);
+ };
+
+ /**
* Add a callback to element click or touch.
* The callback may return false to prevent event bubbling.
*
* @param {function(Event):boolean} callback
* @return {function()} Unsubscribe function.
*/
- GrEventHelper.prototype.onTap = function(callback) {
+ GrEventHelper.prototype.onClick = function(callback) {
return this._listen(this.element, callback);
};
/**
+ * Alias of captureClick
+ *
+ * @see captureClick
+ */
+ GrEventHelper.prototype.captureTap = function(callback) {
+ return this._listen(this.element.parentElement, callback, {capture: true});
+ };
+
+ /**
* Add a callback to element click or touch ahead of normal flow.
* Callback is installed on parent during capture phase.
* https://www.w3.org/TR/DOM-Level-3-Events/#event-flow
@@ -54,13 +72,13 @@
* @param {function(Event):boolean} callback
* @return {function()} Unsubscribe function.
*/
- GrEventHelper.prototype.captureTap = function(callback) {
+ GrEventHelper.prototype.captureClick = function(callback) {
return this._listen(this.element.parentElement, callback, {capture: true});
};
GrEventHelper.prototype._listen = function(container, callback, opt_options) {
const capture = opt_options && opt_options.capture;
- const event = opt_options && opt_options.event || 'tap';
+ const event = opt_options && opt_options.event || 'click';
const handler = e => {
if (e.path.indexOf(this.element) !== -1) {
let mayContinue = true;
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
index 47274f6297..bd76bd42e5 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-event-helper</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-event-helper.html"/>
@@ -30,13 +32,17 @@ limitations under the License.
<script>
Polymer({
is: 'some-element',
- _legacyUndefinedCheck: true,
+
properties: {
fooBar: {
type: Object,
notify: true,
},
},
+
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
});
</script>
</dom-element>
@@ -67,14 +73,23 @@ limitations under the License.
instance.onTap(() => {
done();
});
- element.fire('tap');
+ MockInteractions.tap(element);
});
test('onTap() cancel', () => {
const tapStub = sandbox.stub();
- element.parentElement.addEventListener('tap', tapStub);
+ Polymer.Gestures.addListener(element.parentElement, 'tap', tapStub);
instance.onTap(() => false);
- element.fire('tap');
+ MockInteractions.tap(element);
+ flushAsynchronousOperations();
+ assert.isFalse(tapStub.called);
+ });
+
+ test('onClick() cancel', () => {
+ const tapStub = sandbox.stub();
+ element.parentElement.addEventListener('click', tapStub);
+ instance.onTap(() => false);
+ MockInteractions.tap(element);
flushAsynchronousOperations();
assert.isFalse(tapStub.called);
});
@@ -83,14 +98,30 @@ limitations under the License.
instance.captureTap(() => {
done();
});
- element.fire('tap');
+ MockInteractions.tap(element);
+ });
+
+ test('captureClick()', done => {
+ instance.captureClick(() => {
+ done();
+ });
+ MockInteractions.tap(element);
});
test('captureTap() cancels tap()', () => {
const tapStub = sandbox.stub();
- element.addEventListener('tap', tapStub);
+ Polymer.Gestures.addListener(element.parentElement, 'tap', tapStub);
+ instance.captureTap(() => false);
+ MockInteractions.tap(element);
+ flushAsynchronousOperations();
+ assert.isFalse(tapStub.called);
+ });
+
+ test('captureClick() cancels click()', () => {
+ const tapStub = sandbox.stub();
+ element.addEventListener('click', tapStub);
instance.captureTap(() => false);
- element.fire('tap');
+ MockInteractions.tap(element);
flushAsynchronousOperations();
assert.isFalse(tapStub.called);
});
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.html b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.html
index a83b2ab7b1..6a55349b92 100644
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.html
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<dom-module id="gr-external-style">
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
index 7924e27ca6..e90ff303f8 100644
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-external-style',
- _legacyUndefinedCheck: true,
properties: {
name: String,
@@ -33,20 +32,31 @@
},
},
+ /**
+ * @suppress {checkTypes}
+ */
_import(url) {
if (this._urlsImported.includes(url)) { return Promise.resolve(); }
this._urlsImported.push(url);
return new Promise((resolve, reject) => {
- this.importHref(url, resolve, reject);
+ (this.importHref || Polymer.importHref)(url, resolve, reject);
});
},
_applyStyle(name) {
if (this._stylesApplied.includes(name)) { return; }
this._stylesApplied.push(name);
+ // Hybrid custom-style syntax:
+ // https://polymer-library.polymer-project.org/2.0/docs/devguide/style-shadow-dom
const s = document.createElement('style', 'custom-style');
s.setAttribute('include', name);
- Polymer.dom(this.root).appendChild(s);
+ const cs = document.createElement('custom-style');
+ cs.appendChild(s);
+ // When using Shadow DOM <custom-style> must be added to the <body>.
+ // Within <gr-external-style> itself the styles would have no effect.
+ const topEl = document.getElementsByTagName('body')[0];
+ topEl.insertBefore(cs, topEl.firstChild);
+ Polymer.updateStyles();
},
_importAndApply() {
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html
index ec2888d13f..956606774e 100644
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-external-style</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-external-style.html">
diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.html b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.html
index 8e106cc819..f2778991c3 100644
--- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.html
+++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
index 65f2207689..6c66fbfc0e 100644
--- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
+++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-plugin-host',
- _legacyUndefinedCheck: true,
properties: {
config: {
@@ -28,33 +27,28 @@
},
},
- behaviors: [
- Gerrit.BaseUrlBehavior,
- ],
-
_configChanged(config) {
const plugins = config.plugin;
- const htmlPlugins = (plugins.html_resource_paths || [])
- .map(p => this._urlFor(p))
- .filter(p => !Gerrit._isPluginPreloaded(p));
+ const htmlPlugins = (plugins.html_resource_paths || []);
const jsPlugins =
- this._handleMigrations(plugins.js_resource_paths || [], htmlPlugins)
- .map(p => this._urlFor(p))
- .filter(p => !Gerrit._isPluginPreloaded(p));
+ this._handleMigrations(plugins.js_resource_paths || [], htmlPlugins);
const shouldLoadTheme = config.default_theme &&
!Gerrit._isPluginPreloaded('preloaded:gerrit-theme');
- const defaultTheme =
- shouldLoadTheme ? this._urlFor(config.default_theme) : null;
+ const themeToLoad =
+ shouldLoadTheme ? [config.default_theme] : [];
+
+ // Theme should be loaded first if has one to have better UX
const pluginsPending =
- [].concat(jsPlugins, htmlPlugins, defaultTheme || []);
- Gerrit._setPluginsPending(pluginsPending);
- if (defaultTheme) {
- // Make theme first to be first to load.
- // Load sync to work around rare theme loading race condition.
- this._importHtmlPlugins([defaultTheme], true);
+ themeToLoad.concat(jsPlugins, htmlPlugins);
+
+ const pluginOpts = {};
+
+ if (shouldLoadTheme) {
+ // Theme needs to be loaded synchronous.
+ pluginOpts[config.default_theme] = {sync: true};
}
- this._loadJsPlugins(jsPlugins);
- this._importHtmlPlugins(htmlPlugins);
+
+ Gerrit._loadPlugins(pluginsPending, pluginOpts);
},
/**
@@ -67,53 +61,5 @@
return !htmlPlugins.includes(counterpart);
});
},
-
- /**
- * @suppress {checkTypes}
- * States that it expects no more than 3 parameters, but that's not true.
- * @todo (beckysiegel) check Polymer annotations and submit change.
- * @param {Array} plugins
- * @param {boolean=} opt_sync
- */
- _importHtmlPlugins(plugins, opt_sync) {
- const async = !opt_sync;
- for (const url of plugins) {
- // onload (second param) needs to be a function. When null or undefined
- // were passed, plugins were not loaded correctly.
- this.importHref(
- this._urlFor(url), () => {},
- Gerrit._pluginInstallError.bind(null, `${url} import error`),
- async);
- }
- },
-
- _loadJsPlugins(plugins) {
- for (const url of plugins) {
- this._createScriptTag(this._urlFor(url));
- }
- },
-
- _createScriptTag(url) {
- const el = document.createElement('script');
- el.defer = true;
- el.src = url;
- el.onerror = Gerrit._pluginInstallError.bind(null, `${url} load error`);
- return document.body.appendChild(el);
- },
-
- _urlFor(pathOrUrl) {
- if (!pathOrUrl) {
- return pathOrUrl;
- }
- if (pathOrUrl.startsWith('preloaded:') ||
- pathOrUrl.startsWith('http')) {
- // Plugins are loaded from another domain or preloaded.
- return pathOrUrl;
- }
- if (!pathOrUrl.startsWith('/')) {
- pathOrUrl = '/' + pathOrUrl;
- }
- return window.location.origin + this.getBaseUrl() + pathOrUrl;
- },
});
})();
diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html
index 9901d9fe51..3a8e4d8cf5 100644
--- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-plugin-host</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-plugin-host.html">
@@ -36,195 +38,57 @@ limitations under the License.
suite('gr-plugin-host tests', () => {
let element;
let sandbox;
- let url;
setup(() => {
element = fixture('basic');
sandbox = sinon.sandbox.create();
sandbox.stub(document.body, 'appendChild');
sandbox.stub(element, 'importHref');
- url = window.location.origin;
});
teardown(() => {
sandbox.restore();
});
- test('counts plugins', () => {
- sandbox.stub(Gerrit, '_setPluginsCount');
+ test('load plugins should be called', () => {
+ sandbox.stub(Gerrit, '_loadPlugins');
element.config = {
plugin: {
html_resource_paths: ['plugins/foo/bar', 'plugins/baz'],
js_resource_paths: ['plugins/42'],
},
};
- assert.isTrue(Gerrit._setPluginsCount.calledWith(3));
+ assert.isTrue(Gerrit._loadPlugins.calledOnce);
+ assert.isTrue(Gerrit._loadPlugins.calledWith([
+ 'plugins/42', 'plugins/foo/bar', 'plugins/baz',
+ ], {}));
});
- test('imports relative html plugins from config', () => {
- sandbox.stub(Gerrit, '_pluginInstallError');
- element.config = {
- plugin: {html_resource_paths: ['foo/bar', 'baz']},
- };
- assert.equal(element.importHref.firstCall.args[0], url + '/foo/bar');
- assert.isTrue(element.importHref.firstCall.args[3]);
-
- assert.equal(element.importHref.secondCall.args[0], url + '/baz');
- assert.isTrue(element.importHref.secondCall.args[3]);
-
- assert.equal(Gerrit._pluginInstallError.callCount, 0);
- element.importHref.firstCall.args[2]();
- assert.equal(Gerrit._pluginInstallError.callCount, 1);
- element.importHref.secondCall.args[2]();
- assert.equal(Gerrit._pluginInstallError.callCount, 2);
- });
-
- test('imports relative html plugins from config with a base url', () => {
- sandbox.stub(Gerrit, '_pluginInstallError');
- sandbox.stub(element, 'getBaseUrl').returns('/the-base');
- element.config = {
- plugin: {html_resource_paths: ['foo/bar', 'baz']}};
- assert.equal(element.importHref.firstCall.args[0],
- url + '/the-base/foo/bar');
- assert.isTrue(element.importHref.firstCall.args[3]);
-
- assert.equal(element.importHref.secondCall.args[0],
- url + '/the-base/baz');
- assert.isTrue(element.importHref.secondCall.args[3]);
- assert.equal(Gerrit._pluginInstallError.callCount, 0);
- element.importHref.firstCall.args[2]();
- assert.equal(Gerrit._pluginInstallError.callCount, 1);
- element.importHref.secondCall.args[2]();
- assert.equal(Gerrit._pluginInstallError.callCount, 2);
- });
-
- test('importHref is not called with null callback functions', () => {
- const plugins = ['path/to/plugin'];
- element._importHtmlPlugins(plugins);
- assert.isTrue(element.importHref.calledOnce);
- assert.isFunction(element.importHref.lastCall.args[1]);
- assert.isFunction(element.importHref.lastCall.args[2]);
- });
-
- test('imports absolute html plugins from config', () => {
- sandbox.stub(Gerrit, '_pluginInstallError');
- element.config = {
- plugin: {
- html_resource_paths: [
- 'http://example.com/foo/bar',
- 'https://example.com/baz',
- ],
- },
- };
- assert.equal(element.importHref.firstCall.args[0],
- 'http://example.com/foo/bar');
- assert.isTrue(element.importHref.firstCall.args[3]);
-
- assert.equal(element.importHref.secondCall.args[0],
- 'https://example.com/baz');
- assert.isTrue(element.importHref.secondCall.args[3]);
- assert.equal(Gerrit._pluginInstallError.callCount, 0);
- element.importHref.firstCall.args[2]();
- assert.equal(Gerrit._pluginInstallError.callCount, 1);
- element.importHref.secondCall.args[2]();
- assert.equal(Gerrit._pluginInstallError.callCount, 2);
- });
-
- test('adds js plugins from config to the body', () => {
- element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}};
- assert.isTrue(document.body.appendChild.calledTwice);
- });
-
- test('imports relative js plugins from config', () => {
- sandbox.stub(element, '_createScriptTag');
- element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}};
- assert.isTrue(element._createScriptTag.calledWith(url + '/foo/bar'));
- assert.isTrue(element._createScriptTag.calledWith(url + '/baz'));
- });
-
- test('imports relative html plugins from config with a base url', () => {
- sandbox.stub(element, '_createScriptTag');
- sandbox.stub(element, 'getBaseUrl').returns('/the-base');
- element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}};
- assert.isTrue(element._createScriptTag.calledWith(
- url + '/the-base/foo/bar'));
- assert.isTrue(element._createScriptTag.calledWith(
- url + '/the-base/baz'));
- });
-
- test('imports absolute html plugins from config', () => {
- sandbox.stub(element, '_createScriptTag');
+ test('theme plugins should be loaded if enabled', () => {
+ sandbox.stub(Gerrit, '_loadPlugins');
element.config = {
+ default_theme: 'gerrit-theme.html',
plugin: {
- js_resource_paths: [
- 'http://example.com/foo/bar',
- 'https://example.com/baz',
- ],
- },
- };
- assert.isTrue(element._createScriptTag.calledWith(
- 'http://example.com/foo/bar'));
- assert.isTrue(element._createScriptTag.calledWith(
- 'https://example.com/baz'));
- });
-
- test('default theme is loaded with html plugins', () => {
- sandbox.stub(Gerrit, '_pluginInstallError');
- element.config = {
- default_theme: '/oof',
- plugin: {
- html_resource_paths: ['some'],
+ html_resource_paths: ['plugins/foo/bar', 'plugins/baz'],
+ js_resource_paths: ['plugins/42'],
},
};
- assert.equal(element.importHref.firstCall.args[0], url + '/oof');
- assert.isFalse(element.importHref.firstCall.args[3]);
-
- assert.equal(element.importHref.secondCall.args[0], url + '/some');
- assert.isTrue(element.importHref.secondCall.args[3]);
- assert.equal(Gerrit._pluginInstallError.callCount, 0);
- element.importHref.firstCall.args[2]();
- assert.equal(Gerrit._pluginInstallError.callCount, 1);
- element.importHref.secondCall.args[2]();
- assert.equal(Gerrit._pluginInstallError.callCount, 2);
+ assert.isTrue(Gerrit._loadPlugins.calledOnce);
+ assert.isTrue(Gerrit._loadPlugins.calledWith([
+ 'gerrit-theme.html', 'plugins/42', 'plugins/foo/bar', 'plugins/baz',
+ ], {'gerrit-theme.html': {sync: true}}));
});
- test('default theme is loaded with html plugins', () => {
- sandbox.stub(Gerrit, '_setPluginsPending');
- element.config = {
- default_theme: '/oof',
- plugin: {},
- };
- assert.isTrue(Gerrit._setPluginsPending.calledWith([url + '/oof']));
- });
-
- test('skips default theme loading if preloaded', () => {
+ test('skip theme if preloaded', () => {
sandbox.stub(Gerrit, '_isPluginPreloaded')
.withArgs('preloaded:gerrit-theme').returns(true);
- sandbox.stub(Gerrit, '_setPluginsPending');
+ sandbox.stub(Gerrit, '_loadPlugins');
element.config = {
default_theme: '/oof',
plugin: {},
};
- assert.isFalse(element.importHref.calledWith(url + '/oof'));
- });
-
- test('skips preloaded plugins', () => {
- sandbox.stub(Gerrit, '_isPluginPreloaded')
- .withArgs(url + '/plugins/foo/bar').returns(true)
- .withArgs(url + '/plugins/42').returns(true);
- sandbox.stub(Gerrit, '_setPluginsCount');
- sandbox.stub(Gerrit, '_setPluginsPending');
- sandbox.stub(element, '_createScriptTag');
- element.config = {
- plugin: {
- html_resource_paths: ['plugins/foo/bar', 'plugins/baz'],
- js_resource_paths: ['plugins/42'],
- },
- };
- assert.isTrue(
- Gerrit._setPluginsPending.calledWith([url + '/plugins/baz']));
- assert.equal(element._createScriptTag.callCount, 0);
- assert.isTrue(element.importHref.calledWith(url + '/plugins/baz'));
+ assert.isTrue(Gerrit._loadPlugins.calledOnce);
+ assert.isTrue(Gerrit._loadPlugins.calledWith([], {}));
});
});
</script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.html
index ce0bf1bc18..402d988d21 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.html
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<dom-module id="gr-plugin-popup">
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
index 3ef93e4467..2e7a2b7f27 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
@@ -16,15 +16,18 @@
*/
(function(window) {
'use strict';
+
Polymer({
is: 'gr-plugin-popup',
- _legacyUndefinedCheck: true,
+
get opened() {
return this.$.overlay.opened;
},
+
open() {
return this.$.overlay.open();
},
+
close() {
this.$.overlay.close();
},
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html
index 91386b9717..1f1e81eaac 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-plugin-popup</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-plugin-popup.html"/>
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.html
index 2fdf28cee1..26ece3094f 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.html
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="gr-plugin-popup.html">
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html
index 983c795f3a..53370e29b8 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-popup-interface</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-popup-interface.html"/>
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
index c9486aee6a..593c1e0e74 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../admin/gr-repo-command/gr-repo-command.html">
<dom-module id="gr-plugin-repo-command">
@@ -25,7 +25,6 @@ limitations under the License.
<script>
Polymer({
is: 'gr-plugin-repo-command',
- _legacyUndefinedCheck: true,
properties: {
title: String,
repoName: String,
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.html b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.html
index 34c9797e13..8e6c053bbb 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.html
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="gr-plugin-repo-command.html">
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html
index bb9ae87e0b..0b32f8a701 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../gr-endpoint-decorator/gr-endpoint-decorator.html">
<link rel="import" href="gr-repo-api.html">
@@ -44,7 +46,7 @@ limitations under the License.
let plugin;
Gerrit.install(p => { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
- sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
+ Gerrit._loadPlugins([]);
repoApi = plugin.project();
});
diff --git a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.html b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.html
index 7c916dc370..20cc71be92 100644
--- a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.html
+++ b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../settings/gr-settings-view/gr-settings-item.html">
<link rel="import" href="../../settings/gr-settings-view/gr-settings-menu-item.html">
diff --git a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html
index cabd26b3f7..cbc2de622f 100644
--- a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-settings-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../gr-endpoint-decorator/gr-endpoint-decorator.html">
<link rel="import" href="gr-settings-api.html">
@@ -46,7 +48,7 @@ limitations under the License.
let plugin;
Gerrit.install(p => { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
- sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
+ Gerrit._loadPlugins([]);
settingsApi = plugin.settings();
});
diff --git a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.html b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.html
new file mode 100644
index 0000000000..74b87c8241
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.html
@@ -0,0 +1,18 @@
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<script src="gr-styles-api.js"></script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js
new file mode 100644
index 0000000000..d5647eae9a
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js
@@ -0,0 +1,83 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function(window) {
+ 'use strict';
+
+ // Prevent redefinition.
+ if (window.GrStylesApi) { return; }
+
+ let styleObjectCount = 0;
+
+ function GrStyleObject(rulesStr) {
+ this._rulesStr = rulesStr;
+ this._className = `__pg_js_api_class_${styleObjectCount}`;
+ styleObjectCount++;
+ }
+
+ /**
+ * Creates a new unique CSS class and injects it in a root node of the element
+ * if it hasn't been added yet. A root node is an document or is the
+ * associated shadowRoot. This class can be added to any element with the same
+ * root node.
+ *
+ * @param {HTMLElement} element The element to get class name for.
+ * @return {string} Appropriate class name for the element is returned
+ */
+ GrStyleObject.prototype.getClassName = function(element) {
+ let rootNode = Polymer.Settings.useShadow
+ ? element.getRootNode() : document.body;
+ if (rootNode === document) {
+ rootNode = document.head;
+ }
+ if (!rootNode.__pg_js_api_style_tags) {
+ rootNode.__pg_js_api_style_tags = {};
+ }
+ if (!rootNode.__pg_js_api_style_tags[this._className]) {
+ const styleTag = document.createElement('style');
+ styleTag.innerHTML = `.${this._className} { ${this._rulesStr} }`;
+ rootNode.appendChild(styleTag);
+ rootNode.__pg_js_api_style_tags[this._className] = true;
+ }
+ return this._className;
+ };
+
+ /**
+ * Apply shared style to the element.
+ *
+ * @param {HTMLElement} element The element to apply style for
+ */
+ GrStyleObject.prototype.apply = function(element) {
+ element.classList.add(this.getClassName(element));
+ };
+
+
+ function GrStylesApi() {
+ }
+
+ /**
+ * Creates a new GrStyleObject with specified style properties.
+ *
+ * @param {string} String with style properties.
+ * @return {GrStyleObject}
+ */
+ GrStylesApi.prototype.css = function(ruleStr) {
+ return new GrStyleObject(ruleStr);
+ };
+
+
+ window.GrStylesApi = GrStylesApi;
+})(window);
diff --git a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.html b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.html
new file mode 100644
index 0000000000..46bda6db9d
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.html
@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-admin-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
+<link rel="import" href="gr-styles-api.html">
+
+<script>void(0);</script>
+
+<dom-module id="gr-style-test-element">
+ <template>
+ <div id="wrapper"></div>
+ </template>
+ <script>Polymer({is: 'gr-style-test-element'});</script>
+</dom-module>
+
+<script>
+ suite('gr-styles-api tests', () => {
+ let sandbox;
+ let stylesApi;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ let plugin;
+ Gerrit.install(p => { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+ Gerrit._loadPlugins([]);
+ stylesApi = plugin.styles();
+ });
+
+ teardown(() => {
+ stylesApi = null;
+ sandbox.restore();
+ });
+
+ test('exists', () => {
+ assert.isOk(stylesApi);
+ });
+
+ test('css', () => {
+ const styleObject = stylesApi.css('background: red');
+ assert.isDefined(styleObject);
+ });
+ });
+
+ suite('GrStyleObject tests', () => {
+ let sandbox;
+ let stylesApi;
+ let displayInlineStyle;
+ let displayNoneStyle;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ let plugin;
+ Gerrit.install(p => { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+ Gerrit._loadPlugins([]);
+ stylesApi = plugin.styles();
+ displayInlineStyle = stylesApi.css('display: inline');
+ displayNoneStyle = stylesApi.css('display: none');
+ });
+
+ teardown(() => {
+ displayInlineStyle = null;
+ displayNoneStyle = null;
+ stylesApi = null;
+ sandbox.restore();
+ });
+
+ function createNestedElements(parentElement) {
+ /* parentElement
+ * |--- element1
+ * |--- element2
+ * |--- element3
+ **/
+ const element1 = document.createElement('div');
+ const element2 = document.createElement('div');
+ const element3 = document.createElement('div');
+ Polymer.dom(parentElement).appendChild(element1);
+ Polymer.dom(parentElement).appendChild(element2);
+ Polymer.dom(element2).appendChild(element3);
+
+ return [element1, element2, element3];
+ }
+
+
+ test('getClassName - body level elements', () => {
+ const bodyLevelElements = createNestedElements(document.body);
+
+ testGetClassName(bodyLevelElements);
+ });
+
+ test('getClassName - elements inside polymer element', () => {
+ const polymerElement = document.createElement('gr-style-test-element');
+ Polymer.dom(document.body).appendChild(polymerElement);
+ const contentElements = createNestedElements(polymerElement.$.wrapper);
+
+ testGetClassName(contentElements);
+ });
+
+ function testGetClassName(elements) {
+ assertAllElementsHaveDefaultStyle(elements);
+
+ const className1 = displayInlineStyle.getClassName(elements[0]);
+ const className2 = displayNoneStyle.getClassName(elements[1]);
+ const className3 = displayInlineStyle.getClassName(elements[2]);
+
+ assert.notEqual(className2, className1);
+ assert.equal(className3, className1);
+
+ assertAllElementsHaveDefaultStyle(elements);
+
+ elements[0].classList.add(className1);
+ elements[1].classList.add(className2);
+ elements[2].classList.add(className1);
+
+ assertDisplayPropertyValues(elements, ['inline', 'none', 'inline']);
+ }
+
+ test('apply - body level elements', () => {
+ const bodyLevelElements = createNestedElements(document.body);
+
+ testApply(bodyLevelElements);
+ });
+
+ test('apply - elements inside polymer element', () => {
+ const polymerElement = document.createElement('gr-style-test-element');
+ Polymer.dom(document.body).appendChild(polymerElement);
+ const contentElements = createNestedElements(polymerElement.$.wrapper);
+
+ testApply(contentElements);
+ });
+
+ function testApply(elements) {
+ assertAllElementsHaveDefaultStyle(elements);
+ displayInlineStyle.apply(elements[0]);
+ displayNoneStyle.apply(elements[1]);
+ displayInlineStyle.apply(elements[2]);
+ assertDisplayPropertyValues(elements, ['inline', 'none', 'inline']);
+ }
+
+
+ function assertAllElementsHaveDefaultStyle(elements) {
+ for (const element of elements) {
+ assert.equal(getComputedStyle(element).getPropertyValue('display'),
+ 'block');
+ }
+ }
+
+ function assertDisplayPropertyValues(elements, expectedDisplayValues) {
+ for (const key in elements) {
+ if (elements.hasOwnProperty(key)) {
+ assert.equal(
+ getComputedStyle(elements[key]).getPropertyValue('display'),
+ expectedDisplayValues[key]);
+ }
+ }
+ }
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
index 496d0e71df..f0eacd276d 100644
--- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
+++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-custom-plugin-header">
<template>
@@ -26,7 +26,7 @@ limitations under the License.
vertical-align: middle;
}
.title {
- margin-left: .25em;
+ margin-left: var(--spacing-xs);
}
</style>
<span>
@@ -37,7 +37,6 @@ limitations under the License.
<script>
Polymer({
is: 'gr-custom-plugin-header',
- _legacyUndefinedCheck: true,
properties: {
logoUrl: String,
title: String,
diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.html b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.html
index b84f5b9b27..d6e67fe337 100644
--- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.html
+++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="gr-custom-plugin-header.html">
diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html
index 8d23ea2ccf..6332b9196e 100644
--- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-theme-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../gr-endpoint-decorator/gr-endpoint-decorator.html">
<link rel="import" href="gr-theme-api.html">
@@ -65,7 +67,7 @@ limitations under the License.
stub('gr-custom-plugin-header', {
ready() { customHeader = this; },
});
- Gerrit._setPluginsPending([]);
+ Gerrit._loadPlugins([]);
});
test('sets logo and title', done => {
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
index f534771228..662c6f1d76 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
@@ -15,8 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../shared/gr-avatar/gr-avatar.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -31,10 +33,10 @@ limitations under the License.
gr-avatar {
height: 120px;
width: 120px;
- margin-right: .15em;
+ margin-right: var(--spacing-xs);
vertical-align: -.25em;
}
- .hide {
+ div section.hide {
display: none;
}
</style>
@@ -79,12 +81,17 @@ limitations under the License.
<span
hidden$="[[!usernameMutable]]"
class="value">
- <input
- is="iron-input"
- id="usernameInput"
+ <iron-input
disabled="[[_saving]]"
on-keydown="_handleKeydown"
bind-value="{{_username}}">
+ <input
+ is="iron-input"
+ id="usernameInput"
+ disabled="[[_saving]]"
+ on-keydown="_handleKeydown"
+ bind-value="{{_username}}">
+ </iron-input>
</span>
</section>
<section id="nameSection">
@@ -95,24 +102,34 @@ limitations under the License.
<span
hidden$="[[!nameMutable]]"
class="value">
- <input
- is="iron-input"
- id="nameInput"
+ <iron-input
disabled="[[_saving]]"
on-keydown="_handleKeydown"
bind-value="{{_account.name}}">
+ <input
+ is="iron-input"
+ id="nameInput"
+ disabled="[[_saving]]"
+ on-keydown="_handleKeydown"
+ bind-value="{{_account.name}}">
+ </iron-input>
</span>
</section>
<section>
<span class="title">Status (e.g. "Vacation")</span>
<span class="value">
- <input
- is="iron-input"
- id="statusInput"
+ <iron-input
disabled="[[_saving]]"
on-keydown="_handleKeydown"
bind-value="{{_account.status}}">
- </span>
+ <input
+ is="iron-input"
+ id="statusInput"
+ disabled="[[_saving]]"
+ on-keydown="_handleKeydown"
+ bind-value="{{_account.status}}">
+ </iron-input>
+ </span>
</section>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
index 51a22fc32d..3ba3a80a76 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-account-info',
- _legacyUndefinedCheck: true,
/**
* Fired when account details are changed.
@@ -69,6 +68,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
observers: [
'_nameChanged(_account.name)',
'_statusChanged(_account.status)',
@@ -145,6 +148,14 @@
},
_computeUsernameMutable(config, username) {
+ // Polymer 2: check for undefined
+ if ([
+ config,
+ username,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
// Username may not be changed once it is set.
return config.auth.editable_account_fields.includes('USER_NAME') &&
!username;
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
index 75b9910772..a35c1f05e8 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-account-info</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-account-info.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html
index 72ea503376..852161cb2a 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.html
@@ -16,7 +16,7 @@ limitations under the License.
-->
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
index fe36a86014..41595a9832 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-agreements-list',
- _legacyUndefinedCheck: true,
properties: {
_agreements: Array,
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html
index 56122a99d4..14cf97c942 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-settings-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-agreements-list.html">
@@ -45,7 +47,7 @@ limitations under the License.
}];
stub('gr-rest-api-interface', {
- getAccountGroups() { return Promise.resolve(agreements); },
+ getAccountAgreements() { return Promise.resolve(agreements); },
});
element = fixture('basic');
@@ -56,10 +58,10 @@ limitations under the License.
test('renders', () => {
const rows = Polymer.dom(element.root).querySelectorAll('tbody tr');
- assert.equal(rows.length, 3);
+ assert.equal(rows.length, 1);
- const nameCells = rows.map(row =>
- row.querySelectorAll('td')[0].textContent
+ const nameCells = Array.from(rows).map(row =>
+ row.querySelectorAll('td')[0].textContent.trim()
);
assert.equal(nameCells[0], 'Agreements 1');
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.html b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.html
index 4f695131db..88a53ee171 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.html
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.html
@@ -15,8 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="import" href="../../../behaviors/gr-change-table-behavior/gr-change-table-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -56,11 +55,11 @@ limitations under the License.
<tr>
<td>Number</td>
<td class="checkboxContainer"
- on-tap="_handleCheckboxContainerTap">
+ on-click="_handleCheckboxContainerClick">
<input
type="checkbox"
name="number"
- on-tap="_handleNumberCheckboxTap"
+ on-click="_handleNumberCheckboxClick"
checked$="[[showNumber]]">
</td>
</tr>
@@ -68,11 +67,11 @@ limitations under the License.
<tr>
<td>[[item]]</td>
<td class="checkboxContainer"
- on-tap="_handleCheckboxContainerTap">
+ on-click="_handleCheckboxContainerClick">
<input
type="checkbox"
name="[[item]]"
- on-tap="_handleTargetTap"
+ on-click="_handleTargetClick"
checked$="[[!isColumnHidden(item, displayedColumns)]]">
</td>
</tr>
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
index d660ee58bb..0fef3d625c 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-change-table-editor',
- _legacyUndefinedCheck: true,
properties: {
displayedColumns: {
@@ -43,7 +42,6 @@
* @return {!Array<string>}
*/
_getDisplayedColumns() {
- // Polymer2: querySelectorAll returns NodeList instead of Array.
return Array.from(Polymer.dom(this.root)
.querySelectorAll('.checkboxContainer input:not([name=number])'))
.filter(checkbox => checkbox.checked)
@@ -51,28 +49,28 @@
},
/**
- * Handle a tap on a checkbox container and relay the tap to the checkbox it
+ * Handle a click on a checkbox container and relay the click to the checkbox it
* contains.
*/
- _handleCheckboxContainerTap(e) {
+ _handleCheckboxContainerClick(e) {
const checkbox = Polymer.dom(e.target).querySelector('input');
if (!checkbox) { return; }
checkbox.click();
},
/**
- * Handle a tap on the number checkbox and update the showNumber property
+ * Handle a click on the number checkbox and update the showNumber property
* accordingly.
*/
- _handleNumberCheckboxTap(e) {
+ _handleNumberCheckboxClick(e) {
this.showNumber = Polymer.dom(e).rootTarget.checked;
},
/**
- * Handle a tap on a displayed column checkboxes (excluding number) and
+ * Handle a click on a displayed column checkboxes (excluding number) and
* update the displayedColumns property accordingly.
*/
- _handleTargetTap(e) {
+ _handleTargetClick(e) {
this.set('displayedColumns', this._getDisplayedColumns());
},
});
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
index 32fab9d364..29a70818d8 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-settings-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-change-table-editor.html">
@@ -117,41 +119,41 @@ limitations under the License.
columns.filter(c => c !== 'Assignee'));
});
- test('_handleCheckboxContainerTap relayes taps to checkboxes', () => {
- sandbox.stub(element, '_handleNumberCheckboxTap');
- sandbox.stub(element, '_handleTargetTap');
+ test('_handleCheckboxContainerClick relayes taps to checkboxes', () => {
+ sandbox.stub(element, '_handleNumberCheckboxClick');
+ sandbox.stub(element, '_handleTargetClick');
MockInteractions.tap(
element.$$('table tr:first-of-type .checkboxContainer'));
- assert.isTrue(element._handleNumberCheckboxTap.calledOnce);
- assert.isFalse(element._handleTargetTap.called);
+ assert.isTrue(element._handleNumberCheckboxClick.calledOnce);
+ assert.isFalse(element._handleTargetClick.called);
MockInteractions.tap(
element.$$('table tr:last-of-type .checkboxContainer'));
- assert.isTrue(element._handleNumberCheckboxTap.calledOnce);
- assert.isTrue(element._handleTargetTap.calledOnce);
+ assert.isTrue(element._handleNumberCheckboxClick.calledOnce);
+ assert.isTrue(element._handleTargetClick.calledOnce);
});
- test('_handleNumberCheckboxTap', () => {
- sandbox.spy(element, '_handleNumberCheckboxTap');
+ test('_handleNumberCheckboxClick', () => {
+ sandbox.spy(element, '_handleNumberCheckboxClick');
MockInteractions
.tap(element.$$('.checkboxContainer input[name=number]'));
- assert.isTrue(element._handleNumberCheckboxTap.calledOnce);
+ assert.isTrue(element._handleNumberCheckboxClick.calledOnce);
assert.isTrue(element.showNumber);
MockInteractions
.tap(element.$$('.checkboxContainer input[name=number]'));
- assert.isTrue(element._handleNumberCheckboxTap.calledTwice);
+ assert.isTrue(element._handleNumberCheckboxClick.calledTwice);
assert.isFalse(element.showNumber);
});
- test('_handleTargetTap', () => {
- sandbox.spy(element, '_handleTargetTap');
+ test('_handleTargetClick', () => {
+ sandbox.spy(element, '_handleTargetClick');
assert.include(element.displayedColumns, 'Assignee');
MockInteractions
.tap(element.$$('.checkboxContainer input[name=Assignee]'));
- assert.isTrue(element._handleTargetTap.calledOnce);
+ assert.isTrue(element._handleTargetClick.calledOnce);
assert.notInclude(element.displayedColumns, 'Assignee');
});
});
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
index 4a939e6433..c29153e7d6 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
@@ -16,8 +16,9 @@ limitations under the License.
-->
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -27,17 +28,17 @@ limitations under the License.
<template>
<style include="shared-styles">
h1 {
- margin-bottom: .6em;
+ margin-bottom: var(--spacing-m);
}
h3 {
- margin-bottom: .5em;
+ margin-bottom: var(--spacing-m);
}
.agreementsUrl {
- border: 0.1em solid #b0bdcc;
- margin-bottom: 1.25em;
- margin-left: 1.25em;
- margin-right: 1.25em;
- padding: 0.3em;
+ border: 1px solid #b0bdcc;
+ margin-bottom: var(--spacing-xl);
+ margin-left: var(--spacing-xl);
+ margin-right: var(--spacing-xl);
+ padding: var(--spacing-s);
}
#claNewAgreementsLabel {
font-weight: var(--font-weight-bold);
@@ -53,15 +54,15 @@ limitations under the License.
}
.alreadySubmittedText {
color: var(--error-text-color);
- margin: 0 2em;
- padding: .5em;
+ margin: 0 var(--spacing-xxl);
+ padding: var(--spacing-m);
}
.alreadySubmittedText.hide,
.hideAgreementsTextBox {
display: none;
}
main {
- margin: 2em auto;
+ margin: var(--spacing-xxl) auto;
max-width: 50em;
}
</style>
@@ -76,7 +77,7 @@ limitations under the License.
type="radio"
data-name$="[[item.name]]"
data-url$="[[item.url]]"
- on-tap="_handleShowAgreement"
+ on-click="_handleShowAgreement"
disabled$="[[_disableAgreements(item, _groups, _signedAgreements)]]">
<label id="claNewAgreementsLabel">[[item.name]]</label>
</span>
@@ -95,8 +96,14 @@ limitations under the License.
</div>
<div class$="agreementsTextBox [[_computeHideAgreementClass(_agreementName, _serverConfig.auth.contributor_agreements)]]">
<h3 class="smallHeading">Complete the agreement:</h3>
- <input id="input-agreements" is="iron-input" bind-value="{{_agreementsText}}" placeholder="Enter 'I agree' here" />
- <gr-button on-tap="_handleSaveAgreements" disabled="[[_disableAgreementsText(_agreementsText)]]">
+ <iron-input bind-value="{{_agreementsText}}"
+ placeholder="Enter 'I agree' here">
+ <input id="input-agreements"
+ is="iron-input"
+ bind-value="{{_agreementsText}}"
+ placeholder="Enter 'I agree' here">
+ </iron-input>
+ <gr-button on-click="_handleSaveAgreements" disabled="[[_disableAgreementsText(_agreementsText)]]">
Submit
</gr-button>
</div>
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
index 2a931558a0..cfd7b21ab5 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-cla-view',
- _legacyUndefinedCheck: true,
properties: {
_groups: Object,
@@ -37,6 +36,7 @@
behaviors: [
Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
],
attached() {
@@ -66,7 +66,9 @@
_getAgreementsUrl(configUrl) {
let url;
- if (!configUrl) { return ''; }
+ if (!configUrl) {
+ return '';
+ }
if (configUrl.startsWith('http:') || configUrl.startsWith('https:')) {
url = configUrl;
} else {
@@ -100,8 +102,8 @@
},
_createToast(message) {
- this.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message}, bubbles: true}));
+ this.dispatchEvent(new CustomEvent(
+ 'show-alert', {detail: {message}, bubbles: true, composed: true}));
},
_computeShowAgreementsClass(agreements) {
@@ -135,9 +137,13 @@
_computeHideAgreementClass(name, config) {
if (!config) return '';
for (const key in config) {
- if (!config.hasOwnProperty(key)) { continue; }
+ if (!config.hasOwnProperty(key)) {
+ continue;
+ }
for (const prop in config[key]) {
- if (!config[key].hasOwnProperty(prop)) { continue; }
+ if (!config[key].hasOwnProperty(prop)) {
+ continue;
+ }
if (name === config[key].name &&
!config[key].auto_verify_group) {
return 'hideAgreementsTextBox';
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html
index aec52cb8f2..f1b65d97ba 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-cla-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-cla-view.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.html b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.html
index b3e6990572..53a30c3426 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.html
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -29,40 +30,64 @@ limitations under the License.
<section>
<span class="title">Tab width</span>
<span class="value">
- <input
- is="iron-input"
+ <iron-input
type="number"
prevent-invalid-input
allowed-pattern="[0-9]"
bind-value="{{editPrefs.tab_size}}"
on-keypress="_handleEditPrefsChanged"
on-change="_handleEditPrefsChanged">
+ <input
+ is="iron-input"
+ type="number"
+ prevent-invalid-input
+ allowed-pattern="[0-9]"
+ bind-value="{{editPrefs.tab_size}}"
+ on-keypress="_handleEditPrefsChanged"
+ on-change="_handleEditPrefsChanged">
+ </iron-input>
</span>
</section>
<section>
<span class="title">Columns</span>
<span class="value">
- <input
- is="iron-input"
+ <iron-input
type="number"
prevent-invalid-input
allowed-pattern="[0-9]"
bind-value="{{editPrefs.line_length}}"
on-keypress="_handleEditPrefsChanged"
on-change="_handleEditPrefsChanged">
+ <input
+ is="iron-input"
+ type="number"
+ prevent-invalid-input
+ allowed-pattern="[0-9]"
+ bind-value="{{editPrefs.line_length}}"
+ on-keypress="_handleEditPrefsChanged"
+ on-change="_handleEditPrefsChanged">
+ </iron-input>
</span>
</section>
<section>
<span class="title">Indent unit</span>
<span class="value">
- <input
- is="iron-input"
+ <iron-input
type="number"
prevent-invalid-input
allowed-pattern="[0-9]"
bind-value="{{editPrefs.indent_unit}}"
on-keypress="_handleEditPrefsChanged"
on-change="_handleEditPrefsChanged">
+ <input
+ is="iron-input"
+ type="number"
+ prevent-invalid-input
+ allowed-pattern="[0-9]"
+ bind-value="{{editPrefs.indent_unit}}"
+ on-keypress="_handleEditPrefsChanged"
+ on-change="_handleEditPrefsChanged">
+ </iron-input>
</span>
</section>
<section>
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
index 37bce08614..86350f9af1 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-edit-preferences',
- _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html
index 42171b782f..c1c5c52dd8 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-edit-preferences</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-edit-preferences.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html
index 0a7433e966..caaf18b589 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -62,20 +62,28 @@ limitations under the License.
<template is="dom-repeat" items="[[_emails]]">
<tr>
<td class="emailColumn">[[item.email]]</td>
- <td class="preferredControl" on-tap="_handlePreferredControlTap">
- <input
- is="iron-input"
+ <td class="preferredControl" on-click="_handlePreferredControlClick">
+ <iron-input
class="preferredRadio"
type="radio"
on-change="_handlePreferredChange"
name="preferred"
- value="[[item.email]]"
+ bind-value="[[item.email]]"
checked$="[[item.preferred]]">
+ <input
+ is="iron-input"
+ class="preferredRadio"
+ type="radio"
+ on-change="_handlePreferredChange"
+ name="preferred"
+ value="[[item.email]]"
+ checked$="[[item.preferred]]">
+ </iron-input>
</td>
<td>
<gr-button
data-index$="[[index]]"
- on-tap="_handleDeleteButton"
+ on-click="_handleDeleteButton"
disabled="[[item.preferred]]"
class="remove-button">Delete</gr-button>
</td>
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
index 71d75cc3ea..8490b2641a 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-email-editor',
- _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
@@ -74,7 +73,7 @@
this.hasUnsavedChanges = true;
},
- _handlePreferredControlTap(e) {
+ _handlePreferredControlClick(e) {
if (e.target.classList.contains('preferredControl')) {
e.target.firstElementChild.click();
}
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
index e937f8bdde..8d3f2d203f 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-email-editor</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-email-editor.html">
@@ -49,7 +51,7 @@ limitations under the License.
element = fixture('basic');
- element.loadData().then(done);
+ element.loadData().then(flush(done));
});
test('renders', () => {
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.html b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.html
index 0c589c9980..cf73d99ba1 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.html
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-copy-clipboard/gr-copy-clipboard.html">
@@ -28,9 +28,6 @@ limitations under the License.
<template>
<style include="shared-styles"></style>
<style include="gr-form-styles">
- .statusHeader {
- width: 4em;
- }
.keyHeader {
width: 9em;
}
@@ -38,11 +35,13 @@ limitations under the License.
width: 15em;
}
#viewKeyOverlay {
- padding: 2em;
+ padding: var(--spacing-xxl);
width: 50em;
}
.publicKey {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
overflow-x: scroll;
overflow-wrap: break-word;
width: 30em;
@@ -53,11 +52,7 @@ limitations under the License.
right: 2em;
}
#existing {
- margin-bottom: 1em;
- }
- #existing .commentColumn {
- min-width: 27em;
- width: auto;
+ margin-bottom: var(--spacing-l);
}
</style>
<div class="gr-form-styles">
@@ -85,7 +80,7 @@ limitations under the License.
</td>
<td class="keyHeader">
<gr-button
- on-tap="_showKey"
+ on-click="_showKey"
data-index$="[[index]]"
link>Click to View</gr-button>
</td>
@@ -100,7 +95,7 @@ limitations under the License.
<td>
<gr-button
data-index$="[[index]]"
- on-tap="_handleDeleteKey">Delete</gr-button>
+ on-click="_handleDeleteKey">Delete</gr-button>
</td>
</tr>
</template>
@@ -119,10 +114,10 @@ limitations under the License.
</fieldset>
<gr-button
class="closeButton"
- on-tap="_closeOverlay">Close</gr-button>
+ on-click="_closeOverlay">Close</gr-button>
</gr-overlay>
<gr-button
- on-tap="save"
+ on-click="save"
disabled$="[[!hasUnsavedChanges]]">Save changes</gr-button>
</fieldset>
<fieldset>
@@ -139,7 +134,7 @@ limitations under the License.
<gr-button
id="addButton"
disabled$="[[_computeAddButtonDisabled(_newKey)]]"
- on-tap="_handleAddKey">Add new GPG key</gr-button>
+ on-click="_handleAddKey">Add new GPG key</gr-button>
</fieldset>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
index a50509cbda..890061e87b 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-gpg-editor',
- _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html
index aa53aca1bd..9cfbde5fed 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-gpg-editor</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-gpg-editor.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.html b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.html
index 2c7afd3300..ca500c8732 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.html
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
index 4de24aa193..d62a241dee 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-group-list',
- _legacyUndefinedCheck: true,
properties: {
_groups: Array,
@@ -40,7 +39,9 @@
_computeGroupPath(group) {
if (!group || !group.id) { return; }
- return Gerrit.Nav.getUrlForGroup(group.id);
+ // Group ID is already encoded from the API
+ // Decode it here to match with our router encoding behavior
+ return Gerrit.Nav.getUrlForGroup(decodeURIComponent(group.id));
},
});
})();
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
index 3fa5a36ac8..0422d1b32c 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-settings-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-group-list.html">
@@ -71,7 +73,8 @@ limitations under the License.
teardown(() => { sandbox.restore(); });
test('renders', () => {
- const rows = Polymer.dom(element.root).querySelectorAll('tbody tr');
+ const rows = Array.from(
+ Polymer.dom(element.root).querySelectorAll('tbody tr'));
assert.equal(rows.length, 3);
@@ -90,21 +93,30 @@ limitations under the License.
});
test('_computeGroupPath', () => {
- sandbox.stub(Gerrit.Nav, 'getUrlForGroup',
+ let urlStub = sandbox.stub(Gerrit.Nav, 'getUrlForGroup',
() => '/admin/groups/e2cd66f88a2db4d391ac068a92d987effbe872f5');
let group = {
id: 'e2cd66f88a2db4d391ac068a92d987effbe872f5',
};
-
assert.equal(element._computeGroupPath(group),
'/admin/groups/e2cd66f88a2db4d391ac068a92d987effbe872f5');
group = {
name: 'admin',
};
-
assert.isUndefined(element._computeGroupPath(group));
+
+ urlStub.restore();
+
+ urlStub = sandbox.stub(Gerrit.Nav, 'getUrlForGroup',
+ () => '/admin/groups/user/test');
+
+ group = {
+ id: 'user%2Ftest',
+ };
+ assert.equal(element._computeGroupPath(group),
+ '/admin/groups/user/test');
});
});
</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.html b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.html
index 54cfd655d4..0cb9695cf3 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.html
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-copy-clipboard/gr-copy-clipboard.html">
@@ -28,19 +28,23 @@ limitations under the License.
<style include="shared-styles">
.password {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
}
#generatedPasswordOverlay {
- padding: 2em;
+ padding: var(--spacing-xxl);
width: 50em;
}
#generatedPasswordDisplay {
- margin: 1em 0;
+ margin: var(--spacing-l) 0;
}
#generatedPasswordDisplay .title {
width: unset;
}
#generatedPasswordDisplay .value {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
}
#passwordWarning {
font-style: italic;
@@ -61,7 +65,7 @@ limitations under the License.
</section>
<gr-button
id="generateButton"
- on-tap="_handleGenerateTap">Generate new password</gr-button>
+ on-click="_handleGenerateTap">Generate new password</gr-button>
</div>
<span hidden$="[[!_passwordUrl]]">
<a href$="[[_passwordUrl]]" target="_blank" rel="noopener">
@@ -91,7 +95,7 @@ limitations under the License.
<gr-button
link
class="closeButton"
- on-tap="_closeOverlay">Close</gr-button>
+ on-click="_closeOverlay">Close</gr-button>
</div>
</gr-overlay>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
index 99f4504f13..003e47105f 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-http-password',
- _legacyUndefinedCheck: true,
properties: {
_username: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
index ca50b2bea6..8924058210 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-settings-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-http-password.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html
index 73aa65f52c..ee855cc54e 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
@@ -50,7 +50,7 @@ limitations under the License.
display: none;
}
.space {
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
</style>
<div class="gr-form-styles">
@@ -75,7 +75,7 @@ limitations under the License.
<td class="deleteColumn">
<gr-button
class$="deleteButton [[_computeHideDeleteClass(item.can_delete)]]"
- on-tap="_handleDeleteItem">
+ on-click="_handleDeleteItem">
Delete
</gr-button>
</td>
@@ -84,13 +84,13 @@ limitations under the License.
</tbody>
</table>
</fieldset>
- <dom-if if="[[_showLinkAnotherIdentity]]">
+ <template is="dom-if" if="[[_showLinkAnotherIdentity]]">
<fieldset>
<a href$="[[_computeLinkAnotherIdentity()]]">
<gr-button id="linkAnotherIdentity" link>Link Another Identity</gr-button>
</a>
</fieldset>
- </dom-if>
+ </template>
</div>
<gr-overlay id="overlay" with-backdrop>
<gr-confirm-delete-item-dialog
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
index 4560a2e3b8..c927f1ee24 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
@@ -24,7 +24,6 @@
Polymer({
is: 'gr-identities',
- _legacyUndefinedCheck: true,
properties: {
_identities: Object,
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html
index 5468d27079..12774246f5 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-identities</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-identities.html">
@@ -69,7 +71,8 @@ limitations under the License.
});
test('renders', () => {
- const rows = Polymer.dom(element.root).querySelectorAll('tbody tr');
+ const rows = Array.from(
+ Polymer.dom(element.root).querySelectorAll('tbody tr'));
assert.equal(rows.length, 2);
@@ -82,7 +85,8 @@ limitations under the License.
});
test('renders email', () => {
- const rows = Polymer.dom(element.root).querySelectorAll('tbody tr');
+ const rows = Array.from(
+ Polymer.dom(element.root).querySelectorAll('tbody tr'));
assert.equal(rows.length, 2);
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.html b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.html
index aa42623457..1485628511 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.html
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -61,22 +61,22 @@ limitations under the License.
<td class="buttonColumn">
<gr-button
link
- data-index="[[index]]"
- on-tap="_handleMoveUpButton"
+ data-index$="[[index]]"
+ on-click="_handleMoveUpButton"
class="moveUpButton">↑</gr-button>
</td>
<td class="buttonColumn">
<gr-button
link
- data-index="[[index]]"
- on-tap="_handleMoveDownButton"
+ data-index$="[[index]]"
+ on-click="_handleMoveDownButton"
class="moveDownButton">↓</gr-button>
</td>
<td>
<gr-button
link
- data-index="[[index]]"
- on-tap="_handleDeleteButton"
+ data-index$="[[index]]"
+ on-click="_handleDeleteButton"
class="remove-button">Delete</gr-button>
</td>
</tr>
@@ -85,19 +85,30 @@ limitations under the License.
<tfoot>
<tr>
<th>
- <input
- is="iron-input"
+ <iron-input
placeholder="New Title"
on-keydown="_handleInputKeydown"
bind-value="{{_newName}}">
+ <input
+ is="iron-input"
+ placeholder="New Title"
+ on-keydown="_handleInputKeydown"
+ bind-value="{{_newName}}">
+ </iron-input>
</th>
<th>
- <input
+ <iron-input
class="newUrlInput"
- is="iron-input"
placeholder="New URL"
on-keydown="_handleInputKeydown"
bind-value="{{_newUrl}}">
+ <input
+ class="newUrlInput"
+ is="iron-input"
+ placeholder="New URL"
+ on-keydown="_handleInputKeydown"
+ bind-value="{{_newUrl}}">
+ </iron-input>
</th>
<th></th>
<th></th>
@@ -105,7 +116,7 @@ limitations under the License.
<gr-button
link
disabled$="[[_computeAddDisabled(_newName, _newUrl)]]"
- on-tap="_handleAddButton">Add</gr-button>
+ on-click="_handleAddButton">Add</gr-button>
</th>
</tr>
</tfoot>
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
index 8587338dd7..4f3c0c793f 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-menu-editor',
- _legacyUndefinedCheck: true,
properties: {
menuItems: Array,
@@ -28,7 +27,7 @@
},
_handleMoveUpButton(e) {
- const index = Polymer.dom(e).localTarget.dataIndex;
+ const index = Number(Polymer.dom(e).localTarget.dataset.index);
if (index === 0) { return; }
const row = this.menuItems[index];
const prev = this.menuItems[index - 1];
@@ -36,7 +35,7 @@
},
_handleMoveDownButton(e) {
- const index = Polymer.dom(e).localTarget.dataIndex;
+ const index = Number(Polymer.dom(e).localTarget.dataset.index);
if (index === this.menuItems.length - 1) { return; }
const row = this.menuItems[index];
const next = this.menuItems[index + 1];
@@ -44,7 +43,7 @@
},
_handleDeleteButton(e) {
- const index = Polymer.dom(e).localTarget.dataIndex;
+ const index = Number(Polymer.dom(e).localTarget.dataset.index);
this.splice('menuItems', index, 1);
},
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
index c8a54b6e6f..134e018a37 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-settings-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-menu-editor.html">
@@ -55,7 +57,7 @@ limitations under the License.
MockInteractions.tap(button);
}
- setup(() => {
+ setup(done => {
element = fixture('basic');
menu = [
{url: '/first/url', name: 'first name', target: '_blank'},
@@ -64,6 +66,7 @@ limitations under the License.
];
element.set('menuItems', menu);
Polymer.dom.flush();
+ flush(done);
});
test('renders', () => {
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
index 5f1794c398..f366d2a082 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
@@ -15,7 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -43,23 +45,23 @@ limitations under the License.
display: block;
}
hr {
- margin-top: 1em;
- margin-bottom: 1em;
+ margin-top: var(--spacing-l);
+ margin-bottom: var(--spacing-l);
}
header {
border-bottom: 1px solid var(--border-color);
font-weight: var(--font-weight-bold);
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
.container {
- padding: .5em 1.5em;
+ padding: var(--spacing-m) var(--spacing-xl);
}
footer {
display: flex;
justify-content: flex-end;
}
footer gr-button {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
input {
width: 20em;
@@ -81,19 +83,27 @@ limitations under the License.
<hr>
<section>
<div class="title">Full Name</div>
- <input
- is="iron-input"
- id="name"
+ <iron-input
bind-value="{{_account.name}}"
disabled="[[_saving]]">
+ <input
+ is="iron-input"
+ id="name"
+ bind-value="{{_account.name}}"
+ disabled="[[_saving]]">
+ </iron-input>
</section>
<section class$="[[_computeUsernameClass(_usernameMutable)]]">
<div class="title">Username</div>
- <input
- is="iron-input"
- id="username"
+ <iron-input
bind-value="{{_account.username}}"
disabled="[[_saving]]">
+ <input
+ is="iron-input"
+ id="username"
+ bind-value="{{_account.username}}"
+ disabled="[[_saving]]">
+ </iron-input>
</section>
<section>
<div class="title">Preferred Email</div>
@@ -109,7 +119,7 @@ limitations under the License.
<hr>
<p>
More configuration options for Gerrit may be found in the
- <a on-tap="close" href$="[[settingsUrl]]">settings</a>.
+ <a on-click="close" href$="[[settingsUrl]]">settings</a>.
</p>
</main>
<footer>
@@ -117,13 +127,13 @@ limitations under the License.
id="closeButton"
link
disabled="[[_saving]]"
- on-tap="_handleClose">Close</gr-button>
+ on-click="_handleClose">Close</gr-button>
<gr-button
id="saveButton"
primary
link
disabled="[[_computeSaveDisabled(_account.name, _account.email, _saving)]]"
- on-tap="_handleSave">Save</gr-button>
+ on-click="_handleSave">Save</gr-button>
</footer>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
index 6b4ee18559..063341663f 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-registration-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when account details are changed.
@@ -60,6 +59,10 @@
_serverConfig: Object,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
hostAttributes: {
role: 'dialog',
},
@@ -120,6 +123,14 @@
},
_computeUsernameMutable(config, username) {
+ // Polymer 2: check for undefined
+ if ([
+ config,
+ username,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
return config.auth.editable_account_fields.includes('USER_NAME') &&
!username;
},
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
index 93a3188fe3..d1b5c803e8 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-registration-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-registration-dialog.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html
index 30a3801a49..937ee7976b 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html
@@ -15,14 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-settings-item">
<template>
<style>
:host {
display: block;
- margin-bottom: 2em;
+ margin-bottom: var(--spacing-xxl);
}
</style>
<h2 id="[[anchor]]">[[title]]</h2>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
index dc1aa936ca..dae3b68efd 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-settings-item',
- _legacyUndefinedCheck: true,
+
properties: {
anchor: String,
title: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html
index f64d898c4d..846f776a62 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/gr-page-nav-styles.html">
<dom-module id="gr-settings-menu-item">
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
index 2a56b0935f..5db0031846 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-settings-menu-item',
- _legacyUndefinedCheck: true,
+
properties: {
href: String,
title: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
index f0c4d6ed15..74971cf482 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
@@ -15,10 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../behaviors/docs-url-behavior/docs-url-behavior.html">
-<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="/bower_components/paper-toggle-button/paper-toggle-button.html">
+
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
<link rel="import" href="../../../styles/gr-page-nav-styles.html">
@@ -49,18 +51,18 @@ limitations under the License.
:host {
color: var(--primary-text-color);
}
- #newEmailInput {
+ .newEmailInput {
width: 20em;
}
#email {
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
main section.darkToggle {
display: block;
}
.filters p,
.darkToggle p {
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
.queryExample em {
color: violet;
@@ -68,8 +70,8 @@ limitations under the License.
.toggle {
align-items: center;
display: flex;
- margin-bottom: 1rem;
- margin-right: 1rem;
+ margin-bottom: var(--spacing-l);
+ margin-right: var(--spacing-l);
}
</style>
<style include="gr-form-styles"></style>
@@ -132,7 +134,7 @@ limitations under the License.
mutable="{{_accountNameMutable}}"
has-unsaved-changes="{{_accountInfoChanged}}"></gr-account-info>
<gr-button
- on-tap="_handleSaveAccountInfo"
+ on-click="_handleSaveAccountInfo"
disabled="[[!_accountInfoChanged]]">Save changes</gr-button>
</fieldset>
<h2
@@ -278,7 +280,7 @@ limitations under the License.
</section>
<gr-button
id="savePrefs"
- on-tap="_handleSavePreferences"
+ on-click="_handleSavePreferences"
disabled="[[!_prefsChanged]]">Save changes</gr-button>
</fieldset>
<h2
@@ -292,7 +294,7 @@ limitations under the License.
has-unsaved-changes="{{_diffPrefsChanged}}"></gr-diff-preferences>
<gr-button
id="saveDiffPrefs"
- on-tap="_handleSaveDiffPreferences"
+ on-click="_handleSaveDiffPreferences"
disabled$="[[!_diffPrefsChanged]]">Save changes</gr-button>
</fieldset>
<h2
@@ -306,7 +308,7 @@ limitations under the License.
has-unsaved-changes="{{_editPrefsChanged}}"></gr-edit-preferences>
<gr-button
id="saveEditPrefs"
- on-tap="_handleSaveEditPreferences"
+ on-click="_handleSaveEditPreferences"
disabled$="[[!_editPrefsChanged]]">Save changes</gr-button>
</fieldset>
<h2 id="Menu" class$="[[_computeHeaderClass(_menuChanged)]]">Menu</h2>
@@ -315,12 +317,12 @@ limitations under the License.
menu-items="{{_localMenu}}"></gr-menu-editor>
<gr-button
id="saveMenu"
- on-tap="_handleSaveMenu"
+ on-click="_handleSaveMenu"
disabled="[[!_menuChanged]]">Save changes</gr-button>
<gr-button
id="resetMenu"
link
- on-tap="_handleResetMenuButton">Reset</gr-button>
+ on-click="_handleResetMenuButton">Reset</gr-button>
</fieldset>
<h2 id="ChangeTableColumns"
class$="[[_computeHeaderClass(_changeTableChanged)]]">
@@ -333,7 +335,7 @@ limitations under the License.
</gr-change-table-editor>
<gr-button
id="saveChangeTable"
- on-tap="_handleSaveChangeTable"
+ on-click="_handleSaveChangeTable"
disabled="[[!_changeTableChanged]]">Save changes</gr-button>
</fieldset>
<h2
@@ -346,7 +348,7 @@ limitations under the License.
has-unsaved-changes="{{_watchedProjectsChanged}}"
id="watchedProjectsEditor"></gr-watched-projects-editor>
<gr-button
- on-tap="_handleSaveWatchedProjects"
+ on-click="_handleSaveWatchedProjects"
disabled$="[[!_watchedProjectsChanged]]"
id="_handleSaveWatchedProjects">Save changes</gr-button>
</fieldset>
@@ -360,21 +362,29 @@ limitations under the License.
id="emailEditor"
has-unsaved-changes="{{_emailsChanged}}"></gr-email-editor>
<gr-button
- on-tap="_handleSaveEmails"
+ on-click="_handleSaveEmails"
disabled$="[[!_emailsChanged]]">Save changes</gr-button>
</fieldset>
<fieldset id="newEmail">
<section>
<span class="title">New email address</span>
<span class="value">
- <input
- id="newEmailInput"
+ <iron-input
+ class="newEmailInput"
bind-value="{{_newEmail}}"
- is="iron-input"
type="text"
disabled="[[_addingEmail]]"
on-keydown="_handleNewEmailKeydown"
placeholder="email@example.com">
+ <input
+ class="newEmailInput"
+ bind-value="{{_newEmail}}"
+ is="iron-input"
+ type="text"
+ disabled="[[_addingEmail]]"
+ on-keydown="_handleNewEmailKeydown"
+ placeholder="email@example.com">
+ </iron-input>
</span>
</section>
<section
@@ -387,7 +397,7 @@ limitations under the License.
</section>
<gr-button
disabled="[[!_computeAddEmailButtonEnabled(_newEmail, _addingEmail)]]"
- on-tap="_handleAddEmailButton">Send verification</gr-button>
+ on-click="_handleAddEmailButton">Send verification</gr-button>
</fieldset>
<template is="dom-if" if="[[_showHttpAuth(_serverConfig)]]">
<div>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
index a66c38d8de..714faab262 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
@@ -47,7 +47,6 @@
Polymer({
is: 'gr-settings-view',
- _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -149,6 +148,7 @@
behaviors: [
Gerrit.DocsUrlBehavior,
Gerrit.ChangeTableBehavior,
+ Gerrit.FireBehavior,
],
observers: [
@@ -392,7 +392,7 @@
},
_isNewEmailValid(newEmail) {
- return newEmail.includes('@');
+ return newEmail && newEmail.includes('@');
},
_computeAddEmailButtonEnabled(newEmail, addingEmail) {
@@ -435,6 +435,7 @@
this.dispatchEvent(new CustomEvent('show-alert', {
detail: {message: RELOAD_MESSAGE},
bubbles: true,
+ composed: true,
}));
this.async(() => {
window.location.reload();
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
index 506c6af25b..6dcf124eb2 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-settings-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-settings-view.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.html b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.html
index 57458f824b..2a27194b0d 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.html
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-copy-clipboard/gr-copy-clipboard.html">
@@ -35,11 +35,13 @@ limitations under the License.
width: 7.5em;
}
#viewKeyOverlay {
- padding: 2em;
+ padding: var(--spacing-xxl);
width: 50em;
}
.publicKey {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
overflow-x: scroll;
overflow-wrap: break-word;
width: 30em;
@@ -50,7 +52,7 @@ limitations under the License.
right: 2em;
}
#existing {
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
#existing .commentColumn {
min-width: 27em;
@@ -77,7 +79,7 @@ limitations under the License.
<td>
<gr-button
link
- on-tap="_showKey"
+ on-click="_showKey"
data-index$="[[index]]"
link>Click to View</gr-button>
</td>
@@ -93,7 +95,7 @@ limitations under the License.
<gr-button
link
data-index$="[[index]]"
- on-tap="_handleDeleteKey">Delete</gr-button>
+ on-click="_handleDeleteKey">Delete</gr-button>
</td>
</tr>
</template>
@@ -116,10 +118,10 @@ limitations under the License.
</fieldset>
<gr-button
class="closeButton"
- on-tap="_closeOverlay">Close</gr-button>
+ on-click="_closeOverlay">Close</gr-button>
</gr-overlay>
<gr-button
- on-tap="save"
+ on-click="save"
disabled$="[[!hasUnsavedChanges]]">Save changes</gr-button>
</fieldset>
<fieldset>
@@ -137,7 +139,7 @@ limitations under the License.
id="addButton"
link
disabled$="[[_computeAddButtonDisabled(_newKey)]]"
- on-tap="_handleAddKey">Add new SSH key</gr-button>
+ on-click="_handleAddKey">Add new SSH key</gr-button>
</fieldset>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
index 4c423e8920..874173a4a7 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-ssh-editor',
- _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
index 8ed07309aa..d313f5a50b 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-ssh-editor</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-ssh-editor.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html
index 85fe368bb3..360ea2d331 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html
@@ -14,7 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -27,7 +28,7 @@ limitations under the License.
<style include="gr-form-styles">
#watchedProjects .notifType {
text-align: center;
- padding: 0 0.4em;
+ padding: 0 var(--spacing-s);
}
.notifControl {
cursor: pointer;
@@ -39,7 +40,7 @@ limitations under the License.
.projectFilter {
color: var(--deemphasized-text-color);
font-style: italic;
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
.newFilterInput {
width: 100%;
@@ -73,7 +74,7 @@ limitations under the License.
is="dom-repeat"
items="[[_getTypes()]]"
as="type">
- <td class="notifControl" on-tap="_handleNotifCellTap">
+ <td class="notifControl" on-click="_handleNotifCellClick">
<input
type="checkbox"
data-index$="[[projectIndex]]"
@@ -86,7 +87,7 @@ limitations under the License.
<gr-button
link
data-index$="[[projectIndex]]"
- on-tap="_handleRemoveProject">Delete</gr-button>
+ on-click="_handleRemoveProject">Delete</gr-button>
</td>
</tr>
</template>
@@ -103,14 +104,18 @@ limitations under the License.
placeholder="Repo"></gr-autocomplete>
</th>
<th colspan$="[[_getTypeCount()]]">
- <input
- id="newFilter"
+ <iron-input
class="newFilterInput"
- is="iron-input"
placeholder="branch:name, or other search expression">
+ <input
+ id="newFilter"
+ class="newFilterInput"
+ is="iron-input"
+ placeholder="branch:name, or other search expression">
+ </iron-input>
</th>
<th>
- <gr-button link on-tap="_handleAddProject">Add</gr-button>
+ <gr-button link on-click="_handleAddProject">Add</gr-button>
</th>
</tr>
</tfoot>
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
index bd18456692..a40094d3be 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
@@ -27,7 +27,6 @@
Polymer({
is: 'gr-watched-projects-editor',
- _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
@@ -170,7 +169,7 @@
this.hasUnsavedChanges = true;
},
- _handleNotifCellTap(e) {
+ _handleNotifCellClick(e) {
const checkbox = Polymer.dom(e.target).querySelector('input');
if (checkbox) { checkbox.click(); }
},
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
index 9022bcc579..7a238ec42b 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-settings-view</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-watched-projects-editor.html">
@@ -187,6 +189,7 @@ limitations under the License.
element.$.newProject.value = {id: 'project b'};
element.$.newProject.setText('project b');
element.$.newFilter.bindValue = 'filter 1';
+ element.$.newFilter.value = 'filter 1';
element._handleAddProject();
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
index 543ed85de2..5a095a4142 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../gr-account-link/gr-account-link.html">
<link rel="import" href="../gr-button/gr-button.html">
<link rel="import" href="../gr-icons/gr-icons.html">
@@ -34,28 +35,34 @@ limitations under the License.
background: var(--chip-background-color);
border-radius: .75em;
display: inline-flex;
- padding: 0 .5em;
+ padding: 0 var(--spacing-m);
}
:host([show-avatar]) .container {
padding-left: 0;
}
+ gr-button.remove {
+ --gr-remove-button-style: {
+ border: 0;
+ color: var(--deemphasized-text-color);
+ font-weight: normal;
+ height: .6em;
+ line-height: 10px;
+ margin-left: var(--spacing-xs);
+ padding: 0;
+ text-decoration: none;
+ }
+ }
+
gr-button.remove:hover,
gr-button.remove:focus {
--gr-button: {
+ @apply --gr-remove-button-style;
color: #333;
}
}
gr-button.remove {
--gr-button: {
- border: 0;
- color: var(--deemphasized-text-color);
- font-size: 1.7rem;
- font-weight: normal;
- height: .6em;
- line-height: .6;
- margin-left: .15em;
- padding: 0;
- text-decoration: none;
+ @apply --gr-remove-button-style;
}
}
:host:focus {
@@ -93,7 +100,7 @@ limitations under the License.
tabindex="-1"
aria-label="Remove"
class$="remove [[_getBackgroundClass(transparentBackground)]]"
- on-tap="_handleRemoveTap">
+ on-click="_handleRemoveTap">
<iron-icon icon="gr-icons:close"></iron-icon>
</gr-button>
</div>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
index 827e33b527..10c876dab4 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
@@ -20,7 +20,6 @@
Polymer({
is: 'gr-account-chip',
- _legacyUndefinedCheck: true,
/**
* Fired to indicate a key was pressed while this chip was focused.
@@ -57,6 +56,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
ready() {
this._getHasAvatars().then(hasAvatars => {
this.showAvatar = hasAvatars;
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.html
index 582c83bbe4..ae656fd21f 100644
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.html
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
-<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
-<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../gr-autocomplete/gr-autocomplete.html">
+<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-account-entry">
<template>
@@ -35,14 +35,13 @@ limitations under the License.
borderless="[[borderless]]"
placeholder="[[placeholder]]"
threshold="[[suggestFrom]]"
- query="[[query]]"
+ query="[[querySuggestions]]"
allow-non-suggested-values="[[allowAnyInput]]"
on-commit="_handleInputCommit"
clear-on-commit
warn-uncommitted
text="{{_inputText}}">
</gr-autocomplete>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-account-entry.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js
new file mode 100644
index 0000000000..b2e0973b9b
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.js
@@ -0,0 +1,101 @@
+/**
+ * @license
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function() {
+ 'use strict';
+
+ /**
+ * gr-account-entry is an element for entering account
+ * and/or group with autocomplete support.
+ */
+ Polymer({
+ is: 'gr-account-entry',
+
+ /**
+ * Fired when an account is entered.
+ *
+ * @event add
+ */
+
+ /**
+ * When allowAnyInput is true, account-text-changed is fired when input text
+ * changed. This is needed so that the reply dialog's save button can be
+ * enabled for arbitrary cc's, which don't need a 'commit'.
+ *
+ * @event account-text-changed
+ */
+ properties: {
+ allowAnyInput: Boolean,
+ borderless: Boolean,
+ placeholder: String,
+
+ // suggestFrom = 0 to enable default suggestions.
+ suggestFrom: {
+ type: Number,
+ value: 0,
+ },
+
+ /** @type {!function(string): !Promise<Array<{name, value}>>} */
+ querySuggestions: {
+ type: Function,
+ notify: true,
+ value() {
+ return input => Promise.resolve([]);
+ },
+ },
+
+ _config: Object,
+ /** The value of the autocomplete entry. */
+ _inputText: {
+ type: String,
+ observer: '_inputTextChanged',
+ },
+
+ },
+
+ get focusStart() {
+ return this.$.input.focusStart;
+ },
+
+ focus() {
+ this.$.input.focus();
+ },
+
+ clear() {
+ this.$.input.clear();
+ },
+
+ setText(text) {
+ this.$.input.setText(text);
+ },
+
+ getText() {
+ return this.$.input.text;
+ },
+
+ _handleInputCommit(e) {
+ this.fire('add', {value: e.detail.value});
+ this.$.input.focus();
+ },
+
+ _inputTextChanged(text) {
+ if (text.length && this.allowAnyInput) {
+ this.dispatchEvent(new CustomEvent(
+ 'account-text-changed', {bubbles: true, composed: true}));
+ }
+ },
+ });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html
new file mode 100644
index 0000000000..6896af9537
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-account-entry</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<script src="../../../scripts/util.js"></script>
+
+<link rel="import" href="gr-account-entry.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-account-entry></gr-account-entry>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-account-entry tests', () => {
+ let sandbox;
+
+ const suggestion1 = {
+ email: 'email1@example.com',
+ _account_id: 1,
+ some_property: 'value',
+ };
+ const suggestion2 = {
+ email: 'email2@example.com',
+ _account_id: 2,
+ };
+ const suggestion3 = {
+ email: 'email25@example.com',
+ _account_id: 25,
+ some_other_property: 'other value',
+ };
+
+ setup(done => {
+ element = fixture('basic');
+ sandbox = sinon.sandbox.create();
+ return flush(done);
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ suite('stubbed values for querySuggestions', () => {
+ setup(() => {
+ element.querySuggestions = input => {
+ return Promise.resolve([
+ suggestion1,
+ suggestion2,
+ suggestion3,
+ ]);
+ };
+ });
+ });
+
+ test('account-text-changed fired when input text changed and allowAnyInput',
+ () => {
+ // Spy on query, as that is called when _updateSuggestions proceeds.
+ const changeStub = sandbox.stub();
+ element.allowAnyInput = true;
+ element.querySuggestions = input => Promise.resolve([]);
+ element.addEventListener('account-text-changed', changeStub);
+ element.$.input.text = 'a';
+ assert.isTrue(changeStub.calledOnce);
+ element.$.input.text = 'ab';
+ assert.isTrue(changeStub.calledTwice);
+ });
+
+ test('account-text-changed not fired when input text changed without ' +
+ 'allowAnyInput', () => {
+ // Spy on query, as that is called when _updateSuggestions proceeds.
+ const changeStub = sandbox.stub();
+ element.querySuggestions = input => Promise.resolve([]);
+ element.addEventListener('account-text-changed', changeStub);
+ element.$.input.text = 'a';
+ assert.isFalse(changeStub.called);
+ });
+
+ test('setText', () => {
+ // Spy on query, as that is called when _updateSuggestions proceeds.
+ const suggestSpy = sandbox.spy(element.$.input, 'query');
+ element.setText('test text');
+ flushAsynchronousOperations();
+
+ assert.equal(element.$.input.$.input.value, 'test text');
+ assert.isFalse(suggestSpy.called);
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html
index bdf37bfa04..7ed79624b0 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.html
@@ -15,9 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
+<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.html">
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../gr-avatar/gr-avatar.html">
<link rel="import" href="../gr-limited-text/gr-limited-text.html">
@@ -35,7 +35,7 @@ limitations under the License.
gr-avatar {
height: 1.3em;
width: 1.3em;
- margin-right: .15em;
+ margin-right: var(--spacing-xs);
vertical-align: -.25em;
}
.text {
@@ -64,7 +64,11 @@ limitations under the License.
[[_computeEmailStr(account)]]
</span>
<template is="dom-if" if="[[account.status]]">
- (<gr-limited-text limit="10" text="[[account.status]]"></gr-limited-text>)
+ (<gr-limited-text
+ disable-tooltip="true"
+ limit="[[_computeStatusTextLength(account, _serverConfig)]]"
+ text="[[account.status]]">
+ </gr-limited-text>)
</template>
</span>
</span>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
index 7983fad80e..418d2eac30 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-account-label',
- _legacyUndefinedCheck: true,
properties: {
/**
@@ -52,7 +51,7 @@
},
behaviors: [
- Gerrit.AnonymousNameBehavior,
+ Gerrit.DisplayNameBehavior,
Gerrit.TooltipBehavior,
],
@@ -66,18 +65,38 @@
return this.getUserName(config, account, false);
},
+ _computeStatusTextLength(account, config) {
+ // 35 as the max length of the name + status
+ return Math.max(10, 35 - this._computeName(account, config).length);
+ },
+
_computeAccountTitle(account, tooltip) {
+ // Polymer 2: check for undefined
+ if ([
+ account,
+ tooltip,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
if (!account) { return; }
let result = '';
if (this._computeName(account, this._serverConfig)) {
result += this._computeName(account, this._serverConfig);
}
if (account.email) {
- result += ' <' + account.email + '>';
+ result += ` <${account.email}>`;
}
if (this.additionalText) {
- return result + ' ' + this.additionalText;
+ result += ` ${this.additionalText}`;
}
+
+ // Show status in the label tooltip instead of
+ // in a separate tooltip on status
+ if (account.status) {
+ result += ` (${account.status})`;
+ }
+
return result;
},
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
index cd8e194e03..45545fe211 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-account-label</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
@@ -66,32 +68,35 @@ limitations under the License.
{
name: 'Andrew Bonventre',
email: 'andybons+gerrit@gmail.com',
- }),
+ }, /* additionalText= */ ''),
'Andrew Bonventre <andybons+gerrit@gmail.com>');
assert.equal(element._computeAccountTitle(
- {name: 'Andrew Bonventre'}),
+ {name: 'Andrew Bonventre'}, /* additionalText= */ ''),
'Andrew Bonventre');
assert.equal(element._computeAccountTitle(
{
email: 'andybons+gerrit@gmail.com',
- }),
+ }, /* additionalText= */ ''),
'Anonymous <andybons+gerrit@gmail.com>');
assert.equal(element._computeShowEmailClass(
{
name: 'Andrew Bonventre',
email: 'andybons+gerrit@gmail.com',
- }), '');
+ }, /* additionalText= */ ''), '');
assert.equal(element._computeShowEmailClass(
{
email: 'andybons+gerrit@gmail.com',
- }), 'showEmail');
+ }, /* additionalText= */ ''), 'showEmail');
- assert.equal(element._computeShowEmailClass({name: 'Andrew Bonventre'}),
- '');
+ assert.equal(element._computeShowEmailClass(
+ {name: 'Andrew Bonventre'},
+ /* additionalText= */ ''
+ ),
+ '');
assert.equal(element._computeShowEmailClass(undefined), '');
@@ -134,5 +139,44 @@ limitations under the License.
'TestAnon');
});
});
+
+ suite('status in tooltip', () => {
+ setup(() => {
+ element = fixture('basic');
+ element.account = {
+ name: 'test',
+ email: 'test@google.com',
+ status: 'OOO until Aug 10th',
+ };
+ element._config = {
+ user: {
+ anonymous_coward_name: 'Anonymous Coward',
+ },
+ };
+ });
+
+ test('tooltip should contain status text', () => {
+ assert.deepEqual(element.title,
+ 'test <test@google.com> (OOO until Aug 10th)');
+ });
+
+ test('status text should not have tooltip', () => {
+ flushAsynchronousOperations();
+ assert.deepEqual(element.$$('gr-limited-text').title, '');
+ });
+
+ test('status text should honor the name length and total length', () => {
+ assert.deepEqual(
+ element._computeStatusTextLength(element.account, element._config),
+ 31
+ );
+ assert.deepEqual(
+ element._computeStatusTextLength({
+ name: 'a very long long long long name',
+ }, element._config),
+ 10
+ );
+ });
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html
index 34b0de63bd..d3575b29f9 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.html
@@ -16,7 +16,7 @@ limitations under the License.
-->
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../gr-account-label/gr-account-label.html">
<link rel="import" href="../../../styles/shared-styles.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
index 03967f10ea..faaf9c3ddd 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-account-link',
- _legacyUndefinedCheck: true,
properties: {
additionalText: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
index 6d1831e7a8..134c579ec9 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-account-link</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-account-link.html">
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.html
index 1bfc5eba5e..2ce608be8f 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.html
@@ -15,8 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
+<link rel="import" href="../gr-account-chip/gr-account-chip.html">
<link rel="import" href="../gr-account-entry/gr-account-entry.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -25,7 +26,7 @@ limitations under the License.
<style include="shared-styles">
gr-account-chip {
display: inline-block;
- margin: .2em .2em .2em 0;
+ margin: var(--spacing-xs) var(--spacing-xs) var(--spacing-xs) 0;
}
gr-account-entry {
display: flex;
@@ -55,7 +56,7 @@ limitations under the License.
account="[[account]]"
class$="[[_computeChipClass(account)]]"
data-account-id$="[[account._account_id]]"
- removable="[[_computeRemovable(account)]]"
+ removable="[[_computeRemovable(account, readonly)]]"
on-keydown="_handleChipKeydown"
tabindex="-1">
</gr-account-chip>
@@ -66,13 +67,13 @@ limitations under the License.
hidden$="[[_computeEntryHidden(maxCount, accounts.*, readonly)]]"
id="entry"
change="[[change]]"
- filter="[[filter]]"
placeholder="[[placeholder]]"
on-add="_handleAdd"
on-input-keydown="_handleInputKeydown"
allow-any-input="[[allowAnyInput]]"
- allow-any-user="[[allowAnyUser]]">
+ query-suggestions="[[_querySuggestions]]">
</gr-account-entry>
+ <slot></slot>
</template>
<script src="gr-account-list.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
index 7cdffc8846..de66c501d7 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-account-list',
- _legacyUndefinedCheck: true,
/**
* Fired when user inputs an invalid email address.
@@ -38,6 +37,20 @@
change: Object,
filter: Function,
placeholder: String,
+ disabled: {
+ type: Function,
+ value: false,
+ },
+
+ /**
+ * Returns suggestions and convert them to list item
+ *
+ * @type {Gerrit.GrSuggestionsProvider}
+ */
+ suggestionsProvider: {
+ type: Object,
+ },
+
/**
* Needed for template checking since value is initially set to null.
*
@@ -51,21 +64,6 @@
type: Boolean,
value: false,
},
-
- /**
- * When true, the account-entry autocomplete uses the account suggest API
- * endpoint, which suggests any account in that Gerrit instance (and does
- * not suggest groups).
- *
- * When false/undefined, account-entry uses the suggest_reviewers API
- * endpoint, which suggests any account or group in that Gerrit instance
- * that is not already a reviewer (or is not CCed) on that change.
- */
- allowAnyUser: {
- type: Boolean,
- value: false,
- },
-
/**
* When true, allows for non-suggested inputs to be added.
*/
@@ -83,14 +81,29 @@
type: Number,
value: 0,
},
+
+ /** Returns suggestion items
+ *
+ * @type {!function(string): Promise<Array<Gerrit.GrSuggestionItem>>}
+ */
+ _querySuggestions: {
+ type: Function,
+ value() {
+ return this._getSuggestions.bind(this);
+ },
+ },
},
+ behaviors: [
+ // Used in the tests for gr-account-list and other elements tests.
+ Gerrit.FireBehavior,
+ ],
+
listeners: {
remove: '_handleRemove',
},
get accountChips() {
- // Polymer2: querySelectorAll returns NodeList instead of Array.
return Array.from(
Polymer.dom(this.root).querySelectorAll('gr-account-chip'));
},
@@ -99,36 +112,54 @@
return this.$.entry.focusStart;
},
+ _getSuggestions(input) {
+ const provider = this.suggestionsProvider;
+ if (!provider) {
+ return Promise.resolve([]);
+ }
+ return provider.getSuggestions(input).then(suggestions => {
+ if (!suggestions) { return []; }
+ if (this.filter) {
+ suggestions = suggestions.filter(this.filter);
+ }
+ return suggestions.map(suggestion =>
+ provider.makeSuggestionItem(suggestion));
+ });
+ },
+
_handleAdd(e) {
- this._addReviewer(e.detail.value);
+ this._addAccountItem(e.detail.value);
},
- _addReviewer(reviewer) {
+ _addAccountItem(item) {
// Append new account or group to the accounts property. We add our own
// internal properties to the account/group here, so we clone the object
// to avoid cluttering up the shared change object.
- if (reviewer.account) {
+ if (item.account) {
const account =
- Object.assign({}, reviewer.account, {_pendingAdd: true});
+ Object.assign({}, item.account, {_pendingAdd: true});
this.push('accounts', account);
- } else if (reviewer.group) {
- if (reviewer.confirm) {
- this.pendingConfirmation = reviewer;
+ } else if (item.group) {
+ if (item.confirm) {
+ this.pendingConfirmation = item;
return;
}
- const group = Object.assign({}, reviewer.group,
+ const group = Object.assign({}, item.group,
{_pendingAdd: true, _group: true});
this.push('accounts', group);
} else if (this.allowAnyInput) {
- if (!reviewer.includes('@')) {
+ if (!item.includes('@')) {
// Repopulate the input with what the user tried to enter and have
// a toast tell them why they can't enter it.
- this.$.entry.setText(reviewer);
- this.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message: VALID_EMAIL_ALERT}, bubbles: true}));
+ this.$.entry.setText(item);
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {message: VALID_EMAIL_ALERT},
+ bubbles: true,
+ composed: true,
+ }));
return false;
} else {
- const account = {email: reviewer, _pendingAdd: true};
+ const account = {email: item, _pendingAdd: true};
this.push('accounts', account);
}
}
@@ -166,8 +197,8 @@
return a === b;
},
- _computeRemovable(account) {
- if (this.readonly) { return false; }
+ _computeRemovable(account, readonly) {
+ if (readonly) { return false; }
if (this.removableValues) {
for (let i = 0; i < this.removableValues.length; i++) {
if (this._accountMatches(this.removableValues[i], account)) {
@@ -186,7 +217,9 @@
},
_removeAccount(toRemove) {
- if (!toRemove || !this._computeRemovable(toRemove)) { return; }
+ if (!toRemove || !this._computeRemovable(toRemove, this.readonly)) {
+ return;
+ }
for (let i = 0; i < this.accounts.length; i++) {
let matches;
const account = this.accounts[i];
@@ -203,8 +236,13 @@
console.warn('received remove event for missing account', toRemove);
},
+ _getNativeInput(paperInput) {
+ // In Polymer 2 inputElement isn't nativeInput anymore
+ return paperInput.$.nativeInput || paperInput.inputElement;
+ },
+
_handleInputKeydown(e) {
- const input = e.detail.input.inputElement;
+ const input = this._getNativeInput(e.detail.input);
if (input.selectionStart !== input.selectionEnd ||
input.selectionStart !== 0) {
return;
@@ -270,7 +308,7 @@
submitEntryText() {
const text = this.$.entry.getText();
if (!text.length) { return true; }
- const wasSubmitted = this._addReviewer(text);
+ const wasSubmitted = this._addAccountItem(text);
if (wasSubmitted) { this.$.entry.clear(); }
return wasSubmitted;
},
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
index 544238b7d7..f931a69ec2 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-account-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-account-list.html">
@@ -33,6 +35,16 @@ limitations under the License.
</test-fixture>
<script>
+ class MockSuggestionsProvider {
+ getSuggestions(input) {
+ return Promise.resolve([]);
+ }
+
+ makeSuggestionItem(item) {
+ return item;
+ }
+ }
+
suite('gr-account-list tests', () => {
let _nextAccountId = 0;
const makeAccount = function() {
@@ -49,10 +61,11 @@ limitations under the License.
};
};
- let existingReviewer1;
- let existingReviewer2;
+ let existingAccount1;
+ let existingAccount2;
let sandbox;
let element;
+ let suggestionsProvider;
function getChips() {
return Polymer.dom(element.root).querySelectorAll('gr-account-chip');
@@ -60,14 +73,16 @@ limitations under the License.
setup(() => {
sandbox = sinon.sandbox.create();
- existingReviewer1 = makeAccount();
- existingReviewer2 = makeAccount();
+ existingAccount1 = makeAccount();
+ existingAccount2 = makeAccount();
stub('gr-rest-api-interface', {
getConfig() { return Promise.resolve({}); },
});
element = fixture('basic');
- element.accounts = [existingReviewer1, existingReviewer2];
+ element.accounts = [existingAccount1, existingAccount2];
+ suggestionsProvider = new MockSuggestionsProvider();
+ element.suggestionsProvider = suggestionsProvider;
});
teardown(() => {
@@ -107,7 +122,7 @@ limitations under the License.
assert.isTrue(chips[2].classList.contains('pendingAdd'));
// Removed accounts are taken out of the list.
- element.fire('remove', {account: existingReviewer1});
+ element.fire('remove', {account: existingAccount1});
flushAsynchronousOperations();
chips = getChips();
assert.equal(chips.length, 2);
@@ -115,7 +130,7 @@ limitations under the License.
assert.isTrue(chips[1].classList.contains('pendingAdd'));
// Invalid remove is ignored.
- element.fire('remove', {account: existingReviewer1});
+ element.fire('remove', {account: existingAccount1});
element.fire('remove', {account: newAccount});
flushAsynchronousOperations();
chips = getChips();
@@ -145,6 +160,52 @@ limitations under the License.
assert.isFalse(chips[0].classList.contains('pendingAdd'));
});
+ test('_getSuggestions uses filter correctly', done => {
+ const originalSuggestions = [
+ {
+ email: 'abc@example.com',
+ text: 'abcd',
+ _account_id: 3,
+ },
+ {
+ email: 'qwe@example.com',
+ text: 'qwer',
+ _account_id: 1,
+ },
+ {
+ email: 'xyz@example.com',
+ text: 'aaaaa',
+ _account_id: 25,
+ },
+ ];
+ sandbox.stub(suggestionsProvider, 'getSuggestions')
+ .returns(Promise.resolve(originalSuggestions));
+ sandbox.stub(suggestionsProvider, 'makeSuggestionItem', suggestion => {
+ return {
+ name: suggestion.email,
+ value: suggestion._account_id,
+ };
+ });
+
+
+ element._getSuggestions().then(suggestions => {
+ // Default is no filtering.
+ assert.equal(suggestions.length, 3);
+
+ // Set up filter that only accepts suggestion1.
+ const accountId = originalSuggestions[0]._account_id;
+ element.filter = function(suggestion) {
+ return suggestion._account_id === accountId;
+ };
+
+ element._getSuggestions().then(suggestions => {
+ assert.deepEqual(suggestions,
+ [{name: originalSuggestions[0].email,
+ value: originalSuggestions[0]._account_id}]);
+ }).then(done);
+ });
+ });
+
test('_computeChipClass', () => {
const account = makeAccount();
assert.equal(element._computeChipClass(account), '');
@@ -161,18 +222,18 @@ limitations under the License.
newAccount._pendingAdd = true;
element.readonly = false;
element.removableValues = [];
- assert.isFalse(element._computeRemovable(existingReviewer1));
- assert.isTrue(element._computeRemovable(newAccount));
+ assert.isFalse(element._computeRemovable(existingAccount1, false));
+ assert.isTrue(element._computeRemovable(newAccount, false));
- element.removableValues = [existingReviewer1];
- assert.isTrue(element._computeRemovable(existingReviewer1));
- assert.isTrue(element._computeRemovable(newAccount));
- assert.isFalse(element._computeRemovable(existingReviewer2));
+ element.removableValues = [existingAccount1];
+ assert.isTrue(element._computeRemovable(existingAccount1, false));
+ assert.isTrue(element._computeRemovable(newAccount, false));
+ assert.isFalse(element._computeRemovable(existingAccount2, false));
element.readonly = true;
- assert.isFalse(element._computeRemovable(existingReviewer1));
- assert.isFalse(element._computeRemovable(newAccount));
+ assert.isFalse(element._computeRemovable(existingAccount1, true));
+ assert.isFalse(element._computeRemovable(newAccount, true));
});
test('submitEntryText', () => {
@@ -291,13 +352,40 @@ limitations under the License.
assert.isTrue(element.$.entry.hasAttribute('hidden'));
});
- suite('allowAnyInput', () => {
- let entry;
+ test('enter text calls suggestions provider', done => {
+ const suggestions = [
+ {
+ email: 'abc@example.com',
+ text: 'abcd',
+ },
+ {
+ email: 'qwe@example.com',
+ text: 'qwer',
+ },
+ ];
+ const getSuggestionsStub =
+ sandbox.stub(suggestionsProvider, 'getSuggestions')
+ .returns(Promise.resolve(suggestions));
+
+ const makeSuggestionItemStub =
+ sandbox.stub(suggestionsProvider, 'makeSuggestionItem', item => item);
+
+ const input = element.$.entry.$.input;
+ input.text = 'newTest';
+ MockInteractions.focus(input.$.input);
+ input.noDebounce = true;
+ flushAsynchronousOperations();
+ flush(() => {
+ assert.isTrue(getSuggestionsStub.calledOnce);
+ assert.equal(getSuggestionsStub.lastCall.args[0], 'newTest');
+ assert.equal(makeSuggestionItemStub.getCalls().length, 2);
+ done();
+ });
+ });
+
+ suite('allowAnyInput', () => {
setup(() => {
- entry = element.$.entry;
- sandbox.stub(entry, '_getReviewerSuggestions');
- sandbox.stub(entry.$.input, '_updateSuggestions');
element.allowAnyInput = true;
});
@@ -330,44 +418,51 @@ limitations under the License.
});
suite('keyboard interactions', () => {
- test('backspace at text input start removes last account', () => {
+ test('backspace at text input start removes last account', done => {
const input = element.$.entry.$.input;
- sandbox.stub(element.$.entry, '_getReviewerSuggestions');
sandbox.stub(input, '_updateSuggestions');
sandbox.stub(element, '_computeRemovable').returns(true);
- // Next line is a workaround for Firefix not moving cursor
- // on input field update
- assert.equal(input.$.input.inputElement.selectionStart, 0);
- input.text = 'test';
- MockInteractions.focus(input.$.input);
- flushAsynchronousOperations();
- assert.equal(element.accounts.length, 2);
- MockInteractions.pressAndReleaseKeyOn(
- input.$.input.inputElement, 8); // Backspace
- assert.equal(element.accounts.length, 2);
- input.text = '';
- MockInteractions.pressAndReleaseKeyOn(
- input.$.input.inputElement, 8); // Backspace
- assert.equal(element.accounts.length, 1);
+ flush(() => {
+ // Next line is a workaround for Firefix not moving cursor
+ // on input field update
+ assert.equal(
+ element._getNativeInput(input.$.input).selectionStart, 0);
+ input.text = 'test';
+ MockInteractions.focus(input.$.input);
+ flushAsynchronousOperations();
+ assert.equal(element.accounts.length, 2);
+ MockInteractions.pressAndReleaseKeyOn(
+ element._getNativeInput(input.$.input), 8); // Backspace
+ assert.equal(element.accounts.length, 2);
+ input.text = '';
+ MockInteractions.pressAndReleaseKeyOn(
+ element._getNativeInput(input.$.input), 8); // Backspace
+ flushAsynchronousOperations();
+ assert.equal(element.accounts.length, 1);
+ done();
+ });
});
- test('arrow key navigation', () => {
+ test('arrow key navigation', done => {
const input = element.$.entry.$.input;
input.text = '';
element.accounts = [makeAccount(), makeAccount()];
- MockInteractions.focus(input.$.input);
- flushAsynchronousOperations();
- const chips = element.accountChips;
- const chipsOneSpy = sandbox.spy(chips[1], 'focus');
- MockInteractions.pressAndReleaseKeyOn(input.$.input, 37); // Left
- assert.isTrue(chipsOneSpy.called);
- const chipsZeroSpy = sandbox.spy(chips[0], 'focus');
- MockInteractions.pressAndReleaseKeyOn(chips[1], 37); // Left
- assert.isTrue(chipsZeroSpy.called);
- MockInteractions.pressAndReleaseKeyOn(chips[0], 37); // Left
- assert.isTrue(chipsZeroSpy.calledOnce);
- MockInteractions.pressAndReleaseKeyOn(chips[0], 39); // Right
- assert.isTrue(chipsOneSpy.calledTwice);
+ flush(() => {
+ MockInteractions.focus(input.$.input);
+ flushAsynchronousOperations();
+ const chips = element.accountChips;
+ const chipsOneSpy = sandbox.spy(chips[1], 'focus');
+ MockInteractions.pressAndReleaseKeyOn(input.$.input, 37); // Left
+ assert.isTrue(chipsOneSpy.called);
+ const chipsZeroSpy = sandbox.spy(chips[0], 'focus');
+ MockInteractions.pressAndReleaseKeyOn(chips[1], 37); // Left
+ assert.isTrue(chipsZeroSpy.called);
+ MockInteractions.pressAndReleaseKeyOn(chips[0], 37); // Left
+ assert.isTrue(chipsZeroSpy.calledOnce);
+ MockInteractions.pressAndReleaseKeyOn(chips[0], 39); // Right
+ assert.isTrue(chipsOneSpy.calledTwice);
+ done();
+ });
});
test('delete', done => {
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
index b00fded1ab..b0018df74b 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../gr-button/gr-button.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -31,11 +31,10 @@ limitations under the License.
:host([toast]) {
background-color: var(--tooltip-background-color);
bottom: 1.25rem;
- border-radius: 3px;
+ border-radius: var(--border-radius);
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
color: var(--view-background-color);
left: 1.25rem;
- padding: 1em 1.5em;
position: fixed;
transform: translateY(5rem);
transition: transform var(--gr-alert-transition-duration, 80ms) ease-out;
@@ -44,6 +43,17 @@ limitations under the License.
:host([shown]) {
transform: translateY(0);
}
+ /**
+ * NOTE: To avoid style being overwritten by outside of the shadow DOM
+ * (as outside styles always win), .content-wrapper is introduced as a
+ * wrapper around main content to have better encapsulation, styles that
+ * may be affected by outside should be defined on it.
+ * In this case, `padding:0px` is defined in main.css for all elements
+ * with the universal selector: *.
+ */
+ .content-wrapper {
+ padding: var(--spacing-l) var(--spacing-xl);
+ }
.text {
color: var(--tooltip-text-color);
display: inline-block;
@@ -55,19 +65,21 @@ limitations under the License.
.action {
color: var(--link-color);
font-weight: var(--font-weight-bold);
- margin-left: 1em;
+ margin-left: var(--spacing-l);
text-decoration: none;
--gr-button: {
padding: 0;
}
}
</style>
- <span class="text">[[text]]</span>
- <gr-button
- link
- class="action"
- hidden$="[[_hideActionButton]]"
- on-tap="_handleActionTap">[[actionText]]</gr-button>
+ <div class="content-wrapper">
+ <span class="text">[[text]]</span>
+ <gr-button
+ link
+ class="action"
+ hidden$="[[_hideActionButton]]"
+ on-click="_handleActionTap">[[actionText]]</gr-button>
+ </div>
</template>
<script src="gr-alert.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
index ec7b6eb965..e7c8b2ce97 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-alert',
- _legacyUndefinedCheck: true,
/**
* Fired when the action button is pressed.
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
index 095e6407bd..2338d55247 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-alert</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-alert.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
index 7b82635462..92080682ab 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html
@@ -15,10 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="/bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="/bower_components/iron-fit-behavior/iron-fit-behavior.html">
<link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
<script src="../../../scripts/rootElement.js"></script>
<link rel="import" href="../../../styles/shared-styles.html">
@@ -40,7 +42,7 @@ limitations under the License.
cursor: pointer;
display: flex;
justify-content: space-between;
- padding: .5em .75em;
+ padding: var(--spacing-m) var(--spacing-l);
}
li:last-of-type {
border: none;
@@ -67,7 +69,7 @@ limitations under the License.
}
.label {
color: var(--deemphasized-text-color);
- padding-left: 1em;
+ padding-left: var(--spacing-l);
}
.hide {
display: none;
@@ -86,7 +88,7 @@ limitations under the License.
aria-label$="[[item.name]]"
class="autocompleteOption"
role="option"
- on-tap="_handleTapItem">
+ on-click="_handleClickItem">
<span>[[item.text]]</span>
<span class$="label [[_computeLabelClass(item)]]">[[item.label]]</span>
</li>
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
index 1af629d246..b8c76ff1f8 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-autocomplete-dropdown',
- _legacyUndefinedCheck: true,
/**
* Fired when the dropdown is closed.
@@ -53,13 +52,11 @@
value: () => [],
observer: '_resetCursorStops',
},
- _suggestionEls: {
- type: Array,
- observer: '_resetCursorIndex',
- },
+ _suggestionEls: Array,
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
Polymer.IronFitBehavior,
],
@@ -78,9 +75,9 @@
open() {
this.isHidden = false;
- this.refit();
this._resetCursorStops();
- this._resetCursorIndex();
+ // Refit should run after we call Polymer.flush inside _resetCursorStops
+ this.refit();
},
getCurrentText() {
@@ -138,7 +135,7 @@
this.close();
},
- _handleTapItem(e) {
+ _handleClickItem(e) {
e.preventDefault();
e.stopPropagation();
let selected = e.target;
@@ -147,7 +144,7 @@
selected = selected.parentElement;
}
this.fire('item-selected', {
- trigger: 'tap',
+ trigger: 'click',
selected,
});
},
@@ -162,10 +159,12 @@
_resetCursorStops() {
if (this.suggestions.length > 0) {
- Polymer.dom.flush();
- // Polymer2: querySelectorAll returns NodeList instead of Array.
- this._suggestionEls = Array.from(
- this.$.suggestions.querySelectorAll('li'));
+ if (!this.isHidden) {
+ Polymer.dom.flush();
+ this._suggestionEls = Array.from(
+ this.$.suggestions.querySelectorAll('li'));
+ this._resetCursorIndex();
+ }
} else {
this._suggestionEls = [];
}
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
index d4d54ffcf6..a7b59d72ff 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-autocomplete-dropdown</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-autocomplete-dropdown.html">
@@ -126,7 +128,7 @@ limitations under the License.
MockInteractions.tap(element.$.suggestions.querySelectorAll('li')[1]);
flushAsynchronousOperations();
assert.deepEqual(itemSelectedStub.lastCall.args[0].detail, {
- trigger: 'tap',
+ trigger: 'click',
selected: element.$.suggestions.querySelectorAll('li')[1],
});
});
@@ -139,7 +141,7 @@ limitations under the License.
.lastElementChild);
flushAsynchronousOperations();
assert.deepEqual(itemSelectedStub.lastCall.args[0].detail, {
- trigger: 'tap',
+ trigger: 'click',
selected: element.$.suggestions.querySelectorAll('li')[0],
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
index a878174469..c9d12ce81a 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
@@ -14,8 +14,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/paper-input/paper-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/paper-input/paper-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html">
<link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
@@ -32,21 +33,23 @@ limitations under the License.
display: inline-block;
}
iron-icon {
- margin: 0 .25em;
+ margin: 0 var(--spacing-xs);
+ vertical-align: top;
}
paper-input:not(.borderless) {
border: 1px solid var(--border-color);
}
paper-input {
- height: 100%;
+ height: var(--line-height-normal);
width: 100%;
@apply --gr-autocomplete;
--paper-input-container: {
padding: 0;
- }
+ };
--paper-input-container-input: {
font-size: var(--font-size-normal);
- }
+ line-height: var(--line-height-normal);
+ };
--paper-input-container-underline: {
display: none;
};
@@ -60,7 +63,7 @@ limitations under the License.
paper-input.warnUncommitted {
--paper-input-container-input: {
color: var(--error-text-color);
- font-size: var(--font-size-normal);
+ font-size: inherit;
}
}
</style>
@@ -75,14 +78,14 @@ limitations under the License.
on-focus="_onInputFocus"
on-blur="_onInputBlur"
autocomplete="off">
- <!-- slot is for future use (2.x) while prefix attribute is for 1.x
- (current) -->
- <iron-icon
+
+ <!-- prefix as attribute is required to for polymer 1 -->
+ <div slot="prefix" prefix>
+ <iron-icon
icon="gr-icons:search"
- slot="prefix"
- prefix
class$="searchIcon [[_computeShowSearchIconClass(showSearchIcon)]]">
- </iron-icon>
+ </iron-icon>
+ </div>
</paper-input>
<gr-autocomplete-dropdown
vertical-align="top"
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
index 9d16b9c63e..ee087cc6e4 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-autocomplete',
- _legacyUndefinedCheck: true,
/**
* Fired when a value is chosen.
@@ -168,6 +167,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
],
@@ -176,12 +176,17 @@
'_updateSuggestions(text, threshold, noDebounce)',
],
+ get _nativeInput() {
+ // In Polymer 2 inputElement isn't nativeInput anymore
+ return this.$.input.$.nativeInput || this.$.input.inputElement;
+ },
+
attached() {
- this.listen(document.body, 'tap', '_handleBodyTap');
+ this.listen(document.body, 'click', '_handleBodyClick');
},
detached() {
- this.unlisten(document.body, 'tap', '_handleBodyTap');
+ this.unlisten(document.body, 'click', '_handleBodyClick');
this.cancelDebouncer('update-suggestions');
},
@@ -190,11 +195,11 @@
},
focus() {
- this.$.input.focus();
+ this._nativeInput.focus();
},
selectAll() {
- const nativeInputElement = this.$.input.inputElement;
+ const nativeInputElement = this._nativeInput;
if (!this.$.input.value) { return; }
nativeInputElement.setSelectionRange(0, this.$.input.value.length);
},
@@ -205,7 +210,7 @@
_handleItemSelect(e) {
// Let _handleKeydown deal with keyboard interaction.
- if (e.detail.trigger !== 'tap') { return; }
+ if (e.detail.trigger !== 'click') { return; }
this._selected = e.detail.selected;
this._commit();
},
@@ -242,8 +247,13 @@
},
_updateSuggestions(text, threshold, noDebounce) {
+ // Polymer 2: check for undefined
+ if ([text, threshold, noDebounce].some(arg => arg === undefined)) {
+ return;
+ }
+
if (this._disableSuggestions) { return; }
- if (text === undefined || text.length < threshold) {
+ if (text.length < threshold) {
this._suggestions = [];
this.value = '';
return;
@@ -320,7 +330,7 @@
default:
// For any normal keypress, return focus to the input to allow for
// unbroken user input.
- this.$.input.inputElement.focus();
+ this.focus();
// Since this has been a normal keypress, the suggestions will have
// been based on a previous input. Clear them. This prevents an
@@ -365,7 +375,7 @@
}
},
- _handleBodyTap(e) {
+ _handleBodyClick(e) {
const eventPath = Polymer.dom(e).path;
for (let i = 0; i < eventPath.length; i++) {
if (eventPath[i] === this) {
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
index 1a76f98260..ea1fd503b1 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-reviewer-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-autocomplete.html">
@@ -80,16 +82,19 @@ limitations under the License.
});
});
- test('selectAll', () => {
- const nativeInput = element.$.input.inputElement;
- const selectionStub = sandbox.stub(nativeInput, 'setSelectionRange');
+ test('selectAll', done => {
+ flush(() => {
+ const nativeInput = element._nativeInput;
+ const selectionStub = sandbox.stub(nativeInput, 'setSelectionRange');
- element.selectAll();
- assert.isFalse(selectionStub.called);
+ element.selectAll();
+ assert.isFalse(selectionStub.called);
- element.$.input.value = 'test';
- element.selectAll();
- assert.isTrue(selectionStub.called);
+ element.$.input.value = 'test';
+ element.selectAll();
+ assert.isTrue(selectionStub.called);
+ done();
+ });
});
test('esc key behavior', done => {
@@ -318,12 +323,15 @@ limitations under the License.
});
});
- test('search icon shows with showSearchIcon property', () => {
- assert.equal(getComputedStyle(element.$$('iron-icon')).display,
- 'none');
- element.showSearchIcon = true;
- assert.notEqual(getComputedStyle(element.$$('iron-icon')).display,
- 'none');
+ test('search icon shows with showSearchIcon property', done => {
+ flush(() => {
+ assert.equal(getComputedStyle(element.$$('iron-icon')).display,
+ 'none');
+ element.showSearchIcon = true;
+ assert.notEqual(getComputedStyle(element.$$('iron-icon')).display,
+ 'none');
+ done();
+ });
});
test('vertical offset overridden by param if it exists', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
index bc63acf511..1daffa2323 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
@@ -28,7 +28,7 @@ limitations under the License.
display: inline-block;
border-radius: 50%;
background-size: cover;
- background-color: var(--background-color, #f1f2f3);
+ background-color: var(--avatar-background-color, #f1f2f3);
}
</style>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
index 2435e587bc..bf563823ac 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-avatar',
- _legacyUndefinedCheck: true,
properties: {
account: {
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
index 63718c533c..de05a5c041 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-avatar</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-avatar.html">
@@ -115,7 +117,7 @@ limitations under the License.
assert.strictEqual(element.style.backgroundImage, '');
// Emulate plugins loaded.
- Gerrit._setPluginsPending([]);
+ Gerrit._loadPlugins([]);
Promise.all([
element.$.restAPI.getConfig(),
@@ -127,65 +129,82 @@ limitations under the License.
element.style.backgroundImage.includes('/accounts/123/avatar?s=64'));
});
});
+ });
- test('dom for non available account', () => {
- assert.isFalse(element.hasAttribute('hidden'));
+ suite('plugin has avatars', () => {
+ let element;
+ let sandbox;
- sandbox.stub(element, '_getConfig', () => {
- return Promise.resolve({plugin: {has_avatars: true}});
- });
+ setup(() => {
+ sandbox = sinon.sandbox.create();
- // Emulate plugins loaded.
- Gerrit._setPluginsPending([]);
+ stub('gr-avatar', {
+ _getConfig: () => {
+ return Promise.resolve({plugin: {has_avatars: true}});
+ },
+ });
- return Promise.all([
- element.$.restAPI.getConfig(),
- Gerrit.awaitPluginsLoaded(),
- ]).then(() => {
- assert.isTrue(element.hasAttribute('hidden'));
+ element = fixture('basic');
+ });
- assert.strictEqual(element.style.backgroundImage, '');
- });
+ teardown(() => {
+ sandbox.restore();
});
- test('avatar config not set and account not set', () => {
+ test('dom for non available account', () => {
assert.isFalse(element.hasAttribute('hidden'));
- sandbox.stub(element, '_getConfig', () => {
- return Promise.resolve({});
- });
-
// Emulate plugins loaded.
- Gerrit._setPluginsPending([]);
+ Gerrit._loadPlugins([]);
return Promise.all([
element.$.restAPI.getConfig(),
Gerrit.awaitPluginsLoaded(),
]).then(() => {
assert.isTrue(element.hasAttribute('hidden'));
+
+ assert.strictEqual(element.style.backgroundImage, '');
});
});
+ });
- test('avatar config not set and account set', () => {
- assert.isFalse(element.hasAttribute('hidden'));
+ suite('config not set', () => {
+ let element;
+ let sandbox;
- sandbox.stub(element, '_getConfig', () => {
- return Promise.resolve({});
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+
+ stub('gr-avatar', {
+ _getConfig: () => {
+ return Promise.resolve({});
+ },
});
- element.imageSize = 64;
- element.account = {
- _account_id: 123,
- };
+ element = fixture('basic');
+ });
- // Emulate plugins loaded.
- Gerrit._setPluginsPending([]);
+ teardown(() => {
+ sandbox.restore();
+ });
- return Promise.all([
- element.$.restAPI.getConfig(),
- Gerrit.awaitPluginsLoaded(),
- ]).then(() => {
- assert.isTrue(element.hasAttribute('hidden'));
+ test('avatar hidden when account set', () => {
+ flush(() => {
+ assert.isFalse(element.hasAttribute('hidden'));
+
+ element.imageSize = 64;
+ element.account = {
+ _account_id: 123,
+ };
+ // Emulate plugins loaded.
+ Gerrit._loadPlugins([]);
+
+ return Promise.all([
+ element.$.restAPI.getConfig(),
+ Gerrit.awaitPluginsLoaded(),
+ ]).then(() => {
+ assert.isTrue(element.hasAttribute('hidden'));
+ });
});
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
index cdf617fbbd..87caf64522 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../bower_components/paper-button/paper-button.html">
+<link rel="import" href="/bower_components/paper-button/paper-button.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-button">
@@ -39,6 +39,37 @@ limitations under the License.
text-transform: none;
}
paper-button {
+ /* The next lines contains a copy of paper-button style.
+ Without a copy, the @apply works incorrectly with Polymer 2.
+ @apply is deprecated and is not recommended to use. It is expected
+ that @apply will be replaced with the ::part CSS pseudo-element.
+ After replacecment copied lines can be removed.
+ */
+ @apply --layout-inline;
+ @apply --layout-center-center;
+ position: relative;
+ box-sizing: border-box;
+ min-width: 5.14em;
+ margin: 0 0.29em;
+ background: transparent;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ font: inherit;
+ text-transform: uppercase;
+ outline-width: 0;
+ border-radius: var(--border-radius);
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ z-index: 0;
+ padding: var(--spacing-m);
+
+ @apply --paper-font-common-base;
+ @apply --paper-button;
+ /* End of copy*/
+
/* paper-button sets this to anti-aliased, which appears different than
bold font elsewhere on macOS. */
-webkit-font-smoothing: initial;
@@ -53,6 +84,24 @@ limitations under the License.
padding: var(--padding, 4px 8px);
@apply --gr-button;
}
+ /* https://github.com/PolymerElements/paper-button/blob/2.x/paper-button.html */
+ /* BEGIN: Copy from paper-button */
+ paper-button[elevation="1"] {
+ @apply --paper-material-elevation-1;
+ }
+ paper-button[elevation="2"] {
+ @apply --paper-material-elevation-2;
+ }
+ paper-button[elevation="3"] {
+ @apply --paper-material-elevation-3;
+ }
+ paper-button[elevation="4"] {
+ @apply --paper-material-elevation-4;
+ }
+ paper-button[elevation="5"] {
+ @apply --paper-material-elevation-5;
+ }
+ /* END: Copy from paper-button */
paper-button:hover {
background: linear-gradient(
rgba(0, 0, 0, .12),
@@ -91,6 +140,8 @@ limitations under the License.
}
:host([disabled][link]) {
--background-color: transparent;
+ --text-color: var(--deemphasized-text-color);
+ cursor: default;
}
/* Styles for the optional down arrow */
@@ -101,8 +152,8 @@ limitations under the License.
border-top: .36em solid #ccc;
border-left: .36em solid transparent;
border-right: .36em solid transparent;
- margin-bottom: .05em;
- margin-left: .5em;
+ margin-bottom: var(--spacing-xxs);
+ margin-left: var(--spacing-m);
transition: border-top-color 200ms;
}
:host([down-arrow]) paper-button:hover .downArrow {
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.js b/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
index 5988cdec88..afc6ba84d8 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-button',
- _legacyUndefinedCheck: true,
properties: {
tooltip: String,
@@ -53,7 +52,6 @@
},
listeners: {
- tap: '_handleAction',
click: '_handleAction',
keydown: '_handleKeydown',
},
@@ -81,7 +79,7 @@
_disabledChanged(disabled) {
if (disabled) {
- this._enabledTabindex = this.getAttribute('tabindex');
+ this._enabledTabindex = this.getAttribute('tabindex') || '0';
}
this.setAttribute('tabindex', disabled ? '-1' : this._enabledTabindex);
this.updateStyles();
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
index ed0da2e4ca..ef593c02ee 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-button</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-button.html">
@@ -39,7 +41,11 @@ limitations under the License.
const addSpyOn = function(eventName) {
const spy = sandbox.spy();
- element.addEventListener(eventName, spy);
+ if (eventName == 'tap') {
+ Polymer.Gestures.addListener(element, eventName, spy);
+ } else {
+ element.addEventListener(eventName, spy);
+ }
return spy;
};
@@ -62,6 +68,23 @@ limitations under the License.
assert.isTrue(element.$$('paper-button').disabled);
});
+ test('tabindex should be -1 if disabled', () => {
+ element.disabled = true;
+ assert.isTrue(element.getAttribute('tabindex') === '-1');
+ });
+
+ // Regression tests for Issue: 11969
+ test('tabindex should be reset to 0 if enabled', () => {
+ element.disabled = false;
+ assert.isTrue(element.getAttribute('tabindex') === '0');
+ element.disabled = true;
+ assert.isTrue(element.getAttribute('tabindex') === '-1');
+ element.disabled = false;
+ assert.isTrue(element.getAttribute('tabindex') === '0');
+ });
+
+ // 'tap' event is tested so we don't loose backward compatibility with older
+ // plugins who didn't move to on-click which is faster and well supported.
for (const eventName of ['tap', 'click']) {
test('dispatches ' + eventName + ' event', () => {
const spy = addSpyOn(eventName);
@@ -72,16 +95,16 @@ limitations under the License.
// Keycodes: 32 for Space, 13 for Enter.
for (const key of [32, 13]) {
- test('dispatches tap event on keycode ' + key, () => {
+ test('dispatches click event on keycode ' + key, () => {
const tapSpy = sandbox.spy();
- element.addEventListener('tap', tapSpy);
+ element.addEventListener('click', tapSpy);
MockInteractions.pressAndReleaseKeyOn(element, key);
assert.isTrue(tapSpy.calledOnce);
});
- test('dispatches no tap event with modifier on keycode ' + key, () => {
+ test('dispatches no click event with modifier on keycode ' + key, () => {
const tapSpy = sandbox.spy();
- element.addEventListener('tap', tapSpy);
+ element.addEventListener('click', tapSpy);
MockInteractions.pressAndReleaseKeyOn(element, key, 'shift');
MockInteractions.pressAndReleaseKeyOn(element, key, 'ctrl');
MockInteractions.pressAndReleaseKeyOn(element, key, 'meta');
@@ -105,9 +128,9 @@ limitations under the License.
// Keycodes: 32 for Space, 13 for Enter.
for (const key of [32, 13]) {
- test('stops tap event on keycode ' + key, () => {
+ test('stops click event on keycode ' + key, () => {
const tapSpy = sandbox.spy();
- element.addEventListener('tap', tapSpy);
+ element.addEventListener('click', tapSpy);
MockInteractions.pressAndReleaseKeyOn(element, key);
assert.isFalse(tapSpy.called);
});
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.html b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.html
index a14c652136..377452902b 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.html
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-icons/gr-icons.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -30,7 +30,7 @@ limitations under the License.
fill: var(--link-color);
}
</style>
- <button aria-label="Change star" on-tap="toggleStar">
+ <button aria-label="Change star" on-click="toggleStar">
<iron-icon
class$="[[_computeStarClass(change.starred)]]"
icon$="[[_computeStarIcon(change.starred)]]"></iron-icon>
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
index 44f8c00dbe..a83bc2bd4d 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-change-star',
- _legacyUndefinedCheck: true,
/**
* Fired when star state is toggled.
@@ -49,6 +48,7 @@
this.set('change.starred', newVal);
this.dispatchEvent(new CustomEvent('toggle-star', {
bubbles: true,
+ composed: true,
detail: {change: this.change, starred: newVal},
}));
},
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
index 0ca936861c..7ee22a7536 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-star</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-change-star.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html
index 99ddff1cc0..55623b36f0 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
@@ -25,11 +25,9 @@ limitations under the License.
<template>
<style include="shared-styles">
.chip {
- border-radius: 4px;
+ border-radius: var(--border-radius);
background-color: var(--chip-background-color);
- font-family: var(--font-family);
- font-size: var(--font-size-normal);
- padding: .1em .5em;
+ padding: var(--spacing-xxs) var(--spacing-m);
white-space: nowrap;
}
:host(.merged) .chip {
@@ -66,7 +64,7 @@ limitations under the License.
}
:host([flat]) .chip {
background-color: transparent;
- padding: .1em;
+ padding: var(--spacing-xxs);
}
:host(:not([flat])) .chip {
color: white;
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
index 70c5d729a2..e6f52c61a5 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
@@ -29,12 +29,15 @@
'It will not appear on dashboards unless you are CC\'ed or assigned, ' +
'and email notifications will be silenced until the review is started.';
+ const MERGE_CONFLICT_TOOLTIP = 'This change has merge conflicts. ' +
+ 'Download the patch and run "git rebase master". ' +
+ 'Upload a new patchset after resolving all merge conflicts.';
+
const PRIVATE_TOOLTIP = 'This change is only visible to its owner and ' +
'current reviewers (or anyone with "View Private Changes" permission).';
Polymer({
is: 'gr-change-status',
- _legacyUndefinedCheck: true,
properties: {
flat: {
@@ -76,6 +79,9 @@
case ChangeStates.PRIVATE:
this.tooltipText = PRIVATE_TOOLTIP;
break;
+ case ChangeStates.MERGE_CONFLICT:
+ this.tooltipText = MERGE_CONFLICT_TOOLTIP;
+ break;
default:
this.tooltipText = '';
break;
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html
index f73fc02d04..421c6ab589 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-status</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-change-status.html">
@@ -33,6 +35,17 @@ limitations under the License.
</test-fixture>
<script>
+ const WIP_TOOLTIP = 'This change isn\'t ready to be reviewed or submitted. ' +
+ 'It will not appear on dashboards unless you are CC\'ed or assigned, ' +
+ 'and email notifications will be silenced until the review is started.';
+
+ const MERGE_CONFLICT_TOOLTIP = 'This change has merge conflicts. ' +
+ 'Download the patch and run "git rebase master". ' +
+ 'Upload a new patchset after resolving all merge conflicts.';
+
+ const PRIVATE_TOOLTIP = 'This change is only visible to its owner and ' +
+ 'current reviewers (or anyone with "View Private Changes" permission).';
+
suite('gr-change-status tests', () => {
let element;
let sandbox;
@@ -49,7 +62,7 @@ limitations under the License.
test('WIP', () => {
element.status = 'WIP';
assert.equal(element.$$('.chip').innerText, 'Work in Progress');
- assert.isDefined(element.tooltipText);
+ assert.equal(element.tooltipText, WIP_TOOLTIP);
assert.isTrue(element.classList.contains('wip'));
});
@@ -79,14 +92,14 @@ limitations under the License.
test('merge conflict', () => {
element.status = 'Merge Conflict';
assert.equal(element.$$('.chip').innerText, element.status);
- assert.equal(element.tooltipText, '');
+ assert.equal(element.tooltipText, MERGE_CONFLICT_TOOLTIP);
assert.isTrue(element.classList.contains('merge-conflict'));
});
test('private', () => {
element.status = 'Private';
assert.equal(element.$$('.chip').innerText, element.status);
- assert.isDefined(element.tooltipText);
+ assert.equal(element.tooltipText, PRIVATE_TOOLTIP);
assert.isTrue(element.classList.contains('private'));
});
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
index 8c80b379a2..bbd7ddfd78 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
@@ -27,20 +28,32 @@ limitations under the License.
<dom-module id="gr-comment-thread">
<template>
<style include="shared-styles">
+ :host {
+ font-family: var(--font-family);
+ font-size: var(--font-size-normal);
+ font-weight: var(--font-weight-normal);
+ line-height: var(--line-height-normal);
+ }
gr-button {
- margin-left: .5em;
+ margin-left: var(--spacing-m);
}
#actions {
margin-left: auto;
- padding: .5em .7em;
+ padding: var(--spacing-m);
}
#container {
background-color: var(--comment-background-color);
- border: 1px solid var(--border-color);
color: var(--comment-text-color);
display: block;
- margin-bottom: 1px;
+ margin: 0 4px 4px 4px;
white-space: normal;
+ box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
+ border-radius: var(--border-radius);
+ /** This is required for firefox to continue the inheritance */
+ -webkit-user-select: inherit;
+ -moz-user-select: inherit;
+ -ms-user-select: inherit;
+ user-select: inherit;
}
#container.unresolved {
background-color: var(--unresolved-comment-background-color);
@@ -52,14 +65,14 @@ limitations under the License.
#unresolvedLabel {
font-family: var(--font-family);
margin: auto 0;
- padding: .5em .7em;
+ padding: var(--spacing-m);
}
.pathInfo {
display: flex;
align-items: baseline;
}
.descriptionText {
- margin-left: .5rem;
+ margin-left: var(--spacing-m);
font-style: italic;
}
</style>
@@ -96,25 +109,25 @@ limitations under the License.
link
secondary
class="action reply"
- on-tap="_handleCommentReply">Reply</gr-button>
+ on-click="_handleCommentReply">Reply</gr-button>
<gr-button
id="quoteBtn"
link
secondary
class="action quote"
- on-tap="_handleCommentQuote">Quote</gr-button>
+ on-click="_handleCommentQuote">Quote</gr-button>
<gr-button
id="ackBtn"
link
secondary
class="action ack"
- on-tap="_handleCommentAck">Ack</gr-button>
+ on-click="_handleCommentAck">Ack</gr-button>
<gr-button
id="doneBtn"
link
secondary
class="action done"
- on-tap="_handleCommentDone">Done</gr-button>
+ on-click="_handleCommentDone">Done</gr-button>
</div>
</div>
</div>
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
index 0b4f80604d..c220ecf2c8 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-comment-thread',
- _legacyUndefinedCheck: true,
/**
* Fired when the thread should be discarded.
@@ -127,6 +126,10 @@
},
behaviors: [
+ /**
+ * Not used in this element rather other elements tests
+ */
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.PathListBehavior,
],
@@ -209,7 +212,8 @@
},
_hideActions(_showActions, _lastComment) {
- return !_showActions || !_lastComment || !!_lastComment.__draft;
+ return !_showActions || !_lastComment || !!_lastComment.__draft ||
+ !!_lastComment.robot_id;
},
_getLastComment() {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html
index 74c6f5fb6a..d3946e66d0 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-comment-thread</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
@@ -159,6 +161,9 @@ limitations under the License.
showActions = true;
lastComment.__draft = true;
assert.equal(element._hideActions(showActions, lastComment), true);
+ const robotComment = {};
+ robotComment.robot_id = true;
+ assert.equal(element._hideActions(showActions, robotComment), true);
});
test('setting project name loads the project config', done => {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
index a470285fdc..8ad261c8ae 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
@@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
@@ -41,7 +42,7 @@ limitations under the License.
:host {
display: block;
font-family: var(--font-family);
- padding: .7em .7em;
+ padding: var(--spacing-m);
--iron-autogrow-textarea: {
box-sizing: border-box;
padding: 2px;
@@ -62,23 +63,17 @@ limitations under the License.
align-items: baseline;
cursor: pointer;
display: flex;
- font-family: 'Open Sans', sans-serif;
- margin: -.7em -.7em 0 -.7em;
- padding: .7em;
+ margin: calc(0px - var(--spacing-m)) calc(0px - var(--spacing-m)) 0 calc(0px - var(--spacing-m));
+ padding: var(--spacing-m);
}
.container.collapsed .header {
- margin-bottom: -.7em;
+ margin-bottom: calc(0 - var(--spacing-m));
}
.headerMiddle {
color: var(--deemphasized-text-color);
flex: 1;
overflow: hidden;
}
- .authorName,
- .draftLabel,
- .draftTooltip {
- font-weight: var(--font-weight-bold);
- }
.draftLabel,
.draftTooltip {
color: var(--deemphasized-text-color);
@@ -103,25 +98,33 @@ limitations under the License.
padding-top: 0;
}
.action {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
.robotActions {
display: flex;
justify-content: flex-start;
- padding-top: 0;
+ padding-top: var(--spacing-m);
+ border-top: 1px solid var(--border-color);
}
.robotActions .action {
/* Keep button text lined up with output text */
- margin-left: -.3rem;
- margin-right: 1em;
+ margin-left: -4px;
+ margin-right: var(--spacing-l);
}
.rightActions {
display: flex;
justify-content: flex-end;
}
+ .rightActions gr-button {
+ --gr-button: {
+ height: 20px;
+ padding: 0 var(--spacing-s);
+ color: var(--default-button-text-color);
+ }
+ }
.editMessage {
display: none;
- margin: .5em 0;
+ margin: var(--spacing-m) 0;
width: 100%;
}
.container:not(.draft) .actions .hideOnPublished {
@@ -155,38 +158,37 @@ limitations under the License.
display: block;
}
.show-hide {
- margin-left: .4em;
+ margin-left: var(--spacing-s);
}
.robotId {
color: var(--deemphasized-text-color);
- margin-bottom: .8em;
+ margin-bottom: var(--spacing-m);
margin-top: -.4em;
}
.robotIcon {
- margin-right: .2em;
+ margin-right: var(--spacing-xs);
/* because of the antenna of the robot, it looks off center even when it
is centered. artificially adjust margin to account for this. */
- margin-top: -.3em;
+ margin-top: -4px;
}
.runIdInformation {
- margin: .7em 0;
+ margin: var(--spacing-m) 0;
}
.robotRun {
- margin-left: .5em;
+ margin-left: var(--spacing-m);
}
.robotRunLink {
- margin-left: .5em;
+ margin-left: var(--spacing-m);
}
input.show-hide {
display: none;
}
label.show-hide {
- color: var(--comment-text-color);
cursor: pointer;
display: block;
- font-size: .8rem;
- height: 1.1em;
- margin-top: .1em;
+ }
+ label.show-hide iron-icon {
+ vertical-align: top;
}
#container .collapsedContent {
display: none;
@@ -231,9 +233,19 @@ limitations under the License.
#deleteBtn.showDeleteButtons {
display: block;
}
+
+ /** Disable select for the caret and actions */
+ .actions,
+ .show-hide {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
</style>
<div id="container" class="container">
- <div class="header" id="header" on-tap="_handleToggleCollapsed">
+ <div class="header" id="header" on-click="_handleToggleCollapsed">
<div class="headerLeft">
<span class="authorName">[[comment.author.name]]</span>
<span class="draftLabel">DRAFT</span>
@@ -251,10 +263,10 @@ limitations under the License.
link
secondary
class$="action delete [[_computeDeleteButtonClass(_isAdmin, draft)]]"
- on-tap="_handleCommentDelete">
+ on-click="_handleCommentDelete">
(Delete)
</gr-button>
- <span class="date" on-tap="_handleAnchorTap">
+ <span class="date" on-click="_handleAnchorClick">
<gr-date-formatter
has-tooltip
date-str="[[comment.updated]]"></gr-date-formatter>
@@ -264,7 +276,10 @@ limitations under the License.
<input type="checkbox" class="show-hide"
checked$="[[collapsed]]"
on-change="_handleToggleCollapsed">
- [[_computeShowHideText(collapsed)]]
+ <iron-icon
+ id="icon"
+ icon="[[_computeShowHideIcon(collapsed)]]">
+ </iron-icon>
</label>
</div>
</div>
@@ -280,7 +295,7 @@ limitations under the License.
id="editTextarea"
class="editMessage"
autocomplete="on"
- monospace
+ code
disabled="{{disabled}}"
rows="4"
text="{{_messageText}}"></gr-textarea>
@@ -320,23 +335,23 @@ limitations under the License.
link
secondary
class="action cancel hideOnPublished"
- on-tap="_handleCancel">Cancel</gr-button>
+ on-click="_handleCancel">Cancel</gr-button>
<gr-button
link
secondary
class="action discard hideOnPublished"
- on-tap="_handleDiscard">Discard</gr-button>
+ on-click="_handleDiscard">Discard</gr-button>
<gr-button
link
secondary
class="action edit hideOnPublished"
- on-tap="_handleEdit">Edit</gr-button>
+ on-click="_handleEdit">Edit</gr-button>
<gr-button
link
secondary
disabled$="[[_computeSaveDisabled(_messageText, comment, resolved)]]"
class="action save hideOnPublished"
- on-tap="_handleSave">Save</gr-button>
+ on-click="_handleSave">Save</gr-button>
</div>
</div>
<div class="robotActions" hidden$="[[!_showRobotActions]]">
@@ -345,7 +360,7 @@ limitations under the License.
link
secondary
class="action fix"
- on-tap="_handleFix"
+ on-click="_handleFix"
disabled="[[robotButtonDisabled]]">
Please Fix
</gr-button>
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
index b699b8b37d..68819297ed 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
@@ -33,7 +33,6 @@
Polymer({
is: 'gr-comment',
- _legacyUndefinedCheck: true,
/**
* Fired when the create fix comment action is triggered.
@@ -128,7 +127,8 @@
_numPendingDraftRequests: {
type: Object,
- value: {number: 0}, // Intentional to share the object across instances.
+ value:
+ {number: 0}, // Intentional to share the object across instances.
},
_enableOverlay: {
@@ -156,6 +156,7 @@
],
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
],
@@ -204,11 +205,16 @@
return this._overlays.confirmDiscard;
},
- _computeShowHideText(collapsed) {
- return collapsed ? '◀' : '▼';
+ _computeShowHideIcon(collapsed) {
+ return collapsed ? 'gr-icons:expand-more' : 'gr-icons:expand-less';
},
_calculateActionstoShow(showActions, isRobotComment) {
+ // Polymer 2: check for undefined
+ if ([showActions, isRobotComment].some(arg => arg === undefined)) {
+ return;
+ }
+
this._showHumanActions = showActions && !isRobotComment;
this._showRobotActions = showActions && isRobotComment;
},
@@ -230,7 +236,9 @@
*/
save(opt_comment) {
let comment = opt_comment;
- if (!comment) { comment = this.comment; }
+ if (!comment) {
+ comment = this.comment;
+ }
this.set('comment.message', this._messageText);
this.editing = false;
@@ -315,6 +323,11 @@
},
_editingChanged(editing, previousValue) {
+ // Polymer 2: observer fires when at least one property is defined.
+ // Do nothing to prevent comment.__editing being overwritten
+ // if previousValue is undefined
+ if (previousValue === undefined) return;
+
this.$.container.classList.toggle('editing', editing);
if (this.comment && this.comment.id) {
this.$$('.cancel').hidden = !editing;
@@ -340,7 +353,9 @@
_computeSaveDisabled(draft, comment, resolved) {
// If resolved state has changed and a msg exists, save should be enabled.
- if (comment.unresolved === resolved && draft) { return false; }
+ if (!comment || comment.unresolved === resolved && draft) {
+ return false;
+ }
return !draft || draft.trim() === '';
},
@@ -376,7 +391,9 @@
},
_messageTextChanged(newValue, oldValue) {
- if (!this.comment || (this.comment && this.comment.id)) { return; }
+ if (!this.comment || (this.comment && this.comment.id)) {
+ return;
+ }
this.debounce('store', () => {
const message = this._messageText;
@@ -398,11 +415,14 @@
}, STORAGE_DEBOUNCE_INTERVAL);
},
- _handleAnchorTap(e) {
+ _handleAnchorClick(e) {
e.preventDefault();
- if (!this.comment.line) { return; }
+ if (!this.comment.line) {
+ return;
+ }
this.dispatchEvent(new CustomEvent('comment-anchor-tap', {
bubbles: true,
+ composed: true,
detail: {
number: this.comment.line || FILE,
side: this.side,
@@ -421,7 +441,9 @@
e.preventDefault();
// Ignore saves started while already saving.
- if (this.disabled) { return; }
+ if (this.disabled) {
+ return;
+ }
const timingLabel = this.comment.id ?
REPORT_UPDATE_DRAFT : REPORT_CREATE_DRAFT;
const timer = this.$.reporting.getTimer(timingLabel);
@@ -450,6 +472,7 @@
_handleFix() {
this.dispatchEvent(new CustomEvent('create-fix-comment', {
bubbles: true,
+ composed: true,
detail: this._getEventPayload(),
}));
},
@@ -512,7 +535,9 @@
},
_getSavingMessage(numPending) {
- if (numPending === 0) { return SAVED_MESSAGE; }
+ if (numPending === 0) {
+ return SAVED_MESSAGE;
+ }
return [
SAVING_MESSAGE,
numPending,
@@ -544,8 +569,8 @@
// Note: the event is fired on the body rather than this element because
// this element may not be attached by the time this executes, in which
// case the event would not bubble.
- document.body.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message}, bubbles: true}));
+ document.body.dispatchEvent(new CustomEvent(
+ 'show-alert', {detail: {message}, bubbles: true, composed: true}));
}, TOAST_DEBOUNCE_INTERVAL);
},
@@ -580,6 +605,11 @@
},
_loadLocalDraft(changeNum, patchNum, comment) {
+ // Polymer 2: check for undefined
+ if ([changeNum, patchNum, comment].some(arg => arg === undefined)) {
+ return;
+ }
+
// Only apply local drafts to comments that haven't been saved
// remotely, and haven't been given a default message already.
//
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html
index 7ca524291a..c829343bc2 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-comment</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/page/page.js"></script>
+<script src="/bower_components/page/page.js"></script>
<script src="../../../scripts/util.js"></script>
<link rel="import" href="gr-comment.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
index 9decfa9563..62ab3072a1 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html
@@ -15,8 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -42,6 +43,8 @@ limitations under the License.
}
iron-autogrow-textarea {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
padding: 0;
width: 73ch; /* Add a char to account for the border. */
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
index 4ac059dd2e..c2075f0025 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-confirm-delete-comment-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -37,17 +36,23 @@
message: String,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
resetFocus() {
this.$.messageInput.textarea.focus();
},
_handleConfirmTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('confirm', {reason: this.message}, {bubbles: false});
},
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('cancel', null, {bubbles: false});
},
});
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.html b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.html
index 32ca557345..f58db39ff4 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.html
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-icons/gr-icons.html">
@@ -31,36 +31,53 @@ limitations under the License.
}
.copyText {
flex-grow: 1;
- margin-right: .3em;
+ margin-right: var(--spacing-s);
}
.hideInput {
display: none;
}
- input {
+ input#input {
font-family: var(--monospace-font-family);
- font-size: inherit;
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
@apply --text-container-style;
+ width: 100%;
}
#icon {
height: 1.2em;
width: 1.2em;
}
+ gr-button {
+ --gr-button: {
+ padding: 1px 4px;
+ }
+ }
+
</style>
<div class="text">
- <input id="input" is="iron-input"
- class$="copyText [[_computeInputClass(hideInput)]]"
+ <iron-input
+ class="copyText"
+ type="text"
+ bind-value="[[text]]"
+ on-tap="_handleInputClick"
+ readonly>
+ <input
+ id="input"
+ is="iron-input"
+ class$="[[_computeInputClass(hideInput)]]"
type="text"
bind-value="[[text]]"
- on-tap="_handleInputTap"
+ on-click="_handleInputClick"
readonly>
- <gr-button id="button"
- link
- has-tooltip="[[hasTooltip]]"
- class="copyToClipboard"
- title="[[buttonTitle]]"
- on-tap="_copyToClipboard">
- <iron-icon id="icon" icon="gr-icons:content-copy"></iron-icon>
- </gr-button>
+ </iron-input>
+ <gr-button id="button"
+ link
+ has-tooltip="[[hasTooltip]]"
+ class="copyToClipboard"
+ title="[[buttonTitle]]"
+ on-click="_copyToClipboard">
+ <iron-icon id="icon" icon="gr-icons:content-copy"></iron-icon>
+ </gr-button>
</div>
</template>
<script src="gr-copy-clipboard.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
index 550f1df1e7..3e872028dd 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-copy-clipboard',
- _legacyUndefinedCheck: true,
properties: {
text: String,
@@ -44,7 +43,7 @@
return hideInput ? 'hideInput' : '';
},
- _handleInputTap(e) {
+ _handleInputClick(e) {
e.preventDefault();
Polymer.dom(e).rootTarget.select();
},
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html
index d6e9dca4d8..9cec20ecae 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-copy-clipboard</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-copy-clipboard.html">
@@ -37,12 +39,13 @@ limitations under the License.
let element;
let sandbox;
- setup(() => {
+ setup(done => {
sandbox = sinon.sandbox.create();
element = fixture('basic');
element.text = `git fetch http://gerrit@localhost:8080/a/test-project
refs/changes/05/5/1 && git checkout FETCH_HEAD`;
flushAsynchronousOperations();
+ flush(done);
});
teardown(() => {
@@ -62,7 +65,7 @@ limitations under the License.
element.$$('.copyToClipboard'));
});
- test('_handleInputTap', () => {
+ test('_handleInputClick', () => {
const inputElement = element.$$('input');
MockInteractions.tap(inputElement);
assert.equal(inputElement.selectionStart, 0);
diff --git a/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html b/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html
index e4d896bd4d..d061ac2bae 100644
--- a/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-count-string-formatter/gr-count-string-formatter_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-count-string-formatter</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-count-string-formatter.html"/>
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.html b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.html
index d619b1883b..94d7aaa6c9 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.html
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-cursor-manager">
<template></template>
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
index 766ac7fe84..b97726e9ae 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
@@ -24,7 +24,6 @@
Polymer({
is: 'gr-cursor-manager',
- _legacyUndefinedCheck: true,
properties: {
stops: {
@@ -92,8 +91,22 @@
this.unsetCursor();
},
- next(opt_condition, opt_getTargetHeight) {
- this._moveCursor(1, opt_condition, opt_getTargetHeight);
+ /**
+ * Move the cursor forward. Clipped to the ends of the stop list.
+ *
+ * @param {!Function=} opt_condition Optional stop condition. If a condition
+ * is passed the cursor will continue to move in the specified direction
+ * until the condition is met.
+ * @param {!Function=} opt_getTargetHeight Optional function to calculate the
+ * height of the target's 'section'. The height of the target itself is
+ * sometimes different, used by the diff cursor.
+ * @param {boolean=} opt_clipToTop When none of the next indices match, move
+ * back to first instead of to last.
+ * @private
+ */
+
+ next(opt_condition, opt_getTargetHeight, opt_clipToTop) {
+ this._moveCursor(1, opt_condition, opt_getTargetHeight, opt_clipToTop);
},
previous(opt_condition) {
@@ -148,8 +161,8 @@
},
/**
- * Move the cursor forward or backward by delta. Noop if moving past either
- * end of the stop list.
+ * Move the cursor forward or backward by delta. Clipped to the beginning or
+ * end of stop list.
*
* @param {number} delta either -1 or 1.
* @param {!Function=} opt_condition Optional stop condition. If a condition
@@ -158,9 +171,11 @@
* @param {!Function=} opt_getTargetHeight Optional function to calculate the
* height of the target's 'section'. The height of the target itself is
* sometimes different, used by the diff cursor.
+ * @param {boolean=} opt_clipToTop When none of the next indices match, move
+ * back to first instead of to last.
* @private
*/
- _moveCursor(delta, opt_condition, opt_getTargetHeight) {
+ _moveCursor(delta, opt_condition, opt_getTargetHeight, opt_clipToTop) {
if (!this.stops.length) {
this.unsetCursor();
return;
@@ -168,7 +183,7 @@
this._unDecorateTarget();
- const newIndex = this._getNextindex(delta, opt_condition);
+ const newIndex = this._getNextindex(delta, opt_condition, opt_clipToTop);
let newTarget = null;
if (newIndex !== -1) {
@@ -208,10 +223,12 @@
*
* @param {number} delta either -1 or 1.
* @param {!Function=} opt_condition Optional stop condition.
+ * @param {boolean=} opt_clipToTop When none of the next indices match, move
+ * back to first instead of to last.
* @return {number} the new index.
* @private
*/
- _getNextindex(delta, opt_condition) {
+ _getNextindex(delta, opt_condition, opt_clipToTop) {
if (!this.stops.length || this.index === -1) {
return -1;
}
@@ -227,10 +244,10 @@
// If we failed to satisfy the condition:
if (opt_condition && !opt_condition(this.stops[newIndex])) {
- if (delta > 0) {
- return this.stops.length - 1;
- } else if (delta < 0) {
+ if (delta < 0 || opt_clipToTop) {
return 0;
+ } else if (delta > 0) {
+ return this.stops.length - 1;
}
return this.index;
}
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
index adbe6181d3..0793ccde3a 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-cursor-manager</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-cursor-manager.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
index 481dd2f912..ae5a9451c6 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -31,7 +31,7 @@ limitations under the License.
}
</style>
<span>
- [[_computeDateStr(dateStr, _timeFormat, _relative, showDateAndTime)]]
+ [[_computeDateStr(dateStr, _timeFormat, _dateFormat, _relative, showDateAndTime)]]
</span>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
index a9ce4c9121..545a7c33d3 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
@@ -27,13 +27,33 @@
TIME_12_WITH_SEC: 'h:mm:ss A', // 2:14:00 PM
TIME_24: 'HH:mm', // 14:14
TIME_24_WITH_SEC: 'HH:mm:ss', // 14:14:00
- MONTH_DAY: 'MMM DD', // Aug 29
- MONTH_DAY_YEAR: 'MMM DD, YYYY', // Aug 29, 1997
+ };
+
+ const DateFormats = {
+ STD: {
+ short: 'MMM DD', // Aug 29
+ full: 'MMM DD, YYYY', // Aug 29, 1997
+ },
+ US: {
+ short: 'MM/DD', // 08/29
+ full: 'MM/DD/YY', // 08/29/97
+ },
+ ISO: {
+ short: 'MM-DD', // 08-29
+ full: 'YYYY-MM-DD', // 1997-08-29
+ },
+ EURO: {
+ short: 'DD. MMM', // 29. Aug
+ full: 'DD.MM.YYYY', // 29.08.1997
+ },
+ UK: {
+ short: 'DD/MM', // 29/08
+ full: 'DD/MM/YYYY', // 29/08/1997
+ },
};
Polymer({
is: 'gr-date-formatter',
- _legacyUndefinedCheck: true,
properties: {
dateStr: {
@@ -58,9 +78,11 @@
title: {
type: String,
reflectToAttribute: true,
- computed: '_computeFullDateStr(dateStr, _timeFormat)',
+ computed: '_computeFullDateStr(dateStr, _timeFormat, _dateFormat)',
},
+ /** @type {?{short: string, full: string}} */
+ _dateFormat: Object,
_timeFormat: String, // No default value to prevent flickering.
_relative: Boolean, // No default value to prevent flickering.
},
@@ -81,6 +103,7 @@
return this._getLoggedIn().then(loggedIn => {
if (!loggedIn) {
this._timeFormat = TimeFormats.TIME_24;
+ this._dateFormat = DateFormats.STD;
this._relative = false;
return;
}
@@ -94,19 +117,47 @@
_loadTimeFormat() {
return this._getPreferences().then(preferences => {
const timeFormat = preferences && preferences.time_format;
- switch (timeFormat) {
- case 'HHMM_12':
- this._timeFormat = TimeFormats.TIME_12;
- break;
- case 'HHMM_24':
- this._timeFormat = TimeFormats.TIME_24;
- break;
- default:
- throw Error('Invalid time format: ' + timeFormat);
- }
+ const dateFormat = preferences && preferences.date_format;
+ this._decideTimeFormat(timeFormat);
+ this._decideDateFormat(dateFormat);
});
},
+ _decideTimeFormat(timeFormat) {
+ switch (timeFormat) {
+ case 'HHMM_12':
+ this._timeFormat = TimeFormats.TIME_12;
+ break;
+ case 'HHMM_24':
+ this._timeFormat = TimeFormats.TIME_24;
+ break;
+ default:
+ throw Error('Invalid time format: ' + timeFormat);
+ }
+ },
+
+ _decideDateFormat(dateFormat) {
+ switch (dateFormat) {
+ case 'STD':
+ this._dateFormat = DateFormats.STD;
+ break;
+ case 'US':
+ this._dateFormat = DateFormats.US;
+ break;
+ case 'ISO':
+ this._dateFormat = DateFormats.ISO;
+ break;
+ case 'EURO':
+ this._dateFormat = DateFormats.EURO;
+ break;
+ case 'UK':
+ this._dateFormat = DateFormats.UK;
+ break;
+ default:
+ throw Error('Invalid date format: ' + dateFormat);
+ }
+ },
+
_loadRelative() {
return this._getPreferences().then(prefs => {
// prefs.relative_date_in_change_table is not set when false.
@@ -139,8 +190,10 @@
diff < 180 * Duration.DAY;
},
- _computeDateStr(dateStr, timeFormat, relative, showDateAndTime) {
- if (!dateStr) { return ''; }
+ _computeDateStr(
+ dateStr, timeFormat, dateFormat, relative, showDateAndTime
+ ) {
+ if (!dateStr || !timeFormat || !dateFormat) { return ''; }
const date = moment(util.parseDate(dateStr));
if (!date.isValid()) { return ''; }
if (relative) {
@@ -152,12 +205,12 @@
}
}
const now = new Date();
- let format = TimeFormats.MONTH_DAY_YEAR;
+ let format = dateFormat.full;
if (this._isWithinDay(now, date)) {
format = timeFormat;
} else {
if (this._isWithinHalfYear(now, date)) {
- format = TimeFormats.MONTH_DAY;
+ format = dateFormat.short;
}
if (this.showDateAndTime) {
format = `${format} ${timeFormat}`;
@@ -172,11 +225,20 @@
TimeFormats.TIME_24_WITH_SEC;
},
- _computeFullDateStr(dateStr, timeFormat) {
+ _computeFullDateStr(dateStr, timeFormat, dateFormat) {
+ // Polymer 2: check for undefined
+ if ([
+ dateStr,
+ timeFormat,
+ dateFormat,
+ ].some(arg => arg === undefined)) {
+ return undefined;
+ }
+
if (!dateStr) { return ''; }
const date = moment(util.parseDate(dateStr));
if (!date.isValid()) { return ''; }
- let format = TimeFormats.MONTH_DAY_YEAR + ', ';
+ let format = dateFormat.full + ', ';
format += this._timeToSecondsFormat(timeFormat);
return date.format(format) + this._getUtcOffsetString();
},
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
index 798aa68b1d..d51b5d574f 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-date-formatter</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
@@ -84,16 +86,16 @@ limitations under the License.
return Promise.all([loggedInPromise, preferencesPromise]);
}
- suite('24 hours time format preference', () => {
- setup(() => {
- return stubRestAPI(
- {time_format: 'HHMM_24', relative_date_in_change_table: false}
- ).then(() => {
- element = fixture('basic');
- sandbox.stub(element, '_getUtcOffsetString').returns('');
- return element._loadPreferences();
- });
- });
+ suite('STD + 24 hours time format preference', () => {
+ setup(() => stubRestAPI({
+ time_format: 'HHMM_24',
+ date_format: 'STD',
+ relative_date_in_change_table: false,
+ }).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ }));
test('invalid dates are quietly rejected', () => {
assert.notOk((new Date('foo')).valueOf());
@@ -133,17 +135,161 @@ limitations under the License.
});
});
- suite('12 hours time format preference', () => {
- setup(() => {
+ suite('US + 24 hours time format preference', () => {
+ setup(() => stubRestAPI({
+ time_format: 'HHMM_24',
+ date_format: 'US',
+ relative_date_in_change_table: false,
+ }).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ }));
+
+ test('Within 24 hours on same day', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '15:34',
+ '15:34',
+ '07/29/15, 15:34:14', done);
+ });
+
+ test('Within 24 hours on different days', done => {
+ testDates('2015-07-29 03:34:14.985000000',
+ '2015-07-28 20:25:14.985000000',
+ '07/28',
+ '07/28 20:25',
+ '07/28/15, 20:25:14', done);
+ });
+
+ test('More than 24 hours but less than six months', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-06-15 03:25:14.985000000',
+ '06/15',
+ '06/15 03:25',
+ '06/15/15, 03:25:14', done);
+ });
+ });
+
+ suite('ISO + 24 hours time format preference', () => {
+ setup(() => stubRestAPI({
+ time_format: 'HHMM_24',
+ date_format: 'ISO',
+ relative_date_in_change_table: false,
+ }).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ }));
+
+ test('Within 24 hours on same day', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '15:34',
+ '15:34',
+ '2015-07-29, 15:34:14', done);
+ });
+
+ test('Within 24 hours on different days', done => {
+ testDates('2015-07-29 03:34:14.985000000',
+ '2015-07-28 20:25:14.985000000',
+ '07-28',
+ '07-28 20:25',
+ '2015-07-28, 20:25:14', done);
+ });
+
+ test('More than 24 hours but less than six months', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-06-15 03:25:14.985000000',
+ '06-15',
+ '06-15 03:25',
+ '2015-06-15, 03:25:14', done);
+ });
+ });
+
+ suite('EURO + 24 hours time format preference', () => {
+ setup(() => stubRestAPI({
+ time_format: 'HHMM_24',
+ date_format: 'EURO',
+ relative_date_in_change_table: false,
+ }).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ }));
+
+ test('Within 24 hours on same day', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '15:34',
+ '15:34',
+ '29.07.2015, 15:34:14', done);
+ });
+
+ test('Within 24 hours on different days', done => {
+ testDates('2015-07-29 03:34:14.985000000',
+ '2015-07-28 20:25:14.985000000',
+ '28. Jul',
+ '28. Jul 20:25',
+ '28.07.2015, 20:25:14', done);
+ });
+
+ test('More than 24 hours but less than six months', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-06-15 03:25:14.985000000',
+ '15. Jun',
+ '15. Jun 03:25',
+ '15.06.2015, 03:25:14', done);
+ });
+ });
+
+ suite('UK + 24 hours time format preference', () => {
+ setup(() => stubRestAPI({
+ time_format: 'HHMM_24',
+ date_format: 'UK',
+ relative_date_in_change_table: false,
+ }).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ }));
+
+ test('Within 24 hours on same day', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '15:34',
+ '15:34',
+ '29/07/2015, 15:34:14', done);
+ });
+
+ test('Within 24 hours on different days', done => {
+ testDates('2015-07-29 03:34:14.985000000',
+ '2015-07-28 20:25:14.985000000',
+ '28/07',
+ '28/07 20:25',
+ '28/07/2015, 20:25:14', done);
+ });
+
+ test('More than 24 hours but less than six months', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-06-15 03:25:14.985000000',
+ '15/06',
+ '15/06 03:25',
+ '15/06/2015, 03:25:14', done);
+ });
+ });
+
+ suite('STD + 12 hours time format preference', () => {
+ setup(() =>
// relative_date_in_change_table is not set when false.
- return stubRestAPI(
- {time_format: 'HHMM_12'}
+ stubRestAPI(
+ {time_format: 'HHMM_12', date_format: 'STD'}
).then(() => {
element = fixture('basic');
sandbox.stub(element, '_getUtcOffsetString').returns('');
return element._loadPreferences();
- });
- });
+ })
+ );
test('Within 24 hours on same day', done => {
testDates('2015-07-29 20:34:14.985000000',
@@ -154,16 +300,100 @@ limitations under the License.
});
});
- suite('relative date preference', () => {
- setup(() => {
- return stubRestAPI(
- {time_format: 'HHMM_12', relative_date_in_change_table: true}
+ suite('US + 12 hours time format preference', () => {
+ setup(() =>
+ // relative_date_in_change_table is not set when false.
+ stubRestAPI(
+ {time_format: 'HHMM_12', date_format: 'US'}
).then(() => {
element = fixture('basic');
sandbox.stub(element, '_getUtcOffsetString').returns('');
return element._loadPreferences();
- });
+ })
+ );
+
+ test('Within 24 hours on same day', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '3:34 PM',
+ '3:34 PM',
+ '07/29/15, 3:34:14 PM', done);
});
+ });
+
+ suite('ISO + 12 hours time format preference', () => {
+ setup(() =>
+ // relative_date_in_change_table is not set when false.
+ stubRestAPI(
+ {time_format: 'HHMM_12', date_format: 'ISO'}
+ ).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ })
+ );
+
+ test('Within 24 hours on same day', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '3:34 PM',
+ '3:34 PM',
+ '2015-07-29, 3:34:14 PM', done);
+ });
+ });
+
+ suite('EURO + 12 hours time format preference', () => {
+ setup(() =>
+ // relative_date_in_change_table is not set when false.
+ stubRestAPI(
+ {time_format: 'HHMM_12', date_format: 'EURO'}
+ ).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ })
+ );
+
+ test('Within 24 hours on same day', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '3:34 PM',
+ '3:34 PM',
+ '29.07.2015, 3:34:14 PM', done);
+ });
+ });
+
+ suite('UK + 12 hours time format preference', () => {
+ setup(() =>
+ // relative_date_in_change_table is not set when false.
+ stubRestAPI(
+ {time_format: 'HHMM_12', date_format: 'UK'}
+ ).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ })
+ );
+
+ test('Within 24 hours on same day', done => {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '3:34 PM',
+ '3:34 PM',
+ '29/07/2015, 3:34:14 PM', done);
+ });
+ });
+
+ suite('relative date preference', () => {
+ setup(() => stubRestAPI({
+ time_format: 'HHMM_12',
+ date_format: 'STD',
+ relative_date_in_change_table: true,
+ }).then(() => {
+ element = fixture('basic');
+ sandbox.stub(element, '_getUtcOffsetString').returns('');
+ return element._loadPreferences();
+ }));
test('Within 24 hours on same day', done => {
testDates('2015-07-29 20:34:14.985000000',
@@ -183,31 +413,33 @@ limitations under the License.
});
suite('logged in', () => {
- setup(() => {
- return stubRestAPI(
- {time_format: 'HHMM_12', relative_date_in_change_table: true}
- ).then(() => {
- element = fixture('basic');
- return element._loadPreferences();
- });
- });
+ setup(() => stubRestAPI({
+ time_format: 'HHMM_12',
+ date_format: 'US',
+ relative_date_in_change_table: true,
+ }).then(() => {
+ element = fixture('basic');
+ return element._loadPreferences();
+ }));
test('Preferences are respected', () => {
assert.equal(element._timeFormat, 'h:mm A');
+ assert.equal(element._dateFormat.short, 'MM/DD');
+ assert.equal(element._dateFormat.full, 'MM/DD/YY');
assert.isTrue(element._relative);
});
});
suite('logged out', () => {
- setup(() => {
- return stubRestAPI(null).then(() => {
- element = fixture('basic');
- return element._loadPreferences();
- });
- });
+ setup(() => stubRestAPI(null).then(() => {
+ element = fixture('basic');
+ return element._loadPreferences();
+ }));
test('Default preferences are respected', () => {
assert.equal(element._timeFormat, 'HH:mm');
+ assert.equal(element._dateFormat.short, 'MMM DD');
+ assert.equal(element._dateFormat.full, 'MMM DD, YYYY');
assert.isFalse(element._relative);
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html
index 797c8eaa0d..2ef5539d00 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../gr-button/gr-button.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -45,10 +46,10 @@ limitations under the License.
header,
main,
footer {
- padding: .5em 1.5em;
+ padding: var(--spacing-m) var(--spacing-xl);
}
gr-button {
- margin-left: 1em;
+ margin-left: var(--spacing-l);
}
footer {
display: flex;
@@ -63,10 +64,10 @@ limitations under the License.
<header><slot name="header"></slot></header>
<main><slot name="main"></slot></main>
<footer>
- <gr-button id="cancel" class$="[[_computeCancelClass(cancelLabel)]]" link on-tap="_handleCancelTap">
+ <gr-button id="cancel" class$="[[_computeCancelClass(cancelLabel)]]" link on-click="_handleCancelTap">
[[cancelLabel]]
</gr-button>
- <gr-button id="confirm" link primary on-tap="_handleConfirm" disabled="[[disabled]]">
+ <gr-button id="confirm" link primary on-click="_handleConfirm" disabled="[[disabled]]">
[[confirmLabel]]
</gr-button>
</footer>
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
index b8b2af4a2e..68dc537d92 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-dialog',
- _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -53,6 +52,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
hostAttributes: {
role: 'dialog',
},
@@ -61,11 +64,13 @@
if (this.disabled) { return; }
e.preventDefault();
+ e.stopPropagation();
this.fire('confirm', null, {bubbles: false});
},
_handleCancelTap(e) {
e.preventDefault();
+ e.stopPropagation();
this.fire('cancel', null, {bubbles: false});
},
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html
index 4a5a181a0c..1456e77066 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-dialog</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-dialog.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html
index 1c9f469658..9d85d4484d 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../gr-button/gr-button.html">
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
@@ -60,43 +60,67 @@ limitations under the License.
<section>
<span class="title">Diff width</span>
<span class="value">
- <input
- is="iron-input"
+ <iron-input
type="number"
- id="columnsInput"
prevent-invalid-input
allowed-pattern="[0-9]"
bind-value="{{diffPrefs.line_length}}"
on-keypress="_handleDiffPrefsChanged"
on-change="_handleDiffPrefsChanged">
+ <input
+ is="iron-input"
+ type="number"
+ id="columnsInput"
+ prevent-invalid-input
+ allowed-pattern="[0-9]"
+ bind-value="{{diffPrefs.line_length}}"
+ on-keypress="_handleDiffPrefsChanged"
+ on-change="_handleDiffPrefsChanged">
+ </iron-input>
</span>
</section>
<section>
<span class="title">Tab width</span>
<span class="value">
- <input
- is="iron-input"
+ <iron-input
type="number"
- id="tabSizeInput"
prevent-invalid-input
allowed-pattern="[0-9]"
bind-value="{{diffPrefs.tab_size}}"
on-keypress="_handleDiffPrefsChanged"
on-change="_handleDiffPrefsChanged">
+ <input
+ is="iron-input"
+ type="number"
+ id="tabSizeInput"
+ prevent-invalid-input
+ allowed-pattern="[0-9]"
+ bind-value="{{diffPrefs.tab_size}}"
+ on-keypress="_handleDiffPrefsChanged"
+ on-change="_handleDiffPrefsChanged">
+ </iron-input>
</span>
</section>
<section hidden$="[[!diffPrefs.font_size]]">
<span class="title">Font size</span>
<span class="value">
- <input
- is="iron-input"
+ <iron-input
type="number"
- id="fontSizeInput"
prevent-invalid-input
allowed-pattern="[0-9]"
bind-value="{{diffPrefs.font_size}}"
on-keypress="_handleDiffPrefsChanged"
on-change="_handleDiffPrefsChanged">
+ <input
+ is="iron-input"
+ type="number"
+ id="fontSizeInput"
+ prevent-invalid-input
+ allowed-pattern="[0-9]"
+ bind-value="{{diffPrefs.font_size}}"
+ on-keypress="_handleDiffPrefsChanged"
+ on-change="_handleDiffPrefsChanged">
+ </iron-input>
</span>
</section>
<section>
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
index 89c3d74eed..36fdf5b051 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-diff-preferences',
- _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
index 055d7aa2c3..4d2a1a4fad 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-diff-preferences</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-diff-preferences.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html
index 6aec5a6f8c..14a65b2e29 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.html
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../../bower_components/paper-tabs/paper-tabs.html">
+<link rel="import" href="/bower_components/paper-tabs/paper-tabs.html">
<link rel="import" href="../../shared/gr-shell-command/gr-shell-command.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -29,7 +29,7 @@ limitations under the License.
<style include="shared-styles">
paper-tabs {
height: 3rem;
- margin-bottom: .5em;
+ margin-bottom: var(--spacing-m);
--paper-tabs-selection-bar-color: var(--link-color);
}
paper-tab {
@@ -54,7 +54,7 @@ limitations under the License.
}
gr-shell-command {
width: 60em;
- margin-bottom: .5em;
+ margin-bottom: var(--spacing-m);
}
.hidden {
display: none;
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
index ed7c2cc046..121aa3561b 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-download-commands',
- _legacyUndefinedCheck: true,
+
properties: {
commands: Array,
_loggedIn: {
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html
index c59e56ad28..85a5b1ff3e 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-download-commands</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-download-commands.html">
@@ -65,12 +67,13 @@ limitations under the License.
});
suite('unauthenticated', () => {
- setup(() => {
+ setup(done => {
element = fixture('basic');
element.schemes = SCHEMES;
element.commands = COMMANDS;
element.selectedScheme = SELECTED_SCHEME;
flushAsynchronousOperations();
+ flush(done);
});
test('focusOnCopy', () => {
@@ -89,13 +92,13 @@ limitations under the License.
assert.isTrue(isHidden(element.$$('.commands')));
});
- test('tab selection', () => {
+ test('tab selection', done => {
assert.equal(element.$.downloadTabs.selected, '0');
MockInteractions.tap(element.$$('[data-scheme="ssh"]'));
flushAsynchronousOperations();
-
assert.equal(element.selectedScheme, 'ssh');
assert.equal(element.$.downloadTabs.selected, '2');
+ done();
});
test('loads scheme from preferences', done => {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html
index 42b6f941f9..98d7bf67c8 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.html
@@ -14,11 +14,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
-<link rel="import" href="../../../bower_components/paper-item/paper-item.html">
-<link rel="import" href="../../../bower_components/paper-listbox/paper-listbox.html">
+<link rel="import" href="/bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="/bower_components/paper-item/paper-item.html">
+<link rel="import" href="/bower_components/paper-listbox/paper-listbox.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -46,9 +46,9 @@ limitations under the License.
background-color: var(--dropdown-background-color);
box-shadow: 0 1px 5px rgba(0, 0, 0, .3);
max-height: 70vh;
- margin-top: 2em;
+ margin-top: var(--spacing-xxl);
min-width: 266px;
- @apply --dropdown-content-style
+ @apply --dropdown-content-style;
}
paper-listbox {
--paper-listbox: {
@@ -58,7 +58,7 @@ limitations under the License.
paper-item {
cursor: pointer;
flex-direction: column;
- font-size: var(--font-size-normal);
+ font-size: inherit;
--paper-item: {
min-height: 0;
padding: 10px 16px;
@@ -78,20 +78,10 @@ limitations under the License.
}
.bottomContent {
color: var(--deemphasized-text-color);
- /*
- * Should be 16px when the base font size is 13px (browser default of
- * 16px.
- */
- line-height: 1.37rem;
}
.bottomContent,
.topContent {
display: flex;
- /*
- * Should be 16px when the base font size is 13px (browser default of
- * 16px.
- */
- line-height: 1.37rem;
justify-content: space-between;
flex-direction: row;
width: 100%;
@@ -103,7 +93,7 @@ limitations under the License.
}
gr-date-formatter {
color: var(--deemphasized-text-color);
- margin-left: 2em;
+ margin-left: var(--spacing-xxl);
white-space: nowrap;
}
gr-select {
@@ -135,11 +125,12 @@ limitations under the License.
}
</style>
<gr-button
+ disabled="[[disabled]]"
down-arrow
link
id="trigger"
class="dropdown-trigger"
- on-tap="_showDropdownTapHandler"
+ on-click="_showDropdownTapHandler"
slot="dropdown-trigger">
<span id="triggerText">[[text]]</span>
</gr-button>
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
index 6b3905f5d2..efd1d0c42c 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
@@ -47,7 +47,6 @@
Polymer({
is: 'gr-dropdown-list',
- _legacyUndefinedCheck: true,
/**
* Fired when the selected value changes
@@ -62,6 +61,10 @@
/** @type {!Array<!Defs.item>} */
items: Object,
text: String,
+ disabled: {
+ type: Boolean,
+ value: false,
+ },
value: {
type: String,
notify: true,
@@ -106,6 +109,11 @@
},
_handleValueChange(value, items) {
+ // Polymer 2: check for undefined
+ if ([value, items].some(arg => arg === undefined)) {
+ return;
+ }
+
if (!value) { return; }
const selectedObj = items.find(item => {
return item.value + '' === value + '';
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
index 87fd8de672..2b63d99f98 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-dropdown-list</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-dropdown-list.html">
@@ -66,7 +68,7 @@ limitations under the License.
assert.equal(element._computeMobileText(item), item.mobileText);
});
- test('options are selected and laid out correctly', () => {
+ test('options are selected and laid out correctly', done => {
element.value = 2;
element.items = [
{
@@ -92,77 +94,79 @@ limitations under the License.
];
assert.equal(element.$$('paper-listbox').selected, element.value);
assert.equal(element.text, 'Button Text 2');
- flushAsynchronousOperations();
- const items = Polymer.dom(element.root).querySelectorAll('paper-item');
- const mobileItems = Polymer.dom(element.root).querySelectorAll('option');
- assert.equal(items.length, 3);
- assert.equal(mobileItems.length, 3);
-
- // First Item
- // The first item should be disabled, has no bottom text, and no date.
- assert.isFalse(!!items[0].disabled);
- assert.isFalse(mobileItems[0].disabled);
- assert.isFalse(items[0].classList.contains('iron-selected'));
- assert.isFalse(mobileItems[0].selected);
-
- assert.isNotOk(Polymer.dom(items[0]).querySelector('gr-date-formatter'));
- assert.isNotOk(Polymer.dom(items[0]).querySelector('.bottomContent'));
- assert.equal(items[0].value, element.items[0].value);
- assert.equal(mobileItems[0].value, element.items[0].value);
- assert.equal(Polymer.dom(items[0]).querySelector('.topContent div')
- .innerText, element.items[0].text);
-
- // Since no mobile specific text, it should fall back to text.
- assert.equal(mobileItems[0].text, element.items[0].text);
-
-
- // Second Item
- // The second item should have top text, bottom text, and no date.
- assert.isFalse(!!items[1].disabled);
- assert.isFalse(mobileItems[1].disabled);
- assert.isTrue(items[1].classList.contains('iron-selected'));
- assert.isTrue(mobileItems[1].selected);
-
- assert.isNotOk(Polymer.dom(items[1]).querySelector('gr-date-formatter'));
- assert.isOk(Polymer.dom(items[1]).querySelector('.bottomContent'));
- assert.equal(items[1].value, element.items[1].value);
- assert.equal(mobileItems[1].value, element.items[1].value);
- assert.equal(Polymer.dom(items[1]).querySelector('.topContent div')
- .innerText, element.items[1].text);
-
- // Since there is mobile specific text, it should that.
- assert.equal(mobileItems[1].text, element.items[1].mobileText);
-
- // Since this item is selected, and it has triggerText defined, that
- // should be used.
- assert.equal(element.text, element.items[1].triggerText);
-
- // Third item
- // The third item should be disabled, and have a date, and bottom content.
- assert.isTrue(!!items[2].disabled);
- assert.isTrue(mobileItems[2].disabled);
- assert.isFalse(items[2].classList.contains('iron-selected'));
- assert.isFalse(mobileItems[2].selected);
-
- assert.isOk(Polymer.dom(items[2]).querySelector('gr-date-formatter'));
- assert.isOk(Polymer.dom(items[2]).querySelector('.bottomContent'));
- assert.equal(items[2].value, element.items[2].value);
- assert.equal(mobileItems[2].value, element.items[2].value);
- assert.equal(Polymer.dom(items[2]).querySelector('.topContent div')
- .innerText, element.items[2].text);
-
- // Since there is mobile specific text, it should that.
- assert.equal(mobileItems[2].text, element.items[2].mobileText);
-
- // Select a new item.
- MockInteractions.tap(items[0]);
- flushAsynchronousOperations();
- assert.equal(element.value, 1);
- assert.isTrue(items[0].classList.contains('iron-selected'));
- assert.isTrue(mobileItems[0].selected);
-
- // Since no triggerText, the fallback is used.
- assert.equal(element.text, element.items[0].text);
+ flush(() => {
+ const items = Polymer.dom(element.root).querySelectorAll('paper-item');
+ const mobileItems = Polymer.dom(element.root).querySelectorAll('option');
+ assert.equal(items.length, 3);
+ assert.equal(mobileItems.length, 3);
+
+ // First Item
+ // The first item should be disabled, has no bottom text, and no date.
+ assert.isFalse(!!items[0].disabled);
+ assert.isFalse(mobileItems[0].disabled);
+ assert.isFalse(items[0].classList.contains('iron-selected'));
+ assert.isFalse(mobileItems[0].selected);
+
+ assert.isNotOk(Polymer.dom(items[0]).querySelector('gr-date-formatter'));
+ assert.isNotOk(Polymer.dom(items[0]).querySelector('.bottomContent'));
+ assert.equal(items[0].value, element.items[0].value);
+ assert.equal(mobileItems[0].value, element.items[0].value);
+ assert.equal(Polymer.dom(items[0]).querySelector('.topContent div')
+ .innerText, element.items[0].text);
+
+ // Since no mobile specific text, it should fall back to text.
+ assert.equal(mobileItems[0].text, element.items[0].text);
+
+
+ // Second Item
+ // The second item should have top text, bottom text, and no date.
+ assert.isFalse(!!items[1].disabled);
+ assert.isFalse(mobileItems[1].disabled);
+ assert.isTrue(items[1].classList.contains('iron-selected'));
+ assert.isTrue(mobileItems[1].selected);
+
+ assert.isNotOk(Polymer.dom(items[1]).querySelector('gr-date-formatter'));
+ assert.isOk(Polymer.dom(items[1]).querySelector('.bottomContent'));
+ assert.equal(items[1].value, element.items[1].value);
+ assert.equal(mobileItems[1].value, element.items[1].value);
+ assert.equal(Polymer.dom(items[1]).querySelector('.topContent div')
+ .innerText, element.items[1].text);
+
+ // Since there is mobile specific text, it should that.
+ assert.equal(mobileItems[1].text, element.items[1].mobileText);
+
+ // Since this item is selected, and it has triggerText defined, that
+ // should be used.
+ assert.equal(element.text, element.items[1].triggerText);
+
+ // Third item
+ // The third item should be disabled, and have a date, and bottom content.
+ assert.isTrue(!!items[2].disabled);
+ assert.isTrue(mobileItems[2].disabled);
+ assert.isFalse(items[2].classList.contains('iron-selected'));
+ assert.isFalse(mobileItems[2].selected);
+
+ assert.isOk(Polymer.dom(items[2]).querySelector('gr-date-formatter'));
+ assert.isOk(Polymer.dom(items[2]).querySelector('.bottomContent'));
+ assert.equal(items[2].value, element.items[2].value);
+ assert.equal(mobileItems[2].value, element.items[2].value);
+ assert.equal(Polymer.dom(items[2]).querySelector('.topContent div')
+ .innerText, element.items[2].text);
+
+ // Since there is mobile specific text, it should that.
+ assert.equal(mobileItems[2].text, element.items[2].mobileText);
+
+ // Select a new item.
+ MockInteractions.tap(items[0]);
+ flushAsynchronousOperations();
+ assert.equal(element.value, 1);
+ assert.isTrue(items[0].classList.contains('iron-selected'));
+ assert.isTrue(mobileItems[0].selected);
+
+ // Since no triggerText, the fallback is used.
+ assert.equal(element.text, element.items[0].text);
+ done();
+ });
});
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
index 373e77d9bc..d76721fc0c 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
@@ -17,8 +17,8 @@ limitations under the License.
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-dropdown/iron-dropdown.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -63,7 +63,7 @@ limitations under the License.
li .itemAction {
cursor: pointer;
display: block;
- padding: .85em 1em;
+ padding: var(--spacing-m) var(--spacing-l);
}
li .itemAction {
@apply --gr-dropdown-item;
@@ -90,7 +90,7 @@ limitations under the License.
}
.topContent {
display: block;
- padding: .85em 1em;
+ padding: var(--spacing-m) var(--spacing-l);
@apply --gr-dropdown-item;
}
.bold-text {
@@ -101,7 +101,7 @@ limitations under the License.
link="[[link]]"
class="dropdown-trigger" id="trigger"
down-arrow="[[downArrow]]"
- on-tap="_dropdownTriggerTapHandler">
+ on-click="_dropdownTriggerTapHandler">
<slot></slot>
</gr-button>
<iron-dropdown id="dropdown"
@@ -139,7 +139,7 @@ limitations under the License.
<span
class$="itemAction [[_computeDisabledClass(link.id, disabledIds.*)]]"
data-id$="[[link.id]]"
- on-tap="_handleItemTap"
+ on-click="_handleItemTap"
hidden$="[[link.url]]"
tabindex="-1">[[link.name]]</span>
<a
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
index b4b70875ab..11825c5641 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-dropdown',
- _legacyUndefinedCheck: true,
/**
* Fired when a non-link dropdown item with the given ID is tapped.
@@ -143,10 +142,10 @@
e.preventDefault();
e.stopPropagation();
if (this.$.dropdown.opened) {
- // TODO(kaspern): This solution will not work in Shadow DOM, and
- // is not particularly robust in general. Find a better solution
- // when page.js has been abstracted away from components.
- const el = this.$.cursor.target.querySelector(':not([hidden])');
+ // TODO(milutin): This solution is not particularly robust in general.
+ // Since gr-tooltip-content click on shadow dom is not propagated down,
+ // we have to target `a` inside it.
+ const el = this.$.cursor.target.querySelector(':not([hidden]) a');
if (el) { el.click(); }
} else {
this._open();
@@ -182,8 +181,8 @@
*/
_open() {
this.$.dropdown.open();
+ this._resetCursorStops();
this.$.cursor.setCursorAtIndex(0);
- Polymer.dom.flush();
this.$.cursor.target.focus();
},
@@ -295,10 +294,11 @@
* Recompute the stops for the dropdown item cursor.
*/
_resetCursorStops() {
- Polymer.dom.flush();
- // Polymer2: querySelectorAll returns NodeList instead of Array.
- this._listElements = Array.from(
- Polymer.dom(this.root).querySelectorAll('li'));
+ if (this.items && this.items.length > 0 && this.$.dropdown.opened) {
+ Polymer.dom.flush();
+ this._listElements = Array.from(
+ Polymer.dom(this.root).querySelectorAll('li'));
+ }
},
_computeHasTooltip(tooltip) {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
index 7bb4dce5fe..295f746321 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-dropdown</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-dropdown.html">
@@ -194,7 +196,7 @@ limitations under the License.
MockInteractions.pressAndReleaseKeyOn(element, 32); // Space
assert.isTrue(element.$.dropdown.opened);
- const el = element.$.cursor.target.querySelector(':not([hidden])');
+ const el = element.$.cursor.target.querySelector(':not([hidden]) a');
const stub = sandbox.stub(el, 'click');
MockInteractions.pressAndReleaseKeyOn(element, 32); // Space
assert.isTrue(stub.called);
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
index 6cd87f52ff..45ddfc8369 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.html
@@ -15,10 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../gr-storage/gr-storage.html">
+<link rel="import" href="../gr-button/gr-button.html">
<dom-module id="gr-editable-content">
<template>
@@ -29,10 +31,18 @@ limitations under the License.
:host([disabled]) iron-autogrow-textarea {
opacity: .5;
}
- iron-autogrow-textarea {
+ .viewer {
+ background-color: var(--view-background-color);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: var(--spacing-m);
+ }
+ .editor iron-autogrow-textarea {
+ background-color: var(--view-background-color);
width: 100%;
--iron-autogrow-textarea: {
+ padding: var(--spacing-m);
box-sizing: border-box;
overflow-y: hidden;
white-space: pre;
@@ -43,7 +53,7 @@ limitations under the License.
justify-content: space-between;
}
</style>
- <div hidden$="[[editing]]">
+ <div class="viewer" hidden$="[[editing]]">
<slot></slot>
</div>
<div class="editor" hidden$="[[!editing]]">
@@ -53,10 +63,10 @@ limitations under the License.
disabled="[[disabled]]"></iron-autogrow-textarea>
<div class="editButtons">
<gr-button primary
- on-tap="_handleSave"
+ on-click="_handleSave"
disabled="[[_saveDisabled]]">Save</gr-button>
<gr-button
- on-tap="_handleCancel"
+ on-click="_handleCancel"
disabled="[[disabled]]">Cancel</gr-button>
</div>
</div>
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
index 5463564aa0..ee41103ae9 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-editable-content',
- _legacyUndefinedCheck: true,
/**
* Fired when the save button is pressed.
@@ -71,6 +70,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
focusTextarea() {
this.$$('iron-autogrow-textarea').textarea.focus();
},
@@ -117,8 +120,11 @@
this.$.storage.getEditableContentItem(this.storageKey);
if (storedContent && storedContent.message) {
content = storedContent.message;
- this.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message: RESTORED_MESSAGE}, bubbles: true}));
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {message: RESTORED_MESSAGE},
+ bubbles: true,
+ composed: true,
+ }));
}
}
if (!content) {
@@ -132,6 +138,15 @@
},
_computeSaveDisabled(disabled, content, newContent) {
+ // Polymer 2: check for undefined
+ if ([
+ disabled,
+ content,
+ newContent,
+ ].some(arg => arg === undefined)) {
+ return true;
+ }
+
return disabled || !newContent || content === newContent;
},
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
index ca500cbd21..ee5adb0c6b 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-editable-content</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/iron-test-helpers/mock-interactions.js"></script>
+<script src="/bower_components/iron-test-helpers/mock-interactions.js"></script>
<link rel="import" href="gr-editable-content.html">
@@ -47,6 +49,7 @@ limitations under the License.
teardown(() => { sandbox.restore(); });
test('save event', done => {
+ element.content = '';
element._newContent = 'foo';
element.addEventListener('editable-content-save', e => {
assert.equal(e.detail.content, 'foo');
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
index ddc35bf0aa..78465e188d 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
@@ -14,12 +14,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
-<link rel="import" href="../../../bower_components/paper-input/paper-input.html">
+<link rel="import" href="/bower_components/iron-dropdown/iron-dropdown.html">
+<link rel="import" href="/bower_components/paper-input/paper-input.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../gr-button/gr-button.html">
<dom-module id="gr-editable-label">
<template>
@@ -35,13 +37,9 @@ limitations under the License.
label {
width: 100%;
}
- input {
- font: inherit;
- }
label {
color: var(--deemphasized-text-color);
display: inline-block;
- font-weight: var(--font-weight-bold);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -56,17 +54,17 @@ limitations under the License.
}
.inputContainer {
background-color: var(--dialog-background-color);
- padding: .8em;
+ padding: var(--spacing-m);
@apply --input-style;
}
.buttons {
display: flex;
justify-content: flex-end;
- padding-top: 1.2em;
+ padding-top: var(--spacing-l);
width: 100%;
}
.buttons gr-button {
- margin-left: .5em;
+ margin-left: var(--spacing-m);
}
paper-input {
--paper-input-container: {
@@ -74,7 +72,7 @@ limitations under the License.
min-width: 15em;
}
--paper-input-container-input: {
- font-size: var(--font-size-normal);
+ font-size: inherit;
}
--paper-input-container-focus-color: var(--link-color);
}
@@ -82,7 +80,7 @@ limitations under the License.
<label
class$="[[_computeLabelClass(readOnly, value, placeholder)]]"
title$="[[_computeLabel(value, placeholder)]]"
- on-tap="_showDropdown">[[_computeLabel(value, placeholder)]]</label>
+ on-click="_showDropdown">[[_computeLabel(value, placeholder)]]</label>
<iron-dropdown id="dropdown"
vertical-align="auto"
horizontal-align="auto"
@@ -97,8 +95,8 @@ limitations under the License.
maxlength="[[maxLength]]"
value="{{_inputText}}"></paper-input>
<div class="buttons">
- <gr-button link id="cancelBtn" on-tap="_cancel">cancel</gr-button>
- <gr-button link id="saveBtn" on-tap="_save">save</gr-button>
+ <gr-button link id="cancelBtn" on-click="_cancel">cancel</gr-button>
+ <gr-button link id="saveBtn" on-click="_save">save</gr-button>
</div>
</div>
</div>
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
index f23afeae08..4485551a21 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-editable-label',
- _legacyUndefinedCheck: true,
/**
* Fired when the value is changed.
@@ -68,6 +67,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
],
@@ -94,15 +94,15 @@
_showDropdown() {
if (this.readOnly || this.editing) { return; }
return this._open().then(() => {
- this.$.input.$.input.focus();
+ this._nativeInput.focus();
if (!this.$.input.value) { return; }
- this.$.input.$.input.setSelectionRange(0, this.$.input.value.length);
+ this._nativeInput.setSelectionRange(0, this.$.input.value.length);
});
},
open() {
return this._open().then(() => {
- this.$.input.$.input.focus();
+ this._nativeInput.focus();
});
},
@@ -154,29 +154,25 @@
this._inputText = this.value;
},
- /**
- * @suppress {checkTypes}
- * Closure doesn't think 'e' is an Event.
- * TODO(beckysiegel) figure out why.
- */
+ get _nativeInput() {
+ // In Polymer 2, the namespace of nativeInput
+ // changed from input to nativeInput
+ return this.$.input.$.nativeInput || this.$.input.$.input;
+ },
+
_handleEnter(e) {
e = this.getKeyboardEvent(e);
const target = Polymer.dom(e).rootTarget;
- if (target === this.$.input.$.input) {
+ if (target === this._nativeInput) {
e.preventDefault();
this._save();
}
},
- /**
- * @suppress {checkTypes}
- * Closure doesn't think 'e' is an Event.
- * TODO(beckysiegel) figure out why.
- */
_handleEsc(e) {
e = this.getKeyboardEvent(e);
const target = Polymer.dom(e).rootTarget;
- if (target === this.$.input.$.input) {
+ if (target === this._nativeInput) {
e.preventDefault();
this._cancel();
}
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
index 68151734f4..7ff0a1411d 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-editable-label</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/iron-test-helpers/mock-interactions.js"></script>
+<script src="/bower_components/iron-test-helpers/mock-interactions.js"></script>
<link rel="import" href="gr-editable-label.html">
@@ -59,13 +61,17 @@ limitations under the License.
let label;
let sandbox;
- setup(() => {
+ setup(done => {
element = fixture('basic');
elementNoPlaceholder = fixture('no-placeholder');
- input = element.$.input.$.input;
label = element.$$('label');
sandbox = sinon.sandbox.create();
+ flush(() => {
+ // In Polymer 2 inputElement isn't nativeInput anymore
+ input = element.$.input.$.nativeInput || element.$.input.inputElement;
+ done();
+ });
});
teardown(() => {
@@ -77,7 +83,7 @@ limitations under the License.
assert.isFalse(element.$.dropdown.opened);
assert.isTrue(label.classList.contains('editable'));
assert.equal(label.textContent, 'value text');
- const focusSpy = sandbox.spy(element.$.input.$.input, 'focus');
+ const focusSpy = sandbox.spy(input, 'focus');
const showSpy = sandbox.spy(element, '_showDropdown');
MockInteractions.tap(label);
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
index 674ff977a6..226092fca4 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
@@ -15,13 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-fixed-panel">
<template>
<style include="shared-styles">
:host {
+ box-sizing: border-box;
display: block;
min-height: var(--header-height);
position: relative;
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
index 87cd4b4433..2c32709176 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-fixed-panel',
- _legacyUndefinedCheck: true,
properties: {
floatingDisabled: Boolean,
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
index 9eac7f74fb..75e9901f90 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-fixed-panel</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-fixed-panel.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html
index 39955956c7..a98952c25c 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../gr-linked-text/gr-linked-text.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -29,7 +29,7 @@ limitations under the License.
ul,
blockquote,
gr-linked-text.pre {
- margin: 0 0 .8em 0;
+ margin: 0 0 var(--spacing-m) 0;
}
p,
ul,
@@ -44,14 +44,16 @@ limitations under the License.
}
blockquote {
border-left: 1px solid #aaa;
- padding: 0 .7em;
+ padding: 0 var(--spacing-m);
}
li {
list-style-type: disc;
- margin-left: 1.4em;
+ margin-left: var(--spacing-xl);
}
gr-linked-text.pre {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-code);
+ line-height: var(--line-height-code);
}
</style>
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
index d84c38e759..feae17314e 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-formatted-text',
- _legacyUndefinedCheck: true,
properties: {
content: {
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
index ad036c58c1..801190a75d 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-editable-label</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-formatted-text.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.html b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.html
index 7e3246fcbb..c77ea811c5 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.html
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -34,10 +34,10 @@ limitations under the License.
visibility: visible;
opacity: 1;
}
- :host ::content #hovercard {
+ #hovercard {
background: var(--dialog-background-color);
box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px;
- padding: 1em;
+ padding: var(--spacing-l);
}
</style>
<div id="hovercard" role="tooltip" tabindex="-1">
@@ -46,4 +46,4 @@ limitations under the License.
</template>
<script src="../../../scripts/rootElement.js"></script>
<script src="gr-hovercard.js"></script>
-</dom-module> \ No newline at end of file
+</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
index f8ff549f95..5692b38896 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
@@ -27,7 +27,6 @@
Polymer({
is: 'gr-hovercard',
- _legacyUndefinedCheck: true,
properties: {
/**
@@ -98,7 +97,7 @@
this.listen(this._target, 'focus', 'show');
this.listen(this._target, 'mouseleave', 'hide');
this.listen(this._target, 'blur', 'hide');
- this.listen(this._target, 'tap', 'hide');
+ this.listen(this._target, 'click', 'hide');
},
ready() {
@@ -119,7 +118,7 @@
this.unlisten(this._target, 'focus', 'show');
this.unlisten(this._target, 'mouseleave', 'hide');
this.unlisten(this._target, 'blur', 'hide');
- this.unlisten(this._target, 'tap', 'hide');
+ this.unlisten(this._target, 'click', 'hide');
},
/**
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html
index e3e252fc56..8e79f65795 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-hovercard</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/iron-test-helpers/mock-interactions.js"></script>
+<script src="/bower_components/iron-test-helpers/mock-interactions.js"></script>
<link rel="import" href="gr-hovercard.html">
@@ -39,7 +41,8 @@ limitations under the License.
suite('gr-hovercard tests', () => {
let element;
let sandbox;
- const TRANSITION_TIME = 200;
+ // For css animations
+ const TRANSITION_TIME = 500;
setup(() => {
sandbox = sinon.sandbox.create();
diff --git a/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html b/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
index 1f885afd51..7bd6f48a4d 100644
--- a/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
+++ b/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
@@ -14,8 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/iron-icon/iron-icon.html">
-<link rel="import" href="../../../bower_components/iron-iconset-svg/iron-iconset-svg.html">
+<link rel="import" href="/bower_components/iron-icon/iron-icon.html">
+<link rel="import" href="/bower_components/iron-iconset-svg/iron-iconset-svg.html">
<iron-iconset-svg name="gr-icons" size="24">
<svg>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.js
index 2581ff985d..708e5b68f0 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.js
@@ -45,26 +45,27 @@
*
* @param {number} offset The char offset where the update starts.
* @param {number} length The number of chars that the update covers.
- * @param {string} cssClass The name of a CSS class created using Gerrit.css.
+ * @param {GrStyleObject} styleObject The style object for the range.
* @param {string} side The side of the update. ('left' or 'right')
*/
GrAnnotationActionsContext.prototype.annotateRange = function(
- offset, length, cssClass, side) {
+ offset, length, styleObject, side) {
if (this._contentEl && this._contentEl.getAttribute('data-side') == side) {
- GrAnnotation.annotateElement(this._contentEl, offset, length, cssClass);
+ GrAnnotation.annotateElement(this._contentEl, offset, length,
+ styleObject.getClassName(this._contentEl));
}
};
/**
* Method to add a CSS class to the line number TD element.
*
- * @param {string} cssClass The name of a CSS class created using Gerrit.css.
+ * @param {GrStyleObject} styleObject The style object for the range.
* @param {string} side The side of the update. ('left' or 'right')
*/
GrAnnotationActionsContext.prototype.annotateLineNumber = function(
- cssClass, side) {
+ styleObject, side) {
if (this._lineNumberEl && this._lineNumberEl.classList.contains(side)) {
- this._lineNumberEl.classList.add(cssClass);
+ styleObject.apply(this._lineNumberEl);
}
};
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html
index 03c8c5e384..3223636ef9 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-annotation-actions-context</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<script src="../../diff/gr-diff-highlight/gr-annotation.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
@@ -40,9 +42,13 @@ limitations under the License.
let sandbox;
let el;
let lineNumberEl;
+ let plugin;
setup(() => {
sandbox = sinon.sandbox.create();
+ Gerrit.install(p => { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+
const str = 'lorem ipsum blah blah';
const line = {text: str};
el = document.createElement('div');
@@ -50,6 +56,7 @@ limitations under the License.
el.setAttribute('data-side', 'right');
lineNumberEl = document.createElement('td');
lineNumberEl.classList.add('right');
+ document.body.appendChild(el);
instance = new GrAnnotationActionsContext(
el, lineNumberEl, line, 'dummy/path', '123', '1');
});
@@ -62,32 +69,34 @@ limitations under the License.
annotateElementSpy = sandbox.spy(GrAnnotation, 'annotateElement');
const start = 0;
const end = 100;
- const cssClass = Gerrit.css('background-color: #000000');
+ const cssStyleObject = plugin.styles().css('background-color: #000000');
// Assert annotateElement is not called when side is different.
- instance.annotateRange(start, end, cssClass, 'left');
+ instance.annotateRange(start, end, cssStyleObject, 'left');
assert.equal(annotateElementSpy.callCount, 0);
// Assert annotateElement is called once when side is the same.
- instance.annotateRange(start, end, cssClass, 'right');
+ instance.annotateRange(start, end, cssStyleObject, 'right');
assert.equal(annotateElementSpy.callCount, 1);
const args = annotateElementSpy.getCalls()[0].args;
assert.equal(args[0], el);
assert.equal(args[1], start);
assert.equal(args[2], end);
- assert.equal(args[3], cssClass);
+ assert.equal(args[3], cssStyleObject.getClassName(el));
});
test('test annotateLineNumber', () => {
- const cssClass = Gerrit.css('background-color: #000000');
+ const cssStyleObject = plugin.styles().css('background-color: #000000');
+
+ const className = cssStyleObject.getClassName(lineNumberEl);
// Assert that css class is *not* applied when side is different.
- instance.annotateLineNumber(cssClass, 'left');
- assert.isFalse(lineNumberEl.classList.contains(cssClass));
+ instance.annotateLineNumber(cssStyleObject, 'left');
+ assert.isFalse(lineNumberEl.classList.contains(className));
// Assert that css class is applied when side is the same.
- instance.annotateLineNumber(cssClass, 'right');
- assert.isTrue(lineNumberEl.classList.contains(cssClass));
+ instance.annotateLineNumber(cssStyleObject, 'right');
+ assert.isTrue(lineNumberEl.classList.contains(className));
});
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.js
index 3f91a826c5..4ba9820599 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.js
@@ -67,10 +67,8 @@
* providers are not supported. A second call will just overwrite the
* provider of the first call.
*
- * TODO(brohlfs): Replace Array<Object> type by Array<Gerrit.CoverageRange>.
- *
* @param {function(changeNum, path, basePatchNum, patchNum):
- * !Promise<!Array<Object>>} coverageProvider
+ * !Promise<!Array<!Gerrit.CoverageRange>>} coverageProvider
* @return {GrAnnotationActionsInterface}
*/
GrAnnotationActionsInterface.prototype.setCoverageProvider = function(
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html
index bf7c2cb939..987b551c0e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-annotation-actions-js-api-js-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../change/gr-change-actions/gr-change-actions.html">
@@ -28,7 +30,9 @@ limitations under the License.
<template>
<span hidden id="annotation-span">
<label for="annotation-checkbox" id="annotation-label"></label>
- <input is="iron-input" type="checkbox" id="annotation-checkbox" disabled>
+ <iron-input type="checkbox" disabled>
+ <input is="iron-input" type="checkbox" id="annotation-checkbox" disabled>
+ </iron-input>
</span>
</template>
</test-fixture>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
new file mode 100644
index 0000000000..2925736755
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
@@ -0,0 +1,112 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function(window) {
+ 'use strict';
+
+ const PRELOADED_PROTOCOL = 'preloaded:';
+ const PLUGIN_LOADING_TIMEOUT_MS = 10000;
+
+ let _restAPI;
+ function getRestAPI() {
+ if (!_restAPI) {
+ _restAPI = document.createElement('gr-rest-api-interface');
+ }
+ return _restAPI;
+ }
+
+ function getBaseUrl() {
+ return Gerrit.BaseUrlBehavior.getBaseUrl();
+ }
+
+ /**
+ * Retrieves the name of the plugin base on the url.
+ *
+ * @param {string|URL} url
+ */
+ function getPluginNameFromUrl(url) {
+ if (!(url instanceof URL)) {
+ try {
+ url = new URL(url);
+ } catch (e) {
+ console.warn(e);
+ return null;
+ }
+ }
+ if (url.protocol === PRELOADED_PROTOCOL) {
+ return url.pathname;
+ }
+ const base = Gerrit.BaseUrlBehavior.getBaseUrl();
+ const pathname = url.pathname.replace(base, '');
+ // Site theme is server from predefined path.
+ if (pathname === '/static/gerrit-theme.html') {
+ return 'gerrit-theme';
+ } else if (!pathname.startsWith('/plugins')) {
+ console.warn('Plugin not being loaded from /plugins base path:',
+ url.href, '— Unable to determine name.');
+ return null;
+ }
+
+ // Pathname should normally look like this:
+ // /plugins/PLUGINNAME/static/SCRIPTNAME.html
+ // Or, for app/samples:
+ // /plugins/PLUGINNAME.html
+ // TODO(taoalpha): guard with a regex
+ return pathname.split('/')[2].split('.')[0];
+ }
+
+ // TODO(taoalpha): to be deprecated.
+ function send(method, url, opt_callback, opt_payload) {
+ return getRestAPI().send(method, url, opt_payload).then(response => {
+ if (response.status < 200 || response.status >= 300) {
+ return response.text().then(text => {
+ if (text) {
+ return Promise.reject(text);
+ } else {
+ return Promise.reject(response.status);
+ }
+ });
+ } else {
+ return getRestAPI().getResponseObject(response);
+ }
+ }).then(response => {
+ if (opt_callback) {
+ opt_callback(response);
+ }
+ return response;
+ });
+ }
+
+
+ // TEST only methods / properties
+
+ function testOnly_resetInternalState() {
+ _restAPI = undefined;
+ }
+
+ window._apiUtils = {
+ getPluginNameFromUrl,
+ send,
+ getRestAPI,
+ getBaseUrl,
+ PRELOADED_PROTOCOL,
+ PLUGIN_LOADING_TIMEOUT_MS,
+
+ // TEST only methods
+ testOnly_resetInternalState,
+ };
+})(window); \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
new file mode 100644
index 0000000000..c407aa816d
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-api-interface</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-js-api-interface.html">
+
+<script>void(0);</script>
+
+<script>
+
+ const PRELOADED_PROTOCOL = 'preloaded:';
+
+ suite('gr-api-utils tests', () => {
+ suite('test getPluginNameFromUrl', () => {
+ const {getPluginNameFromUrl} = window._apiUtils;
+
+ test('with empty string', () => {
+ assert.equal(getPluginNameFromUrl(''), null);
+ });
+
+ test('with invalid url', () => {
+ assert.equal(getPluginNameFromUrl('test'), null);
+ });
+
+ test('with random invalid url', () => {
+ assert.equal(getPluginNameFromUrl('http://example.com'), null);
+ assert.equal(
+ getPluginNameFromUrl('http://example.com/static/a.html'),
+ null
+ );
+ });
+
+ test('with valid urls', () => {
+ assert.equal(
+ getPluginNameFromUrl('http://example.com/plugins/a.html'),
+ 'a'
+ );
+ assert.equal(
+ getPluginNameFromUrl('http://example.com/plugins/a/static/t.html'),
+ 'a'
+ );
+ });
+
+ test('with preloaded urls', () => {
+ assert.equal(getPluginNameFromUrl(`${PRELOADED_PROTOCOL}a`), 'a');
+ });
+
+ test('with gerrit-theme override', () => {
+ assert.equal(
+ getPluginNameFromUrl('http://example.com/static/gerrit-theme.html'),
+ 'gerrit-theme'
+ );
+ });
+ });
+ });
+</script> \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
index fef4fc9df2..7332877748 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-actions-js-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<!--
This must refer to the element this interface is wrapping around. Otherwise
@@ -52,17 +54,18 @@ breaking changes to gr-change-actions won’t be noticed.
suite('early init', () => {
setup(() => {
- Gerrit._resetPlugins();
+ Gerrit._testOnly_resetPlugins();
Gerrit.install(p => { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
// Mimic all plugins loaded.
- Gerrit._setPluginsPending([]);
+ Gerrit._loadPlugins([]);
changeActions = plugin.changeActions();
element = fixture('basic');
});
teardown(() => {
changeActions = null;
+ Gerrit._testOnly_resetPlugins();
});
test('does not throw', ()=> {
@@ -74,7 +77,7 @@ breaking changes to gr-change-actions won’t be noticed.
suite('normal init', () => {
setup(() => {
- Gerrit._resetPlugins();
+ Gerrit._testOnly_resetPlugins();
element = fixture('basic');
sinon.stub(element, '_editStatusChanged');
element.change = {};
@@ -83,11 +86,12 @@ breaking changes to gr-change-actions won’t be noticed.
'http://test.com/plugins/testplugin/static/test.js');
changeActions = plugin.changeActions();
// Mimic all plugins loaded.
- Gerrit._setPluginsPending([]);
+ Gerrit._loadPlugins([]);
});
teardown(() => {
changeActions = null;
+ Gerrit._testOnly_resetPlugins();
});
test('property existence', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
index 278f95a3f7..842a2fe855 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-change-reply-js-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<!--
This must refer to the element this interface is wrapping around. Otherwise
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js
new file mode 100644
index 0000000000..73a248063e
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js
@@ -0,0 +1,196 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This defines the Gerrit instance. All methods directly attached to Gerrit
+ * should be defined or linked here.
+ */
+
+(function(window) {
+ 'use strict';
+
+ // Import utils methods
+ const {
+ send,
+ getRestAPI,
+ } = window._apiUtils;
+
+ /**
+ * Trigger the preinstalls for bundled plugins.
+ * This needs to happen before Gerrit as plugin bundle overrides the Gerrit.
+ */
+ function flushPreinstalls() {
+ if (window.Gerrit.flushPreinstalls) {
+ window.Gerrit.flushPreinstalls();
+ }
+ }
+ flushPreinstalls();
+
+ window.Gerrit = window.Gerrit || {};
+ const Gerrit = window.Gerrit;
+ Gerrit._pluginLoader = new PluginLoader();
+
+ Gerrit._endpoints = new GrPluginEndpoints();
+
+ // Provide reset plugins function to clear installed plugins between tests.
+ const app = document.querySelector('#app');
+ if (!app) {
+ // No gr-app found (running tests)
+ const {
+ testOnly_resetInternalState,
+ } = window._apiUtils;
+ Gerrit._testOnly_installPreloadedPlugins = (...args) => Gerrit._pluginLoader
+ .installPreloadedPlugins(...args);
+ Gerrit._testOnly_flushPreinstalls = flushPreinstalls;
+ Gerrit._testOnly_resetPlugins = () => {
+ testOnly_resetInternalState();
+ Gerrit._endpoints = new GrPluginEndpoints();
+ Gerrit._pluginLoader = new PluginLoader();
+ };
+ }
+
+ /**
+ * @deprecated Use plugin.styles().css(rulesStr) instead. Please, consult
+ * the documentation how to replace it accordingly.
+ */
+ Gerrit.css = function(rulesStr) {
+ console.warn('Gerrit.css(rulesStr) is deprecated!',
+ 'Use plugin.styles().css(rulesStr)');
+ if (!Gerrit._customStyleSheet) {
+ const styleEl = document.createElement('style');
+ document.head.appendChild(styleEl);
+ Gerrit._customStyleSheet = styleEl.sheet;
+ }
+
+ const name = '__pg_js_api_class_' +
+ Gerrit._customStyleSheet.cssRules.length;
+ Gerrit._customStyleSheet.insertRule('.' + name + '{' + rulesStr + '}', 0);
+ return name;
+ };
+
+ Gerrit.install = function(callback, opt_version, opt_src) {
+ Gerrit._pluginLoader.install(callback, opt_version, opt_src);
+ };
+
+ Gerrit.getLoggedIn = function() {
+ console.warn('Gerrit.getLoggedIn() is deprecated! ' +
+ 'Use plugin.restApi().getLoggedIn()');
+ return document.createElement('gr-rest-api-interface').getLoggedIn();
+ };
+
+ Gerrit.get = function(url, callback) {
+ console.warn('.get() is deprecated! Use plugin.restApi().get()');
+ send('GET', url, callback);
+ };
+
+ Gerrit.post = function(url, payload, callback) {
+ console.warn('.post() is deprecated! Use plugin.restApi().post()');
+ send('POST', url, callback, payload);
+ };
+
+ Gerrit.put = function(url, payload, callback) {
+ console.warn('.put() is deprecated! Use plugin.restApi().put()');
+ send('PUT', url, callback, payload);
+ };
+
+ Gerrit.delete = function(url, opt_callback) {
+ console.warn('.delete() is deprecated! Use plugin.restApi().delete()');
+ return getRestAPI().send('DELETE', url).then(response => {
+ if (response.status !== 204) {
+ return response.text().then(text => {
+ if (text) {
+ return Promise.reject(text);
+ } else {
+ return Promise.reject(response.status);
+ }
+ });
+ }
+ if (opt_callback) {
+ opt_callback(response);
+ }
+ return response;
+ });
+ };
+
+ Gerrit.awaitPluginsLoaded = function() {
+ return Gerrit._pluginLoader.awaitPluginsLoaded();
+ };
+
+ // TODO(taoalpha): consider removing these proxy methods
+ // and using _pluginLoader directly
+
+ Gerrit._loadPlugins = function(plugins, opt_option) {
+ Gerrit._pluginLoader.loadPlugins(plugins, opt_option);
+ };
+
+ Gerrit._arePluginsLoaded = function() {
+ return Gerrit._pluginLoader.arePluginsLoaded;
+ };
+
+ Gerrit._isPluginPreloaded = function(url) {
+ return Gerrit._pluginLoader.isPluginPreloaded(url);
+ };
+
+ Gerrit._isPluginEnabled = function(pathOrUrl) {
+ return Gerrit._pluginLoader.isPluginEnabled(pathOrUrl);
+ };
+
+ Gerrit._isPluginLoaded = function(pathOrUrl) {
+ return Gerrit._pluginLoader.isPluginLoaded(pathOrUrl);
+ };
+
+ // Preloaded plugins should be installed after Gerrit.install() is set,
+ // since plugin preloader substitutes Gerrit.install() temporarily.
+ Gerrit._pluginLoader.installPreloadedPlugins();
+
+ // TODO(taoalpha): List all internal supported event names.
+ // Also convert this to inherited class once we move Gerrit to class.
+ Gerrit._eventEmitter = new EventEmitter();
+ ['addListener',
+ 'dispatch',
+ 'emit',
+ 'off',
+ 'on',
+ 'once',
+ 'removeAllListeners',
+ 'removeListener',
+ ].forEach(method => {
+ /**
+ * Enabling EventEmitter interface on Gerrit.
+ *
+ * This will enable to signal across different parts of js code without relying on DOM,
+ * including core to core, plugin to plugin and also core to plugin.
+ *
+ * @example
+ *
+ * // Emit this event from pluginA
+ * Gerrit.install(pluginA => {
+ * fetch("some-api").then(() => {
+ * Gerrit.on("your-special-event", {plugin: pluginA});
+ * });
+ * });
+ *
+ * // Listen on your-special-event from pluignB
+ * Gerrit.install(pluginB => {
+ * Gerrit.on("your-special-event", ({plugin}) => {
+ * // do something, plugin is pluginA
+ * });
+ * });
+ */
+ Gerrit[method] = Gerrit._eventEmitter[method].bind(Gerrit._eventEmitter);
+ });
+})(window); \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
new file mode 100644
index 0000000000..e81b8aad01
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-api-interface</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-js-api-interface.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-js-api-interface></gr-js-api-interface>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-gerrit tests', () => {
+ let element;
+ let sandbox;
+ let sendStub;
+
+ setup(() => {
+ this.clock = sinon.useFakeTimers();
+ sandbox = sinon.sandbox.create();
+ sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
+ stub('gr-rest-api-interface', {
+ getAccount() {
+ return Promise.resolve({name: 'Judy Hopps'});
+ },
+ send(...args) {
+ return sendStub(...args);
+ },
+ });
+ element = fixture('basic');
+ });
+
+ teardown(() => {
+ this.clock.restore();
+ sandbox.restore();
+ element._removeEventCallbacks();
+ Gerrit._testOnly_resetPlugins();
+ });
+
+ suite('proxy methods', () => {
+ test('Gerrit._isPluginEnabled proxy to pluginLoader', () => {
+ const stubFn = sandbox.stub();
+ sandbox.stub(
+ Gerrit._pluginLoader,
+ 'isPluginEnabled',
+ (...args) => stubFn(...args)
+ );
+ Gerrit._isPluginEnabled('test_plugin');
+ assert.isTrue(stubFn.calledWith('test_plugin'));
+ });
+
+ test('Gerrit._isPluginLoaded proxy to pluginLoader', () => {
+ const stubFn = sandbox.stub();
+ sandbox.stub(
+ Gerrit._pluginLoader,
+ 'isPluginLoaded',
+ (...args) => stubFn(...args)
+ );
+ Gerrit._isPluginLoaded('test_plugin');
+ assert.isTrue(stubFn.calledWith('test_plugin'));
+ });
+
+ test('Gerrit._isPluginPreloaded proxy to pluginLoader', () => {
+ const stubFn = sandbox.stub();
+ sandbox.stub(
+ Gerrit._pluginLoader,
+ 'isPluginPreloaded',
+ (...args) => stubFn(...args)
+ );
+ Gerrit._isPluginPreloaded('test_plugin');
+ assert.isTrue(stubFn.calledWith('test_plugin'));
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
index d95fd0afd6..72fc3d0fd4 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
@@ -26,10 +26,19 @@ limitations under the License.
<link rel="import" href="../../plugins/gr-popup-interface/gr-popup-interface.html">
<link rel="import" href="../../plugins/gr-repo-api/gr-repo-api.html">
<link rel="import" href="../../plugins/gr-settings-api/gr-settings-api.html">
+<link rel="import" href="../../plugins/gr-styles-api/gr-styles-api.html">
<link rel="import" href="../../plugins/gr-theme-api/gr-theme-api.html">
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-js-api-interface">
+ <!--
+ Note: the order matters as files depend on each other.
+ 1. gr-api-utils will be used in multiple files below.
+ 2. gr-gerrit depends on gr-plugin-loader, gr-public-js-api and
+ also gr-plugin-endpoints
+ 3. gr-public-js-api depends on gr-plugin-rest-api
+ -->
+ <script src="gr-api-utils.js"></script>
<script src="../gr-event-interface/gr-event-interface.js"></script>
<script src="gr-annotation-actions-context.js"></script>
<script src="gr-annotation-actions-js-api.js"></script>
@@ -40,4 +49,6 @@ limitations under the License.
<script src="gr-plugin-action-context.js"></script>
<script src="gr-plugin-rest-api.js"></script>
<script src="gr-public-js-api.js"></script>
+ <script src="gr-plugin-loader.js"></script>
+ <script src="gr-gerrit.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
index ad318d7a74..2c00c89237 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
@@ -17,11 +17,13 @@
(function() {
'use strict';
+ // Note: for new events, naming convention should be: `a-b`
const EventType = {
HISTORY: 'history',
LABEL_CHANGE: 'labelchange',
SHOW_CHANGE: 'showchange',
SUBMIT_CHANGE: 'submitchange',
+ SHOW_REVISION_ACTIONS: 'show-revision-actions',
COMMIT_MSG_EDIT: 'commitmsgedit',
COMMENT: 'comment',
REVERT: 'revert',
@@ -38,7 +40,6 @@
Polymer({
is: 'gr-js-api-interface',
- _legacyUndefinedCheck: true,
properties: {
_elements: {
@@ -71,6 +72,9 @@
case EventType.LABEL_CHANGE:
this._handleLabelChange(detail);
break;
+ case EventType.SHOW_REVISION_ACTIONS:
+ this._handleShowRevisionActions(detail);
+ break;
case EventType.HIGHLIGHTJS_LOADED:
this._handleHighlightjsLoaded(detail);
break;
@@ -136,13 +140,16 @@
//
// This clone and getter can be removed after plugins migrate to use
// info.mergeable.
- const change = Object.assign({
+ //
+ // assign on getter with existing property will report error
+ // see Issue: 12286
+ const change = Object.assign({}, detail.change, {
get mergeable() {
console.warn('Accessing change.mergeable from SHOW_CHANGE is ' +
'deprecated! Use info.mergeable instead.');
- return detail.info.mergeable;
+ return detail.info && detail.info.mergeable;
},
- }, detail.change);
+ });
const patchNum = detail.patchNum;
const info = detail.info;
@@ -163,6 +170,22 @@
}
},
+ /**
+ * @param {!{change: !Object, revisionActions: !Object}} detail
+ */
+ _handleShowRevisionActions(detail) {
+ const registeredCallbacks = this._getEventCallbacks(
+ EventType.SHOW_REVISION_ACTIONS
+ );
+ for (const cb of registeredCallbacks) {
+ try {
+ cb(detail.revisionActions, detail.change);
+ } catch (err) {
+ console.error(err);
+ }
+ }
+ },
+
handleCommitMessage(change, msg) {
for (const cb of this._getEventCallbacks(EventType.COMMIT_MSG_EDIT)) {
try {
@@ -232,30 +255,13 @@
* Retrieves coverage data possibly provided by a plugin.
*
* Will wait for plugins to be loaded. If multiple plugins offer a coverage
- * provider, the first one is used. If no plugin offers a coverage provider,
- * will resolve to [].
- *
- * TODO(brohlfs): Replace Array<Object> type by Array<Gerrit.CoverageRange>.
- *
- * @param {string|number} changeNum
- * @param {string} path
- * @param {string|number} basePatchNum
- * @param {string|number} patchNum
- * @return {!Promise<!Array<Object>>}
+ * provider, the first one is returned. If no plugin offers a coverage provider,
+ * will resolve to null.
*/
- getCoverageRanges(changeNum, path, basePatchNum, patchNum) {
- return Gerrit.awaitPluginsLoaded().then(() => {
- for (const annotationApi of
- this._getEventCallbacks(EventType.ANNOTATE_DIFF)) {
- const provider = annotationApi.getCoverageProvider();
- // Only one coverage provider makes sense. If there are more, then we
- // simply ignore them.
- if (provider) {
- return provider(changeNum, path, basePatchNum, patchNum);
- }
- }
- return [];
- });
+ getCoverageAnnotationApi() {
+ return Gerrit.awaitPluginsLoaded()
+ .then(() => this._getEventCallbacks(EventType.ANNOTATE_DIFF)
+ .find(api => api.getCoverageProvider()));
},
getAdminMenuLinks() {
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index f03aa09879..14928e611b 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-api-interface</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-js-api-interface.html">
@@ -33,6 +35,7 @@ limitations under the License.
</test-fixture>
<script>
+ const {PLUGIN_LOADING_TIMEOUT_MS} = window._apiUtils;
suite('gr-js-api-interface tests', () => {
let element;
let plugin;
@@ -46,6 +49,7 @@ limitations under the License.
};
setup(() => {
+ this.clock = sinon.useFakeTimers();
sandbox = sinon.sandbox.create();
getResponseObjectStub = sandbox.stub().returns(Promise.resolve());
sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
@@ -62,32 +66,16 @@ limitations under the License.
errorStub = sandbox.stub(console, 'error');
Gerrit.install(p => { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
- Gerrit._setPluginsPending([]);
+ Gerrit._loadPlugins([]);
});
teardown(() => {
+ this.clock.restore();
sandbox.restore();
element._removeEventCallbacks();
plugin = null;
});
- test('reuse plugin for install calls', () => {
- let otherPlugin;
- Gerrit.install(p => { otherPlugin = p; }, '0.1',
- 'http://test.com/plugins/testplugin/static/test.js');
- assert.strictEqual(plugin, otherPlugin);
- });
-
- test('flushes preinstalls if provided', () => {
- assert.doesNotThrow(() => {
- Gerrit._flushPreinstalls();
- });
- window.Gerrit.flushPreinstalls = sandbox.stub();
- Gerrit._flushPreinstalls();
- assert.isTrue(window.Gerrit.flushPreinstalls.calledOnce);
- delete window.Gerrit.flushPreinstalls;
- });
-
test('url', () => {
assert.equal(plugin.url(), 'http://test.com/plugins/testplugin/');
assert.equal(plugin.url('/static/test.js'),
@@ -203,18 +191,37 @@ limitations under the License.
{change: testChange, patchNum: 1, info: {mergeable: false}});
});
+ test('show-revision-actions event', done => {
+ const testChange = {
+ _number: 42,
+ revisions: {def: {_number: 2}, abc: {_number: 1}},
+ };
+ plugin.on(element.EventType.SHOW_REVISION_ACTIONS, throwErrFn);
+ plugin.on(element.EventType.SHOW_REVISION_ACTIONS, (actions, change) => {
+ assert.deepEqual(change, testChange);
+ assert.deepEqual(actions, {test: {}});
+ assert.isTrue(errorStub.calledOnce);
+ done();
+ });
+ element.handleEvent(element.EventType.SHOW_REVISION_ACTIONS,
+ {change: testChange, revisionActions: {test: {}}});
+ });
+
test('handleEvent awaits plugins load', done => {
const testChange = {
_number: 42,
revisions: {def: {_number: 2}, abc: {_number: 1}},
};
const spy = sandbox.spy();
- Gerrit._setPluginsCount(1);
+ Gerrit._loadPlugins(['plugins/test.html']);
plugin.on(element.EventType.SHOW_CHANGE, spy);
element.handleEvent(element.EventType.SHOW_CHANGE,
{change: testChange, patchNum: 1});
assert.isFalse(spy.called);
- Gerrit._setPluginsCount(0);
+
+ // Timeout on loading plugins
+ this.clock.tick(PLUGIN_LOADING_TIMEOUT_MS * 2);
+
flush(() => {
assert.isTrue(spy.called);
done();
@@ -311,90 +318,13 @@ limitations under the License.
element.handleEvent(element.EventType.HIGHLIGHTJS_LOADED, {hljs: testHljs});
});
- test('versioning', () => {
- const callback = sandbox.spy();
- Gerrit.install(callback, '0.0pre-alpha');
- assert(callback.notCalled);
- });
-
test('getAccount', done => {
- Gerrit.getLoggedIn().then(loggedIn => {
+ plugin.restApi().getLoggedIn().then(loggedIn => {
assert.isTrue(loggedIn);
done();
});
});
- test('_setPluginsCount', done => {
- stub('gr-reporting', {
- pluginsLoaded() {
- done();
- },
- });
- Gerrit._setPluginsCount(0);
- });
-
- test('_arePluginsLoaded', () => {
- assert.isTrue(Gerrit._arePluginsLoaded());
- Gerrit._setPluginsCount(1);
- assert.isFalse(Gerrit._arePluginsLoaded());
- Gerrit._setPluginsCount(0);
- assert.isTrue(Gerrit._arePluginsLoaded());
- });
-
- test('_pluginInstalled', () => {
- const pluginsLoadedStub = sandbox.stub();
- stub('gr-reporting', {
- pluginsLoaded: (...args) => pluginsLoadedStub(...args),
- });
- const plugins = [
- 'http://test.com/plugins/foo/static/test.js',
- 'http://test.com/plugins/bar/static/test.js',
- ];
- Gerrit._setPluginsPending(plugins);
- Gerrit._pluginInstalled(plugins[0]);
- Gerrit._pluginInstalled(plugins[1]);
- assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar']));
- });
-
- test('install calls _pluginInstalled', () => {
- sandbox.stub(Gerrit, '_pluginInstalled');
- Gerrit.install(p => { plugin = p; }, '0.1',
- 'http://test.com/plugins/testplugin/static/test.js');
-
- // testplugin has already been installed once (in setup).
- assert.isFalse(Gerrit._pluginInstalled.called);
-
- // testplugin2 plugin has not yet been installed.
- Gerrit.install(p => { plugin = p; }, '0.1',
- 'http://test.com/plugins/testplugin2/static/test.js');
- assert.isTrue(Gerrit._pluginInstalled.calledOnce);
- });
-
- test('plugin install errors mark plugins as loaded', () => {
- Gerrit._setPluginsCount(1);
- Gerrit.install(() => {}, '0.0pre-alpha');
- return Gerrit.awaitPluginsLoaded();
- });
-
- test('multiple ui plugins per java plugin', () => {
- const file1 = 'http://test.com/plugins/qaz/static/foo.nocache.js';
- const file2 = 'http://test.com/plugins/qaz/static/bar.js';
- Gerrit._setPluginsPending([file1, file2]);
- Gerrit.install(() => {}, '0.1', file1);
- Gerrit.install(() => {}, '0.1', file2);
- return Gerrit.awaitPluginsLoaded();
- });
-
- test('plugin install errors shows toasts', () => {
- const alertStub = sandbox.stub();
- document.addEventListener('show-alert', alertStub);
- Gerrit._setPluginsCount(1);
- Gerrit.install(() => {}, '0.0pre-alpha');
- return Gerrit.awaitPluginsLoaded().then(() => {
- assert.isTrue(alertStub.calledOnce);
- });
- });
-
test('attributeHelper', () => {
assert.isOk(plugin.attributeHelper());
});
@@ -420,40 +350,12 @@ limitations under the License.
element.EventType.ADMIN_MENU_LINKS);
});
- test('Gerrit._isPluginPreloaded', () => {
- Gerrit._preloadedPlugins = {baz: ()=>{}};
- assert.isFalse(Gerrit._isPluginPreloaded('plugins/foo/bar'));
- assert.isFalse(Gerrit._isPluginPreloaded('http://a.com/42'));
- assert.isTrue(Gerrit._isPluginPreloaded('preloaded:baz'));
- Gerrit._preloadedPlugins = null;
- });
-
- test('preloaded plugins are installed', () => {
- const installStub = sandbox.stub();
- Gerrit._preloadedPlugins = {foo: installStub};
- Gerrit._installPreloadedPlugins();
- assert.isTrue(installStub.called);
- const pluginApi = installStub.lastCall.args[0];
- assert.strictEqual(pluginApi.getPluginName(), 'foo');
- });
-
- test('installing preloaded plugin', () => {
- let plugin;
- window.ASSETS_PATH = 'http://blips.com/chitz';
- Gerrit.install(p => { plugin = p; }, '0.1', 'preloaded:foo');
- assert.strictEqual(plugin.getPluginName(), 'foo');
- assert.strictEqual(plugin.url('/some/thing.html'),
- 'http://blips.com/chitz/plugins/foo/some/thing.html');
- delete window.ASSETS_PATH;
- });
-
suite('test plugin with base url', () => {
let baseUrlPlugin;
setup(() => {
sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl').returns('/r');
- Gerrit._setPluginsCount(1);
Gerrit.install(p => { baseUrlPlugin = p; }, '0.1',
'http://test.com/r/plugins/baseurlplugin/static/test.js');
});
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html
index ca87956488..6da117f40e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-plugin-action-context</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-js-api-interface.html"/>
@@ -40,7 +42,6 @@ limitations under the License.
setup(() => {
sandbox = sinon.sandbox.create();
- Gerrit._setPluginsCount(1);
Gerrit.install(p => { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
instance = new GrPluginActionContext(plugin);
@@ -140,12 +141,12 @@ limitations under the License.
send: sendStub,
});
const errorStub = sandbox.stub();
- document.addEventListener('network-error', errorStub);
+ document.addEventListener('show-alert', errorStub);
instance.call();
flush(() => {
assert.isTrue(errorStub.calledOnce);
assert.equal(errorStub.args[0][0].detail.message,
- 'Plugin network error: boom');
+ 'Plugin network error: Error: boom');
done();
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html
index b00b5acbcc..8ed7f14bcf 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-plugin-endpoints</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-js-api-interface.html"/>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
new file mode 100644
index 0000000000..201b68302b
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
@@ -0,0 +1,397 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function(window) {
+ 'use strict';
+
+ // Import utils methods
+ const {
+ PLUGIN_LOADING_TIMEOUT_MS,
+ PRELOADED_PROTOCOL,
+ getPluginNameFromUrl,
+ getBaseUrl,
+ } = window._apiUtils;
+
+ /**
+ * @enum {string}
+ */
+ const PluginState = {
+ /**
+ * State that indicates the plugin is pending to be loaded.
+ */
+ PENDING: 'PENDING',
+
+ /**
+ * State that indicates the plugin is already loaded.
+ */
+ LOADED: 'LOADED',
+
+ /**
+ * State that indicates the plugin is already loaded.
+ */
+ PRE_LOADED: 'PRE_LOADED',
+
+ /**
+ * State that indicates the plugin failed to load.
+ */
+ LOAD_FAILED: 'LOAD_FAILED',
+ };
+
+ // Prefix for any unrecognized plugin urls.
+ // Url should match following patterns:
+ // /plugins/PLUGINNAME/static/SCRIPTNAME.(html|js)
+ // /plugins/PLUGINNAME.(js|html)
+ const UNKNOWN_PLUGIN_PREFIX = '__$$__';
+
+ // Current API version for Plugin,
+ // plugins with incompatible version will not be laoded.
+ const API_VERSION = '0.1';
+
+ /**
+ * PluginLoader, responsible for:
+ *
+ * Loading all plugins and handling errors etc.
+ * Recording plugin state.
+ * Reporting on plugin loading status.
+ * Retrieve plugin.
+ * Check plugin status and if all plugins loaded.
+ */
+ class PluginLoader {
+ constructor() {
+ this._pluginListLoaded = false;
+
+ /** @type {Map<string,PluginLoader.PluginObject>} */
+ this._plugins = new Map();
+
+ this._reporting = null;
+
+ // Promise that resolves when all plugins loaded
+ this._loadingPromise = null;
+
+ // Resolver to resolve _loadingPromise once all plugins loaded
+ this._loadingResolver = null;
+ }
+
+ _getReporting() {
+ if (!this._reporting) {
+ this._reporting = document.createElement('gr-reporting');
+ }
+ return this._reporting;
+ }
+
+ /**
+ * Use the plugin name or use the full url if not recognized.
+ *
+ * @see gr-api-utils#getPluginNameFromUrl
+ * @param {string|URL} url
+ */
+ _getPluginKeyFromUrl(url) {
+ return getPluginNameFromUrl(url) ||
+ `${UNKNOWN_PLUGIN_PREFIX}${url}`;
+ }
+
+ /**
+ * Load multiple plugins with certain options.
+ *
+ * @param {Array<string>} plugins
+ * @param {Object<string, PluginLoader.PluginOption>} opts
+ */
+ loadPlugins(plugins = [], opts = {}) {
+ this._pluginListLoaded = true;
+
+ plugins.forEach(path => {
+ const url = this._urlFor(path);
+ // Skip if preloaded, for bundling.
+ if (this.isPluginPreloaded(url)) return;
+
+ const pluginKey = this._getPluginKeyFromUrl(url);
+ // Skip if already installed.
+ if (this._plugins.has(pluginKey)) return;
+ this._plugins.set(pluginKey, {
+ name: pluginKey,
+ url,
+ state: PluginState.PENDING,
+ plugin: null,
+ });
+
+ if (this._isPathEndsWith(url, '.html')) {
+ this._importHtmlPlugin(url, opts && opts[path]);
+ } else if (this._isPathEndsWith(url, '.js')) {
+ this._loadJsPlugin(url);
+ } else {
+ this._failToLoad(`Unrecognized plugin url ${url}`, url);
+ }
+ });
+
+ this.awaitPluginsLoaded().then(() => {
+ console.info('Plugins loaded');
+ this._getReporting().pluginsLoaded(this._getAllInstalledPluginNames());
+ });
+ }
+
+ _isPathEndsWith(url, suffix) {
+ if (!(url instanceof URL)) {
+ try {
+ url = new URL(url);
+ } catch (e) {
+ console.warn(e);
+ return false;
+ }
+ }
+
+ return url.pathname && url.pathname.endsWith(suffix);
+ }
+
+ _getAllInstalledPluginNames() {
+ const installedPlugins = [];
+ for (const plugin of this._plugins.values()) {
+ if (plugin.state === PluginState.LOADED) {
+ installedPlugins.push(plugin.name);
+ }
+ }
+ return installedPlugins;
+ }
+
+ install(callback, opt_version, opt_src) {
+ // HTML import polyfill adds __importElement pointing to the import tag.
+ const script = document.currentScript &&
+ (document.currentScript.__importElement || document.currentScript);
+ let src = opt_src || (script && script.src);
+ if (!src || src.startsWith('data:')) {
+ src = script && script.baseURI;
+ }
+
+ if (opt_version && opt_version !== API_VERSION) {
+ this._failToLoad(`Plugin ${src} install error: only version ` +
+ API_VERSION + ' is supported in PolyGerrit. ' + opt_version +
+ ' was given.', src);
+ return;
+ }
+
+ const pluginObject = this.getPlugin(src);
+ let plugin = pluginObject && pluginObject.plugin;
+ if (!plugin) {
+ plugin = new Plugin(src);
+ }
+ try {
+ callback(plugin);
+ this._pluginInstalled(src, plugin);
+ } catch (e) {
+ this._failToLoad(`${e.name}: ${e.message}`, src);
+ }
+ }
+
+ get arePluginsLoaded() {
+ // As the size of plugins is relatively small,
+ // so the performance of this check should be reasonable
+ if (!this._pluginListLoaded) return false;
+ for (const plugin of this._plugins.values()) {
+ if (plugin.state === PluginState.PENDING) return false;
+ }
+ return true;
+ }
+
+ _checkIfCompleted() {
+ if (this.arePluginsLoaded && this._loadingResolver) {
+ this._loadingResolver();
+ this._loadingResolver = null;
+ this._loadingPromise = null;
+ }
+ }
+
+ _timeout() {
+ const pendingPlugins = [];
+ for (const plugin of this._plugins.values()) {
+ if (plugin.state === PluginState.PENDING) {
+ this._updatePluginState(plugin.url, PluginState.LOAD_FAILED);
+ this._checkIfCompleted();
+ pendingPlugins.push(plugin.url);
+ }
+ }
+ return `Timeout when loading plugins: ${pendingPlugins.join(',')}`;
+ }
+
+ _failToLoad(message, pluginUrl) {
+ // Show an alert with the error
+ document.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: `Plugin install error: ${message} from ${pluginUrl}`,
+ },
+ }));
+ this._updatePluginState(pluginUrl, PluginState.LOAD_FAILED);
+ this._checkIfCompleted();
+ }
+
+ _updatePluginState(pluginUrl, state) {
+ const key = this._getPluginKeyFromUrl(pluginUrl);
+ if (this._plugins.has(key)) {
+ this._plugins.get(key).state = state;
+ } else {
+ // Plugin is not recorded for some reason.
+ console.warn(`Plugin loaded separately: ${pluginUrl}`);
+ this._plugins.set(key, {
+ name: key,
+ url: pluginUrl,
+ state,
+ plugin: null,
+ });
+ }
+ return this._plugins.get(key);
+ }
+
+ _pluginInstalled(url, plugin) {
+ const pluginObj = this._updatePluginState(url, PluginState.LOADED);
+ pluginObj.plugin = plugin;
+ this._getReporting().pluginLoaded(plugin.getPluginName() || url);
+ console.log(`Plugin ${plugin.getPluginName() || url} installed.`);
+ this._checkIfCompleted();
+ }
+
+ installPreloadedPlugins() {
+ if (!window.Gerrit || !window.Gerrit._preloadedPlugins) { return; }
+ const Gerrit = window.Gerrit;
+ for (const name in Gerrit._preloadedPlugins) {
+ if (!Gerrit._preloadedPlugins.hasOwnProperty(name)) { continue; }
+ const callback = Gerrit._preloadedPlugins[name];
+ this.install(callback, API_VERSION, PRELOADED_PROTOCOL + name);
+ }
+ }
+
+ isPluginPreloaded(pathOrUrl) {
+ const url = this._urlFor(pathOrUrl);
+ const name = getPluginNameFromUrl(url);
+ if (name && window.Gerrit._preloadedPlugins) {
+ return window.Gerrit._preloadedPlugins.hasOwnProperty(name);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if given plugin path/url is enabled or not.
+ *
+ * @param {string} pathOrUrl
+ */
+ isPluginEnabled(pathOrUrl) {
+ const url = this._urlFor(pathOrUrl);
+ if (this.isPluginPreloaded(url)) return true;
+ const key = this._getPluginKeyFromUrl(url);
+ return this._plugins.has(key);
+ }
+
+ /**
+ * Returns the plugin object with a given url.
+ *
+ * @param {string} pathOrUrl
+ */
+ getPlugin(pathOrUrl) {
+ const key = this._getPluginKeyFromUrl(this._urlFor(pathOrUrl));
+ return this._plugins.get(key);
+ }
+
+ /**
+ * Checks if given plugin path/url is loaded or not.
+ *
+ * @param {string} pathOrUrl
+ */
+ isPluginLoaded(pathOrUrl) {
+ const url = this._urlFor(pathOrUrl);
+ const key = this._getPluginKeyFromUrl(url);
+ return this._plugins.has(key) ?
+ this._plugins.get(key).state === PluginState.LOADED :
+ false;
+ }
+
+ _importHtmlPlugin(pluginUrl, opts = {}) {
+ // onload (second param) needs to be a function. When null or undefined
+ // were passed, plugins were not loaded correctly.
+ (Polymer.importHref || Polymer.Base.importHref)(
+ this._urlFor(pluginUrl), () => {},
+ () => this._failToLoad(`${pluginUrl} import error`, pluginUrl),
+ !opts.sync);
+ }
+
+ _loadJsPlugin(pluginUrl) {
+ this._createScriptTag(this._urlFor(pluginUrl));
+ }
+
+ _createScriptTag(url) {
+ const el = document.createElement('script');
+ el.defer = true;
+ el.src = url;
+ el.onerror = () => this._failToLoad(`${url} load error`, url);
+ return document.body.appendChild(el);
+ }
+
+ _urlFor(pathOrUrl) {
+ if (!pathOrUrl) {
+ return pathOrUrl;
+ }
+ if (pathOrUrl.startsWith(PRELOADED_PROTOCOL) ||
+ pathOrUrl.startsWith('http')) {
+ // Plugins are loaded from another domain or preloaded.
+ return pathOrUrl;
+ }
+ if (!pathOrUrl.startsWith('/')) {
+ pathOrUrl = '/' + pathOrUrl;
+ }
+ return window.location.origin + getBaseUrl() + pathOrUrl;
+ }
+
+ awaitPluginsLoaded() {
+ // Resolve if completed.
+ this._checkIfCompleted();
+
+ if (this.arePluginsLoaded) {
+ return Promise.resolve();
+ }
+ if (!this._loadingPromise) {
+ let timerId;
+ this._loadingPromise =
+ Promise.race([
+ new Promise(resolve => this._loadingResolver = resolve),
+ new Promise((_, reject) => timerId = setTimeout(
+ () => {
+ reject(this._timeout());
+ }, PLUGIN_LOADING_TIMEOUT_MS)),
+ ]).then(() => {
+ if (timerId) clearTimeout(timerId);
+ });
+ }
+ return this._loadingPromise;
+ }
+ }
+
+ /**
+ * @typedef {{
+ * name:string,
+ * url:string,
+ * state:PluginState,
+ * plugin:Object
+ * }}
+ */
+ PluginLoader.PluginObject;
+
+ /**
+ * @typedef {{
+ * sync:boolean,
+ * }}
+ */
+ PluginLoader.PluginOption;
+
+ window.PluginLoader = PluginLoader;
+})(window); \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html
new file mode 100644
index 0000000000..ee54319bf0
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html
@@ -0,0 +1,502 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-plugin-host</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-js-api-interface.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-js-api-interface></gr-js-api-interface>
+ </template>
+</test-fixture>
+
+<script>
+ const {PRELOADED_PROTOCOL, PLUGIN_LOADING_TIMEOUT_MS} = window._apiUtils;
+ suite('gr-plugin-loader tests', () => {
+ let plugin;
+ let sandbox;
+ let url;
+ let sendStub;
+
+ setup(() => {
+ this.clock = sinon.useFakeTimers();
+ sandbox = sinon.sandbox.create();
+ sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
+ stub('gr-rest-api-interface', {
+ getAccount() {
+ return Promise.resolve({name: 'Judy Hopps'});
+ },
+ send(...args) {
+ return sendStub(...args);
+ },
+ });
+ sandbox.stub(document.body, 'appendChild');
+ fixture('basic');
+ url = window.location.origin;
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ this.clock.restore();
+ Gerrit._testOnly_resetPlugins();
+ });
+
+ test('reuse plugin for install calls', () => {
+ Gerrit.install(p => { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+
+ let otherPlugin;
+ Gerrit.install(p => { otherPlugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+ assert.strictEqual(plugin, otherPlugin);
+ });
+
+ test('flushes preinstalls if provided', () => {
+ assert.doesNotThrow(() => {
+ Gerrit._testOnly_flushPreinstalls();
+ });
+ window.Gerrit.flushPreinstalls = sandbox.stub();
+ Gerrit._testOnly_flushPreinstalls();
+ assert.isTrue(window.Gerrit.flushPreinstalls.calledOnce);
+ delete window.Gerrit.flushPreinstalls;
+ });
+
+ test('versioning', () => {
+ const callback = sandbox.spy();
+ Gerrit.install(callback, '0.0pre-alpha');
+ assert(callback.notCalled);
+ });
+
+ test('report pluginsLoaded', done => {
+ stub('gr-reporting', {
+ pluginsLoaded() {
+ done();
+ },
+ });
+ Gerrit._loadPlugins([]);
+ });
+
+ test('arePluginsLoaded', done => {
+ assert.isFalse(Gerrit._arePluginsLoaded());
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ ];
+
+ Gerrit._loadPlugins(plugins);
+ assert.isFalse(Gerrit._arePluginsLoaded());
+ // Timeout on loading plugins
+ this.clock.tick(PLUGIN_LOADING_TIMEOUT_MS * 2);
+
+ flush(() => {
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ done();
+ });
+ });
+
+ test('plugins installed successfully', done => {
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ Gerrit.install(() => void 0, undefined, url);
+ });
+ const pluginsLoadedStub = sandbox.stub();
+ stub('gr-reporting', {
+ pluginsLoaded: (...args) => pluginsLoadedStub(...args),
+ });
+
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ ];
+ Gerrit._loadPlugins(plugins);
+
+ flush(() => {
+ assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar']));
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ done();
+ });
+ });
+
+ test('isPluginEnabled and isPluginLoaded', done => {
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ Gerrit.install(() => void 0, undefined, url);
+ });
+ const pluginsLoadedStub = sandbox.stub();
+ stub('gr-reporting', {
+ pluginsLoaded: (...args) => pluginsLoadedStub(...args),
+ });
+
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ 'bar/static/test.js',
+ ];
+ Gerrit._loadPlugins(plugins);
+ assert.isTrue(
+ plugins.every(plugin => Gerrit._pluginLoader.isPluginEnabled(plugin))
+ );
+
+ flush(() => {
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ assert.isTrue(
+ plugins.every(plugin => Gerrit._pluginLoader.isPluginLoaded(plugin))
+ );
+
+ done();
+ });
+ });
+
+ test('plugins installed mixed result, 1 fail 1 succeed', done => {
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ ];
+
+ const alertStub = sandbox.stub();
+ document.addEventListener('show-alert', alertStub);
+
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ Gerrit.install(() => {
+ if (url === plugins[0]) {
+ throw new Error('failed');
+ }
+ }, undefined, url);
+ });
+
+ const pluginsLoadedStub = sandbox.stub();
+ stub('gr-reporting', {
+ pluginsLoaded: (...args) => pluginsLoadedStub(...args),
+ });
+
+ Gerrit._loadPlugins(plugins);
+
+ flush(() => {
+ assert.isTrue(pluginsLoadedStub.calledWithExactly(['bar']));
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ assert.isTrue(alertStub.calledOnce);
+ done();
+ });
+ });
+
+ test('isPluginEnabled and isPluginLoaded for mixed results', done => {
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ ];
+
+ const alertStub = sandbox.stub();
+ document.addEventListener('show-alert', alertStub);
+
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ Gerrit.install(() => {
+ if (url === plugins[0]) {
+ throw new Error('failed');
+ }
+ }, undefined, url);
+ });
+
+ const pluginsLoadedStub = sandbox.stub();
+ stub('gr-reporting', {
+ pluginsLoaded: (...args) => pluginsLoadedStub(...args),
+ });
+
+ Gerrit._loadPlugins(plugins);
+ assert.isTrue(
+ plugins.every(plugin => Gerrit._pluginLoader.isPluginEnabled(plugin))
+ );
+
+ flush(() => {
+ assert.isTrue(pluginsLoadedStub.calledWithExactly(['bar']));
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ assert.isTrue(alertStub.calledOnce);
+ assert.isTrue(Gerrit._pluginLoader.isPluginLoaded(plugins[1]));
+ assert.isFalse(Gerrit._pluginLoader.isPluginLoaded(plugins[0]));
+ done();
+ });
+ });
+
+ test('plugins installed all failed', done => {
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ ];
+
+ const alertStub = sandbox.stub();
+ document.addEventListener('show-alert', alertStub);
+
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ Gerrit.install(() => {
+ throw new Error('failed');
+ }, undefined, url);
+ });
+
+ const pluginsLoadedStub = sandbox.stub();
+ stub('gr-reporting', {
+ pluginsLoaded: (...args) => pluginsLoadedStub(...args),
+ });
+
+ Gerrit._loadPlugins(plugins);
+
+ flush(() => {
+ assert.isTrue(pluginsLoadedStub.calledWithExactly([]));
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ assert.isTrue(alertStub.calledTwice);
+ done();
+ });
+ });
+
+ test('plugins installed failed becasue of wrong version', done => {
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ ];
+
+ const alertStub = sandbox.stub();
+ document.addEventListener('show-alert', alertStub);
+
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ Gerrit.install(() => {
+ }, url === plugins[0] ? '' : 'alpha', url);
+ });
+
+ const pluginsLoadedStub = sandbox.stub();
+ stub('gr-reporting', {
+ pluginsLoaded: (...args) => pluginsLoadedStub(...args),
+ });
+
+ Gerrit._loadPlugins(plugins);
+
+ flush(() => {
+ assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo']));
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ assert.isTrue(alertStub.calledOnce);
+ done();
+ });
+ });
+
+ test('multiple assets for same plugin installed successfully', done => {
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ Gerrit.install(() => void 0, undefined, url);
+ });
+ const pluginsLoadedStub = sandbox.stub();
+ stub('gr-reporting', {
+ pluginsLoaded: (...args) => pluginsLoadedStub(...args),
+ });
+
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/foo/static/test2.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ ];
+ Gerrit._loadPlugins(plugins);
+
+ flush(() => {
+ assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar']));
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ done();
+ });
+ });
+
+ suite('plugin path and url', () => {
+ let importHtmlPluginStub;
+ let loadJsPluginStub;
+ setup(() => {
+ importHtmlPluginStub = sandbox.stub();
+ sandbox.stub(Gerrit._pluginLoader, '_importHtmlPlugin', url => {
+ importHtmlPluginStub(url);
+ });
+ loadJsPluginStub = sandbox.stub();
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ loadJsPluginStub(url);
+ });
+ });
+
+ test('invalid plugin path', () => {
+ const failToLoadStub = sandbox.stub();
+ sandbox.stub(Gerrit._pluginLoader, '_failToLoad', (...args) => {
+ failToLoadStub(...args);
+ });
+
+ Gerrit._loadPlugins([
+ 'foo/bar',
+ ]);
+
+ assert.isTrue(failToLoadStub.calledOnce);
+ assert.isTrue(failToLoadStub.calledWithExactly(
+ `Unrecognized plugin url ${url}/foo/bar`,
+ `${url}/foo/bar`
+ ));
+ });
+
+ test('relative path for plugins', () => {
+ Gerrit._loadPlugins([
+ 'foo/bar.js',
+ 'foo/bar.html',
+ ]);
+
+ assert.isTrue(importHtmlPluginStub.calledOnce);
+ assert.isTrue(
+ importHtmlPluginStub.calledWithExactly(`${url}/foo/bar.html`)
+ );
+ assert.isTrue(loadJsPluginStub.calledOnce);
+ assert.isTrue(
+ loadJsPluginStub.calledWithExactly(`${url}/foo/bar.js`)
+ );
+ });
+
+
+ test('relative path should honor getBaseUrl', () => {
+ const testUrl = '/test';
+ sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl', () => {
+ return testUrl;
+ });
+
+ Gerrit._loadPlugins([
+ 'foo/bar.js',
+ 'foo/bar.html',
+ ]);
+
+ assert.isTrue(importHtmlPluginStub.calledOnce);
+ assert.isTrue(loadJsPluginStub.calledOnce);
+ assert.isTrue(
+ importHtmlPluginStub.calledWithExactly(
+ `${url}${testUrl}/foo/bar.html`
+ )
+ );
+ assert.isTrue(
+ loadJsPluginStub.calledWithExactly(`${url}${testUrl}/foo/bar.js`)
+ );
+ });
+
+ test('absolute path for plugins', () => {
+ Gerrit._loadPlugins([
+ 'http://e.com/foo/bar.js',
+ 'http://e.com/foo/bar.html',
+ ]);
+
+ assert.isTrue(importHtmlPluginStub.calledOnce);
+ assert.isTrue(
+ importHtmlPluginStub.calledWithExactly(`http://e.com/foo/bar.html`)
+ );
+ assert.isTrue(loadJsPluginStub.calledOnce);
+ assert.isTrue(
+ loadJsPluginStub.calledWithExactly(`http://e.com/foo/bar.js`)
+ );
+ });
+ });
+
+ test('adds js plugins will call the body', () => {
+ Gerrit._loadPlugins([
+ 'http://e.com/foo/bar.js',
+ 'http://e.com/bar/foo.js',
+ ]);
+ assert.isTrue(document.body.appendChild.calledTwice);
+ });
+
+ test('can call awaitPluginsLoaded multiple times', done => {
+ const plugins = [
+ 'http://e.com/foo/bar.js',
+ 'http://e.com/bar/foo.js',
+ ];
+
+ let installed = false;
+ function pluginCallback(url) {
+ if (url === plugins[1]) {
+ installed = true;
+ }
+ }
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ Gerrit.install(() => pluginCallback(url), undefined, url);
+ });
+
+ Gerrit._loadPlugins(plugins);
+
+ Gerrit.awaitPluginsLoaded().then(() => {
+ assert.isTrue(installed);
+
+ Gerrit.awaitPluginsLoaded().then(() => {
+ done();
+ });
+ });
+ });
+
+ suite('preloaded plugins', () => {
+ test('skips preloaded plugins when load plugins', () => {
+ const importHtmlPluginStub = sandbox.stub();
+ sandbox.stub(Gerrit._pluginLoader, '_importHtmlPlugin', url => {
+ importHtmlPluginStub(url);
+ });
+ const loadJsPluginStub = sandbox.stub();
+ sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+ loadJsPluginStub(url);
+ });
+
+ Gerrit._preloadedPlugins = {
+ foo: () => void 0,
+ bar: () => void 0,
+ };
+
+ Gerrit._loadPlugins([
+ 'http://e.com/plugins/foo.js',
+ 'plugins/bar.html',
+ 'http://e.com/plugins/test/foo.js',
+ ]);
+
+ assert.isTrue(importHtmlPluginStub.notCalled);
+ assert.isTrue(loadJsPluginStub.calledOnce);
+ });
+
+ test('isPluginPreloaded', () => {
+ Gerrit._preloadedPlugins = {baz: ()=>{}};
+ assert.isFalse(Gerrit._pluginLoader.isPluginPreloaded('plugins/foo/bar'));
+ assert.isFalse(Gerrit._pluginLoader.isPluginPreloaded('http://a.com/42'));
+ assert.isTrue(
+ Gerrit._pluginLoader.isPluginPreloaded(PRELOADED_PROTOCOL + 'baz')
+ );
+ Gerrit._preloadedPlugins = null;
+ });
+
+ test('preloaded plugins are installed', () => {
+ const installStub = sandbox.stub();
+ Gerrit._preloadedPlugins = {foo: installStub};
+ Gerrit._pluginLoader.installPreloadedPlugins();
+ assert.isTrue(installStub.called);
+ const pluginApi = installStub.lastCall.args[0];
+ assert.strictEqual(pluginApi.getPluginName(), 'foo');
+ });
+
+ test('installing preloaded plugin', () => {
+ let plugin;
+ window.ASSETS_PATH = 'http://blips.com/chitz';
+ Gerrit.install(p => { plugin = p; }, '0.1', 'preloaded:foo');
+ assert.strictEqual(plugin.getPluginName(), 'foo');
+ assert.strictEqual(plugin.url('/some/thing.html'),
+ 'http://blips.com/chitz/plugins/foo/some/thing.html');
+ delete window.ASSETS_PATH;
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
index ce619a0190..ecfafb5d3d 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js
@@ -46,6 +46,19 @@
getRestApi().invalidateReposCache();
};
+ GrPluginRestApi.prototype.getAccount = function() {
+ return getRestApi().getAccount();
+ };
+
+ GrPluginRestApi.prototype.getAccountCapabilities = function(capabilities) {
+ return getRestApi().getAccountCapabilities(capabilities);
+ };
+
+ GrPluginRestApi.prototype.getRepos =
+ function(filter, reposPerPage, opt_offset) {
+ return getRestApi().getRepos(filter, reposPerPage, opt_offset);
+ };
+
/**
* Fetch and return native browser REST API Response.
*
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html
index 7a59337771..bcbd961c00 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-plugin-rest-api</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-js-api-interface.html"/>
@@ -48,7 +50,6 @@ limitations under the License.
a[k] = (...args) => restApiStub[k](...args);
return a;
}, {}));
- Gerrit._setPluginsCount(1);
Gerrit.install(p => { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
instance = new GrPluginRestApi();
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 7376a1222f..b261a90a01 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -17,71 +17,18 @@
(function(window) {
'use strict';
- /**
- * Hash of loaded and installed plugins, name to Plugin object.
- */
- const _plugins = {};
-
- /**
- * Array of plugin URLs to be loaded, name to url.
- */
- let _pluginsPending = {};
-
- let _pluginsInstalled = [];
-
- let _pluginsPendingCount = -1;
-
const PRELOADED_PROTOCOL = 'preloaded:';
- const UNKNOWN_PLUGIN = 'unknown';
-
const PANEL_ENDPOINTS_MAPPING = {
CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK: 'change-view-integration',
CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK: 'change-metadata-item',
};
- const PLUGIN_LOADING_TIMEOUT_MS = 10000;
-
- let _restAPI;
-
- const getRestAPI = () => {
- if (!_restAPI) {
- _restAPI = document.createElement('gr-rest-api-interface');
- }
- return _restAPI;
- };
-
- let _reporting;
- const getReporting = () => {
- if (!_reporting) {
- _reporting = document.createElement('gr-reporting');
- }
- return _reporting;
- };
-
- // TODO (viktard): deprecate in favor of GrPluginRestApi.
- function send(method, url, opt_callback, opt_payload) {
- return getRestAPI().send(method, url, opt_payload).then(response => {
- if (response.status < 200 || response.status >= 300) {
- return response.text().then(text => {
- if (text) {
- return Promise.reject(text);
- } else {
- return Promise.reject(response.status);
- }
- });
- } else {
- return getRestAPI().getResponseObject(response);
- }
- }).then(response => {
- if (opt_callback) {
- opt_callback(response);
- }
- return response;
- });
- }
-
- const API_VERSION = '0.1';
+ // Import utils methods
+ const {
+ getPluginNameFromUrl,
+ send,
+ } = window._apiUtils;
/**
* Plugin-provided custom components can affect content in extension
@@ -99,50 +46,6 @@
STYLE: 'style',
};
- function flushPreinstalls() {
- if (window.Gerrit.flushPreinstalls) {
- window.Gerrit.flushPreinstalls();
- }
- }
-
- function installPreloadedPlugins() {
- if (!Gerrit._preloadedPlugins) { return; }
- for (const name in Gerrit._preloadedPlugins) {
- if (!Gerrit._preloadedPlugins.hasOwnProperty(name)) { continue; }
- const callback = Gerrit._preloadedPlugins[name];
- Gerrit.install(callback, API_VERSION, PRELOADED_PROTOCOL + name);
- }
- }
-
- function getPluginNameFromUrl(url) {
- if (!(url instanceof URL)) {
- try {
- url = new URL(url);
- } catch (e) {
- console.warn(e);
- return null;
- }
- }
- if (url.protocol === PRELOADED_PROTOCOL) {
- return url.pathname;
- }
- const base = Gerrit.BaseUrlBehavior.getBaseUrl();
- const pathname = url.pathname.replace(base, '');
- // Site theme is server from predefined path.
- if (pathname === '/static/gerrit-theme.html') {
- return 'gerrit-theme';
- } else if (!pathname.startsWith('/plugins')) {
- console.warn('Plugin not being loaded from /plugins base path:',
- url.href, '— Unable to determine name.');
- return;
- }
- // Pathname should normally look like this:
- // /plugins/PLUGINNAME/static/SCRIPTNAME.html
- // Or, for app/samples:
- // /plugins/PLUGINNAME.html
- return pathname.split('/')[2].split('.')[0];
- }
-
function Plugin(opt_url) {
this._domHooks = new GrDomHooksManager(this);
@@ -315,6 +218,10 @@
return new GrSettingsApi(this);
};
+ Plugin.prototype.styles = function() {
+ return new GrStylesApi();
+ };
+
/**
* To make REST requests for plugin-provided endpoints, use
*
@@ -361,10 +268,14 @@
return;
}
return this.registerCustomComponent(
- Gerrit._getPluginScreenName(this.getPluginName(), screenName),
+ this._getScreenName(screenName),
opt_moduleName);
};
+ Plugin.prototype._getScreenName = function(screenName) {
+ return `${this.getPluginName()}-screen-${screenName}`;
+ };
+
const deprecatedAPI = {
_loadedGwt: ()=> {},
@@ -415,7 +326,7 @@
'Please use strings for patterns.');
return;
}
- this.hook(Gerrit._getPluginScreenName(this.getPluginName(), pattern))
+ this.hook(this._getScreenName(pattern))
.onAttached(el => {
el.style.display = 'none';
callback({
@@ -472,243 +383,5 @@
},
};
- flushPreinstalls();
-
- const Gerrit = window.Gerrit || {};
-
- let _resolveAllPluginsLoaded = null;
- let _allPluginsPromise = null;
-
- Gerrit._endpoints = new GrPluginEndpoints();
-
- // Provide reset plugins function to clear installed plugins between tests.
- const app = document.querySelector('#app');
- if (!app) {
- // No gr-app found (running tests)
- Gerrit._installPreloadedPlugins = installPreloadedPlugins;
- Gerrit._flushPreinstalls = flushPreinstalls;
- Gerrit._resetPlugins = () => {
- _allPluginsPromise = null;
- _pluginsInstalled = [];
- _pluginsPending = {};
- _pluginsPendingCount = -1;
- _reporting = null;
- _resolveAllPluginsLoaded = null;
- _restAPI = null;
- Gerrit._endpoints = new GrPluginEndpoints();
- for (const k of Object.keys(_plugins)) {
- delete _plugins[k];
- }
- };
- }
-
- Gerrit.getPluginName = function() {
- console.warn('Gerrit.getPluginName is not supported in PolyGerrit.',
- 'Please use plugin.getPluginName() instead.');
- };
-
- Gerrit.css = function(rulesStr) {
- if (!Gerrit._customStyleSheet) {
- const styleEl = document.createElement('style');
- document.head.appendChild(styleEl);
- Gerrit._customStyleSheet = styleEl.sheet;
- }
-
- const name = '__pg_js_api_class_' +
- Gerrit._customStyleSheet.cssRules.length;
- Gerrit._customStyleSheet.insertRule('.' + name + '{' + rulesStr + '}', 0);
- return name;
- };
-
- Gerrit.install = function(callback, opt_version, opt_src) {
- // HTML import polyfill adds __importElement pointing to the import tag.
- const script = document.currentScript &&
- (document.currentScript.__importElement || document.currentScript);
- const src = opt_src || (script && (script.src || script.baseURI));
- const name = getPluginNameFromUrl(src);
-
- if (opt_version && opt_version !== API_VERSION) {
- Gerrit._pluginInstallError(`Plugin ${name} install error: only version ` +
- API_VERSION + ' is supported in PolyGerrit. ' + opt_version +
- ' was given.');
- return;
- }
-
- const existingPlugin = _plugins[name];
- const plugin = existingPlugin || new Plugin(src);
- try {
- callback(plugin);
- if (name) {
- _plugins[name] = plugin;
- }
- if (!existingPlugin) {
- Gerrit._pluginInstalled(src);
- }
- } catch (e) {
- Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
- }
- };
-
- Gerrit.getLoggedIn = function() {
- console.warn('Gerrit.getLoggedIn() is deprecated! ' +
- 'Use plugin.restApi().getLoggedIn()');
- return document.createElement('gr-rest-api-interface').getLoggedIn();
- };
-
- Gerrit.get = function(url, callback) {
- console.warn('.get() is deprecated! Use plugin.restApi().get()');
- send('GET', url, callback);
- };
-
- Gerrit.post = function(url, payload, callback) {
- console.warn('.post() is deprecated! Use plugin.restApi().post()');
- send('POST', url, callback, payload);
- };
-
- Gerrit.put = function(url, payload, callback) {
- console.warn('.put() is deprecated! Use plugin.restApi().put()');
- send('PUT', url, callback, payload);
- };
-
- Gerrit.delete = function(url, opt_callback) {
- console.warn('.delete() is deprecated! Use plugin.restApi().delete()');
- return getRestAPI().send('DELETE', url).then(response => {
- if (response.status !== 204) {
- return response.text().then(text => {
- if (text) {
- return Promise.reject(text);
- } else {
- return Promise.reject(response.status);
- }
- });
- }
- if (opt_callback) {
- opt_callback(response);
- }
- return response;
- });
- };
-
- Gerrit.awaitPluginsLoaded = function() {
- if (!_allPluginsPromise) {
- if (Gerrit._arePluginsLoaded()) {
- _allPluginsPromise = Promise.resolve();
- } else {
- let timeoutId;
- _allPluginsPromise =
- Promise.race([
- new Promise(resolve => _resolveAllPluginsLoaded = resolve),
- new Promise(resolve => timeoutId = setTimeout(
- Gerrit._pluginLoadingTimeout, PLUGIN_LOADING_TIMEOUT_MS)),
- ]).then(() => clearTimeout(timeoutId));
- }
- }
- return _allPluginsPromise;
- };
-
- Gerrit._pluginLoadingTimeout = function() {
- console.error(`Failed to load plugins: ${Object.keys(_pluginsPending)}`);
- Gerrit._setPluginsPending([]);
- };
-
- Gerrit._setPluginsPending = function(plugins) {
- _pluginsPending = plugins.reduce((o, url) => {
- // TODO(viktard): Remove guard (@see Issue 8962)
- o[getPluginNameFromUrl(url) || UNKNOWN_PLUGIN] = url;
- return o;
- }, {});
- Gerrit._setPluginsCount(Object.keys(_pluginsPending).length);
- };
-
- Gerrit._setPluginsCount = function(count) {
- _pluginsPendingCount = count;
- if (Gerrit._arePluginsLoaded()) {
- getReporting().pluginsLoaded(_pluginsInstalled);
- if (_resolveAllPluginsLoaded) {
- _resolveAllPluginsLoaded();
- }
- }
- };
-
- Gerrit._pluginInstallError = function(message) {
- document.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message: `Plugin install error: ${message}`,
- },
- }));
- console.info(`Plugin install error: ${message}`);
- Gerrit._setPluginsCount(_pluginsPendingCount - 1);
- };
-
- Gerrit._pluginInstalled = function(url) {
- const name = getPluginNameFromUrl(url) || UNKNOWN_PLUGIN;
- if (!_pluginsPending[name]) {
- console.warn(`Unexpected plugin ${name} installed from ${url}.`);
- } else {
- delete _pluginsPending[name];
- _pluginsInstalled.push(name);
- Gerrit._setPluginsCount(_pluginsPendingCount - 1);
- console.log(`Plugin ${name} installed.`);
- }
- };
-
- Gerrit._arePluginsLoaded = function() {
- return _pluginsPendingCount === 0;
- };
-
- Gerrit._getPluginScreenName = function(pluginName, screenName) {
- return `${pluginName}-screen-${screenName}`;
- };
-
- Gerrit._isPluginPreloaded = function(url) {
- const name = getPluginNameFromUrl(url);
- if (name && Gerrit._preloadedPlugins) {
- return name in Gerrit._preloadedPlugins;
- } else {
- return false;
- }
- };
-
- // TODO(taoalpha): List all internal supported event names.
- // Also convert this to inherited class once we move Gerrit to class.
- Gerrit._eventEmitter = new EventEmitter();
- ['addListener',
- 'dispatch',
- 'emit',
- 'off',
- 'on',
- 'once',
- 'removeAllListeners',
- 'removeListener',
- ].forEach(method => {
- /**
- * Enabling EventEmitter interface on Gerrit.
- *
- * This will enable to signal across different parts of js code without relying on DOM,
- * including core to core, plugin to plugin and also core to plugin.
- *
- * @example
- *
- * // Emit this event from pluginA
- * Gerrit.install(pluginA => {
- * fetch("some-api").then(() => {
- * Gerrit.on("your-special-event", {plugin: pluginA});
- * });
- * });
- *
- * // Listen on your-special-event from pluignB
- * Gerrit.install(pluginB => {
- * Gerrit.on("your-special-event", ({plugin}) => {
- * // do something, plugin is pluginA
- * });
- * });
- */
- Gerrit[method] = Gerrit._eventEmitter[method].bind(Gerrit._eventEmitter);
- });
-
- window.Gerrit = Gerrit;
-
- // Preloaded plugins should be installed after Gerrit.install() is set,
- // since plugin preloader substitutes Gerrit.install() temporarily.
- installPreloadedPlugins();
+ window.Plugin = Plugin;
})(window);
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.html b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.html
index ca5c49f035..63a528e76d 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.html
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.html
@@ -15,11 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/gr-voting-styles.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../gr-account-label/gr-account-label.html">
+<link rel="import" href="../gr-account-chip/gr-account-chip.html">
<link rel="import" href="../gr-button/gr-button.html">
<link rel="import" href="../gr-icons/gr-icons.html">
<link rel="import" href="../gr-label/gr-label.html">
@@ -31,7 +32,7 @@ limitations under the License.
<style include="shared-styles">
.placeholder {
color: var(--deemphasized-text-color);
- padding-top: .2em;
+ padding-top: var(--spacing-xs);
}
.hidden {
display: none;
@@ -39,9 +40,10 @@ limitations under the License.
.voteChip {
display: flex;
justify-content: center;
- margin-right: .3em;
- padding: .05em .85em;
+ margin-right: var(--spacing-s);
+ padding: 0;
@apply --vote-chip-styles;
+ border-width: 0;
}
.max {
background-color: var(--vote-color-approved);
@@ -59,30 +61,31 @@ limitations under the License.
display: none;
}
td {
- vertical-align: middle;
+ vertical-align: top;
}
tr {
- min-height: 2.25em;
+ min-height: var(--line-height-normal);
}
gr-button {
+ vertical-align: top;
--gr-button: {
- height: 2em;
+ height: var(--line-height-normal);
+ width: var(--line-height-normal);
padding: 0;
- width: 2em;
}
}
gr-button[disabled] iron-icon {
color: var(--border-color);
}
gr-account-chip {
- margin-right: .25em;
+ margin-right: var(--spacing-xs);
}
iron-icon {
- height: 1.2em;
- width: 1.2em;
+ height: calc(var(--line-height-normal) - 2px);
+ width: calc(var(--line-height-normal) - 2px);
}
.labelValueContainer:not(:first-of-type) td {
- padding-top: .3em;
+ padding-top: var(--spacing-s);
}
</style>
<table>
@@ -111,7 +114,7 @@ limitations under the License.
<gr-button
link
aria-label="Remove"
- on-tap="_onDeleteVote"
+ on-click="_onDeleteVote"
tooltip="Remove vote"
data-account-id$="[[mappedLabel.account._account_id]]"
class$="deleteBtn [[_computeDeleteClass(mappedLabel.account, mutable, change)]]">
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
index 387fe96441..ed2dfdd23d 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-label-info',
- _legacyUndefinedCheck: true,
properties: {
labelInfo: Object,
@@ -38,7 +37,7 @@
*/
_mapLabelInfo(labelInfo, account, changeLabelsRecord) {
const result = [];
- if (!labelInfo) { return result; }
+ if (!labelInfo || !account) { return result; }
if (!labelInfo.values) {
if (labelInfo.rejected || labelInfo.approved) {
const ok = labelInfo.approved || !labelInfo.rejected;
@@ -149,10 +148,12 @@
* order to trigger computation when a label is removed from the change.
*/
_computeShowPlaceholder(labelInfo, changeLabelsRecord) {
- if (!labelInfo.values && (labelInfo.rejected || labelInfo.approved)) {
+ if (labelInfo &&
+ !labelInfo.values && (labelInfo.rejected || labelInfo.approved)) {
return 'hidden';
}
- if (labelInfo.all) {
+
+ if (labelInfo && labelInfo.all) {
for (const label of labelInfo.all) {
if (label.value && label.value != labelInfo.default_value) {
return 'hidden';
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
index e6e72d26c7..658a973380 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
@@ -17,9 +17,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-label-info</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-label-info.html">
@@ -98,7 +100,6 @@ limitations under the License.
assert.isTrue(button.disabled);
return deleteResponse.then(() => {
assert.isFalse(button.disabled);
- assert.notOk(element.change.labels.test.recommended);
assert.isTrue(deleteStub.calledWithExactly(42, 1, 'test'));
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-label/gr-label.html b/polygerrit-ui/app/elements/shared/gr-label/gr-label.html
index fe290b759c..55ecc98848 100644
--- a/polygerrit-ui/app/elements/shared/gr-label/gr-label.html
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
<dom-module id="gr-label">
<template strip-whitespace>
diff --git a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
index c437885465..0de0881130 100644
--- a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-label',
- _legacyUndefinedCheck: true,
behaviors: [
Gerrit.TooltipBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.html b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.html
index c001ce7be1..da0b93f763 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -28,7 +28,7 @@ limitations under the License.
#container {
background: var(--chip-background-color);
border-radius: 1em;
- padding: .5em;
+ padding: var(--spacing-m);
}
#header {
color: var(--deemphasized-text-color);
@@ -48,7 +48,7 @@ limitations under the License.
border-left: 1px solid var(--deemphasized-text-color);
color: var(--deemphasized-text-color);
cursor: pointer;
- padding-left: .4em;
+ padding-left: var(--spacing-s);
}
#trigger:hover {
color: var(--primary-text-color);
@@ -64,7 +64,7 @@ limitations under the License.
disabled="[[disabled]]"
placeholder="[[placeholder]]"
borderless></gr-autocomplete>
- <div id="trigger" on-tap="_handleTriggerTap">▼</div>
+ <div id="trigger" on-click="_handleTriggerClick">▼</div>
</div>
</div>
</template>
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
index a892522481..fd0f2285d0 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-labeled-autocomplete',
- _legacyUndefinedCheck: true,
/**
* Fired when a value is chosen.
@@ -59,7 +58,7 @@
},
},
- _handleTriggerTap(e) {
+ _handleTriggerClick(e) {
// Stop propagation here so we don't confuse gr-autocomplete, which
// listens for taps on body to try to determine when it's blurred.
e.stopPropagation();
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html
index 6bcaa1873d..b257746d14 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-labeled-autocomplete</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-labeled-autocomplete.html">
@@ -47,7 +49,7 @@ limitations under the License.
const e = {stopPropagation: () => undefined};
sandbox.stub(e, 'stopPropagation');
sandbox.stub(element.$.autocomplete, 'focus');
- element._handleTriggerTap(e);
+ element._handleTriggerClick(e);
assert.isTrue(e.stopPropagation.calledOnce);
assert.isTrue(element.$.autocomplete.focus.calledOnce);
});
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.html b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.html
index 4137485315..fb55c67354 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.html
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<dom-module id="gr-lib-loader">
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
index ba86b66cef..5ea5dca99a 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-lib-loader',
- _legacyUndefinedCheck: true,
properties: {
_hljsState: {
@@ -68,14 +67,16 @@
* custom-style DOM element.
*
* @return {!Promise<Element>}
+ * @suppress {checkTypes}
*/
getDarkTheme() {
return new Promise((resolve, reject) => {
- this.importHref(this._getLibRoot() + DARK_THEME_PATH, () => {
- const module = document.createElement('style', 'custom-style');
- module.setAttribute('include', 'dark-theme');
- resolve(module);
- });
+ (this.importHref || Polymer.importHref)(
+ this._getLibRoot() + DARK_THEME_PATH, () => {
+ const module = document.createElement('style', 'custom-style');
+ module.setAttribute('include', 'dark-theme');
+ resolve(module);
+ });
});
},
@@ -139,7 +140,7 @@
return;
}
- script.src = src;
+ script.setAttribute('src', src);
script.onload = resolve;
script.onerror = reject;
Polymer.dom(document.head).appendChild(script);
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html
index cf9a41cff1..10d16085a4 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-lib-loader</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-lib-loader.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.html b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.html
index 91866e513c..d00416badd 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.html
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
<dom-module id="gr-limited-text">
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
index 44a8791f10..048e4f5b77 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
@@ -26,7 +26,6 @@
Polymer({
is: 'gr-limited-text',
- _legacyUndefinedCheck: true,
properties: {
/** The un-truncated text to display. */
@@ -45,6 +44,15 @@
},
/**
+ * Disable the tooltip.
+ * When set to true, will not show tooltip even text is over limit
+ */
+ disableTooltip: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
* The maximum number of characters to display in the tooltop.
*/
tooltipLimit: {
@@ -66,8 +74,13 @@
* enabled.
*/
_updateTitle(text, limit, tooltipLimit) {
+ // Polymer 2: check for undefined
+ if ([text, limit, tooltipLimit].some(arg => arg === undefined)) {
+ return;
+ }
+
this.hasTooltip = !!limit && !!text && text.length > limit;
- if (this.hasTooltip) {
+ if (this.hasTooltip && !this.disableTooltip) {
this.setAttribute('title', text.substr(0, tooltipLimit));
} else {
this.removeAttribute('title');
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html
index 16eb960227..7946bb6f95 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-limited-text</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-limited-text.html">
@@ -90,5 +92,14 @@ limitations under the License.
assert.equal(element._computeDisplayText('foo bar', 4), 'foo…');
assert.equal(element._computeDisplayText('foo bar', null), 'foo bar');
});
+
+ test('when disable tooltip', () => {
+ sandbox.spy(element, '_updateTitle');
+ element.text = 'abcdefghijklmn';
+ element.disableTooltip = true;
+ element.limit = 10;
+ flushAsynchronousOperations();
+ assert.equal(element.getAttribute('title'), null);
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
index fab562a603..f3e0906416 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
<link rel="import" href="../gr-button/gr-button.html">
<link rel="import" href="../gr-icons/gr-icons.html">
@@ -34,25 +35,31 @@ limitations under the License.
background: var(--chip-background-color);
border-radius: .75em;
display: inline-flex;
- padding: 0 .5em;
+ padding: 0 var(--spacing-m);
}
+ gr-button.remove {
+ --gr-remove-button-style: {
+ border: 0;
+ color: var(--deemphasized-text-color);
+ font-weight: normal;
+ height: .6em;
+ line-height: 10px;
+ margin-left: var(--spacing-xs);
+ padding: 0;
+ text-decoration: none;
+ }
+ }
+
gr-button.remove:hover,
gr-button.remove:focus {
--gr-button: {
+ @apply --gr-remove-button-style;
color: #333;
}
}
gr-button.remove {
--gr-button: {
- border: 0;
- color: var(--deemphasized-text-color);
- font-size: 1.7rem;
- font-weight: normal;
- height: .6em;
- line-height: .6;
- margin-left: .15em;
- padding: 0;
- text-decoration: none;
+ @apply --gr-remove-button-style;
}
}
.transparentBackground,
@@ -83,7 +90,7 @@ limitations under the License.
hidden$="[[!removable]]"
hidden
class$="remove [[_getBackgroundClass(transparentBackground)]]"
- on-tap="_handleRemoveTap">
+ on-click="_handleRemoveTap">
<iron-icon icon="gr-icons:close"></iron-icon>
</gr-button>
</div>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
index 8388a079b5..33a9c25808 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-linked-chip',
- _legacyUndefinedCheck: true,
properties: {
href: String,
@@ -42,6 +41,10 @@
limit: Number,
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
_getBackgroundClass(transparent) {
return transparent ? 'transparentBackground' : '';
},
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
index eb57428f50..22a2eaf054 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
@@ -18,11 +18,13 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-linked-chip</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
-<script src="../../../bower_components/iron-test-helpers/mock-interactions.js"></script>
+<script src="/bower_components/iron-test-helpers/mock-interactions.js"></script>
<link rel="import" href="gr-linked-chip.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html
index af70e374dd..61facc042e 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html
@@ -15,12 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
-<script src="../../../bower_components/ba-linkify/ba-linkify.js"></script>
+<script src="/bower_components/ba-linkify/ba-linkify.js"></script>
<script src="link-text-parser.js"></script>
<dom-module id="gr-linked-text">
<template>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
index 157ad5ed88..229fa1919b 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-linked-text',
- _legacyUndefinedCheck: true,
properties: {
removeZeroWidthSpace: Boolean,
@@ -62,6 +61,7 @@
* commentLink patterns
*/
_contentOrConfigChanged(content, config) {
+ if (!Gerrit.Nav || !Gerrit.Nav.mapCommentlinks) return;
config = Gerrit.Nav.mapCommentlinks(config);
const output = Polymer.dom(this.$.output);
output.textContent = '';
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
index 9fc92b1511..0deff05f8e 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-linked-text</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
@@ -276,16 +278,58 @@ limitations under the License.
let links = element.$.output.querySelectorAll('a');
assert.equal(links.length, 1);
assert.equal(links[0].getAttribute('href'), 'mailto:test@google.com');
+ assert.equal(links[0].innerHTML, 'mailto:test@google.com');
element.content = 'xx http://google.com yy';
links = element.$.output.querySelectorAll('a');
assert.equal(links.length, 1);
assert.equal(links[0].getAttribute('href'), 'http://google.com');
+ assert.equal(links[0].innerHTML, 'http://google.com');
element.content = 'xx https://google.com yy';
links = element.$.output.querySelectorAll('a');
assert.equal(links.length, 1);
assert.equal(links[0].getAttribute('href'), 'https://google.com');
+ assert.equal(links[0].innerHTML, 'https://google.com');
+
+ element.content = 'xx ssh://google.com yy';
+ links = element.$.output.querySelectorAll('a');
+ assert.equal(links.length, 0);
+
+ element.content = 'xx ftp://google.com yy';
+ links = element.$.output.querySelectorAll('a');
+ assert.equal(links.length, 0);
+ });
+
+ test('links without leading whitespace are linkified', () => {
+ element.content = 'xx abcmailto:test@google.com yy';
+ assert.equal(element.$.output.innerHTML.substr(0, 6), 'xx abc');
+ let links = element.$.output.querySelectorAll('a');
+ assert.equal(links.length, 1);
+ assert.equal(links[0].getAttribute('href'), 'mailto:test@google.com');
+ assert.equal(links[0].innerHTML, 'mailto:test@google.com');
+
+ element.content = 'xx defhttp://google.com yy';
+ assert.equal(element.$.output.innerHTML.substr(0, 6), 'xx def');
+ links = element.$.output.querySelectorAll('a');
+ assert.equal(links.length, 1);
+ assert.equal(links[0].getAttribute('href'), 'http://google.com');
+ assert.equal(links[0].innerHTML, 'http://google.com');
+
+ element.content = 'xx qwehttps://google.com yy';
+ assert.equal(element.$.output.innerHTML.substr(0, 6), 'xx qwe');
+ links = element.$.output.querySelectorAll('a');
+ assert.equal(links.length, 1);
+ assert.equal(links[0].getAttribute('href'), 'https://google.com');
+ assert.equal(links[0].innerHTML, 'https://google.com');
+
+ // Non-latin character
+ element.content = 'xx абвhttps://google.com yy';
+ assert.equal(element.$.output.innerHTML.substr(0, 6), 'xx абв');
+ links = element.$.output.querySelectorAll('a');
+ assert.equal(links.length, 1);
+ assert.equal(links[0].getAttribute('href'), 'https://google.com');
+ assert.equal(links[0].innerHTML, 'https://google.com');
element.content = 'xx ssh://google.com yy';
links = element.$.output.querySelectorAll('a');
@@ -328,26 +372,4 @@ limitations under the License.
assert.isTrue(contentConfigStub.called);
});
});
-
- suite('gr-linked-text with null config', () => {
- let element;
- let sandbox;
-
- setup(() => {
- element = fixture('basic');
- sandbox = sinon.sandbox.create();
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('_contentOrConfigChanged not called without config', () => {
- const contentStub = sandbox.stub(element, '_contentChanged');
- const contentConfigStub = sandbox.stub(element, '_contentOrConfigChanged');
- element.content = 'some text';
- assert.isTrue(contentStub.called);
- assert.isFalse(contentConfigStub.called);
- });
- });
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
index 027c632e12..fa38a66007 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
@@ -17,23 +17,12 @@
(function() {
'use strict';
- const Defs = {};
-
- /**
- * @typedef {{
- * html: Node,
- * position: number,
- * length: number,
- * }}
- */
- Defs.CommentLinkItem;
-
/**
* Pattern describing URLs with supported protocols.
*
* @type {RegExp}
*/
- const URL_PROTOCOL_PATTERN = /^(https?:\/\/|mailto:)/;
+ const URL_PROTOCOL_PATTERN = /^(.*)(https?:\/\/|mailto:)/;
/**
* Construct a parser for linkifying text. Will linkify plain URLs that appear
@@ -73,7 +62,7 @@
*
* @param {string} text The chuml of source text over which the outputArray
* items range.
- * @param {!Array<Defs.CommentLinkItem>} outputArray The list of items to add
+ * @param {!Array<Gerrit.CommentLinkItem>} outputArray The list of items to add
* resulting from commentlink matches.
*/
GrLinkTextParser.prototype.processLinks = function(text, outputArray) {
@@ -109,7 +98,7 @@
* Sort the given array of CommentLinkItems such that the positions are in
* reverse order.
*
- * @param {!Array<Defs.CommentLinkItem>} outputArray
+ * @param {!Array<Gerrit.CommentLinkItem>} outputArray
*/
GrLinkTextParser.prototype.sortArrayReverse = function(outputArray) {
outputArray.sort((a, b) => b.position - a.position);
@@ -132,7 +121,7 @@
* starts.
* @param {number} length The number of characters in the source text
* represented by the item.
- * @param {!Array<Defs.CommentLinkItem>} outputArray The array to which the
+ * @param {!Array<Gerrit.CommentLinkItem>} outputArray The array to which the
* new item is to be appended.
*/
GrLinkTextParser.prototype.addItem =
@@ -174,7 +163,7 @@
* starts.
* @param {number} length The number of characters in the source text
* represented by the link.
- * @param {!Array<Defs.CommentLinkItem>} outputArray The array to which the
+ * @param {!Array<Gerrit.CommentLinkItem>} outputArray The array to which the
* new item is to be appended.
*/
GrLinkTextParser.prototype.addLink =
@@ -196,7 +185,7 @@
* starts.
* @param {number} length The number of characters in the source text
* represented by the item.
- * @param {!Array<Defs.CommentLinkItem>} outputArray The array to which the
+ * @param {!Array<Gerrit.CommentLinkItem>} outputArray The array to which the
* new item is to be appended.
*/
GrLinkTextParser.prototype.addHTML =
@@ -214,7 +203,7 @@
*
* @param {number} position
* @param {number} length
- * @param {!Array<Defs.CommentLinkItem>} outputArray
+ * @param {!Array<Gerrit.CommentLinkItem>} outputArray
*/
GrLinkTextParser.prototype.hasOverlap =
function(position, length, outputArray) {
@@ -238,9 +227,11 @@
* @param {string} text
*/
GrLinkTextParser.prototype.parse = function(text) {
- linkify(text, {
- callback: this.parseChunk.bind(this),
- });
+ if (text) {
+ linkify(text, {
+ callback: this.parseChunk.bind(this),
+ });
+ }
};
/**
@@ -265,13 +256,29 @@
// the source text does not include a protocol, the protocol will be added
// by ba-linkify. Create the link if the href is provided and its protocol
// matches the expected pattern.
- if (href && URL_PROTOCOL_PATTERN.test(href)) {
- this.addText(text, href);
- } else {
- // For the sections of text that lie between the links found by
- // ba-linkify, we search for the project-config-specified link patterns.
- this.parseLinks(text, this.linkConfig);
+ if (href) {
+ const result = URL_PROTOCOL_PATTERN.exec(href);
+ if (result) {
+ const prefixText = result[1];
+ if (prefixText.length > 0) {
+ // Fix for simple cases from
+ // https://bugs.chromium.org/p/gerrit/issues/detail?id=11697
+ // When leading whitespace is missed before link,
+ // linkify add this text before link as a schema name to href.
+ // We suppose, that prefixText just a single word
+ // before link and add this word as is, without processing
+ // any patterns in it.
+ this.parseLinks(prefixText, []);
+ text = text.substring(prefixText.length);
+ href = href.substring(prefixText.length);
+ }
+ this.addText(text, href);
+ return;
+ }
}
+ // For the sections of text that lie between the links found by
+ // ba-linkify, we search for the project-config-specified link patterns.
+ this.parseLinks(text, this.linkConfig);
};
/**
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html
index be02d403fd..3d41a7c51d 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.html
@@ -14,9 +14,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-input/iron-input.html">
+<link rel="import" href="/bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
@@ -25,7 +28,6 @@ limitations under the License.
<template>
<style include="shared-styles">
#filter {
- font-size: var(--font-size-normal);
max-width: 25em;
}
#filter:focus {
@@ -36,7 +38,7 @@ limitations under the License.
display: flex;
height: 3rem;
justify-content: space-between;
- margin: 0 1em;
+ margin: 0 var(--spacing-l);
}
#createNewContainer:not(.show) {
display: none;
@@ -68,20 +70,26 @@ limitations under the License.
<div id="topContainer">
<div class="filterContainer">
<label>Filter:</label>
- <input is="iron-input"
+ <iron-input
type="text"
- id="filter"
bind-value="{{filter}}">
+ <input
+ is="iron-input"
+ type="text"
+ id="filter"
+ bind-value="{{filter}}">
+ </iron-input>
</div>
<div id="createNewContainer"
class$="[[_computeCreateClass(createNew)]]">
- <gr-button primary link id="createNew" on-tap="_createNewItem">
+ <gr-button primary link id="createNew" on-click="_createNewItem">
Create New
</gr-button>
</div>
</div>
<slot></slot>
<nav>
+ Page [[_computePage(offset, itemsPerPage)]]
<a id="prevArrow"
href$="[[_computeNavLink(offset, -1, itemsPerPage, filter, path)]]"
hidden$="[[_hidePrevArrow(loading, offset)]]" hidden>
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
index 53d05e1044..6840e97047 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
@@ -21,7 +21,6 @@
Polymer({
is: 'gr-list-view',
- _legacyUndefinedCheck: true,
properties: {
createNew: Boolean,
@@ -38,6 +37,7 @@
behaviors: [
Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -90,11 +90,18 @@
},
_hideNextArrow(loading, items) {
- let lastPage = false;
- if (items.length < this.itemsPerPage + 1) {
- lastPage = true;
+ if (loading || !items || !items.length) {
+ return true;
}
- return loading || lastPage || !items || !items.length;
+ const lastPage = items.length < this.itemsPerPage + 1;
+ return lastPage;
+ },
+
+ // TODO: fix offset (including itemsPerPage)
+ // to either support a decimal or make it go to the nearest
+ // whole number (e.g 3).
+ _computePage(offset, itemsPerPage) {
+ return offset / itemsPerPage + 1;
},
});
})();
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html
index 09e68dda65..ea1dcbb8d1 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-list-view</title>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-list-view.html">
@@ -153,5 +155,10 @@ limitations under the License.
element.path = TAGS_PATH;
assert.equal(element._computeNavLink.lastCall.args[4], TAGS_PATH);
});
+
+ test('_computePage', () => {
+ assert.equal(element._computePage(0, 25), 1);
+ assert.equal(element._computePage(50, 25), 3);
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html
index e94b655f9e..2b4b982016 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.html
@@ -15,8 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-overlay-behavior/iron-overlay-behavior.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-overlay-behavior/iron-overlay-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-overlay">
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
index c167b3b54c..862345865b 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
@@ -23,7 +23,6 @@
Polymer({
is: 'gr-overlay',
- _legacyUndefinedCheck: true,
/**
* Fired when a fullscreen overlay is closed
@@ -45,6 +44,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Polymer.IronOverlayBehavior,
],
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html
index ee05b699f7..08b749744c 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-overlay</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.html b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.html
index 3885497fde..f1c3a6f359 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.html
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-page-nav">
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
index 181c7bc751..2e056075c5 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-page-nav',
- _legacyUndefinedCheck: true,
properties: {
_headerHeight: Number,
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html
index 428bab3294..b384b474d0 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-page-nav</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/page/page.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/page/page.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.html b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.html
index d794dd648d..ce596f8a89 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.html
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.html
@@ -14,8 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-icon/iron-icon.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.html">
<link rel="import" href="../../shared/gr-icons/gr-icons.html">
@@ -33,7 +33,7 @@ limitations under the License.
display: inline-block;
}
iron-icon {
- margin-bottom: 1.2em;
+ margin-bottom: var(--spacing-l);
}
</style>
<div>
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
index 2fccc8defa..e2298c3d37 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
@@ -22,7 +22,6 @@
Polymer({
is: 'gr-repo-branch-picker',
- _legacyUndefinedCheck: true,
properties: {
repo: {
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
index 989e838181..1ed9151383 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-repo-branch-picker</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-repo-branch-picker.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html
index f20359466e..dc07d0f52b 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-auth</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.html
index c5a0dfe80a..d3500d82a2 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-etag-decorator">
<script src="gr-etag-decorator.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
index 09ae1da341..76c8c2c336 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-etag-decorator_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-etag-decorator</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="gr-etag-decorator.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
index 562980c313..7461ac475b 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
@@ -15,19 +15,21 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
<link rel="import" href="gr-etag-decorator.html">
<!-- NB: es6-promise Needed for IE11 and fetch polyfill support, see Issue 4308 -->
-<script src="../../../bower_components/es6-promise/dist/es6-promise.min.js"></script>
-<script src="../../../bower_components/fetch/fetch.js"></script>
+<script src="/bower_components/es6-promise/dist/es6-promise.min.js"></script>
+<script src="/bower_components/fetch/fetch.js"></script>
<dom-module id="gr-rest-api-interface">
<!-- NB: Order is important, because of namespaced classes. -->
+ <script src="gr-rest-apis/gr-rest-api-helper.js"></script>
<script src="gr-auth.js"></script>
<script src="gr-reviewer-updates-parser.js"></script>
<script src="gr-rest-api-interface.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 546d0045fa..8c34e33ac9 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -17,113 +17,15 @@
(function() {
'use strict';
- const Defs = {};
-
- /**
- * @typedef {{
- * basePatchNum: (string|number),
- * patchNum: (number),
- * }}
- */
- Defs.patchRange;
-
- /**
- * @typedef {{
- * url: string,
- * fetchOptions: (Object|null|undefined),
- * anonymizedUrl: (string|undefined),
- * }}
- */
- Defs.FetchRequest;
-
- /**
- * Object to describe a request for passing into _fetchJSON or _fetchRawJSON.
- * - url is the URL for the request (excluding get params)
- * - errFn is a function to invoke when the request fails.
- * - cancelCondition is a function that, if provided and returns true, will
- * cancel the response after it resolves.
- * - params is a key-value hash to specify get params for the request URL.
- *
- * @typedef {{
- * url: string,
- * errFn: (function(?Response, string=)|null|undefined),
- * cancelCondition: (function()|null|undefined),
- * params: (Object|null|undefined),
- * fetchOptions: (Object|null|undefined),
- * anonymizedUrl: (string|undefined),
- * reportUrlAsIs: (boolean|undefined),
- * }}
- */
- Defs.FetchJSONRequest;
-
- /**
- * @typedef {{
- * changeNum: (string|number),
- * endpoint: string,
- * patchNum: (string|number|null|undefined),
- * errFn: (function(?Response, string=)|null|undefined),
- * params: (Object|null|undefined),
- * fetchOptions: (Object|null|undefined),
- * anonymizedEndpoint: (string|undefined),
- * reportEndpointAsIs: (boolean|undefined),
- * }}
- */
- Defs.ChangeFetchRequest;
-
- /**
- * Object to describe a request for passing into _send.
- * - method is the HTTP method to use in the request.
- * - url is the URL for the request
- * - body is a request payload.
- * TODO (beckysiegel) remove need for number at least.
- * - errFn is a function to invoke when the request fails.
- * - cancelCondition is a function that, if provided and returns true, will
- * cancel the response after it resolves.
- * - contentType is the content type of the body.
- * - headers is a key-value hash to describe HTTP headers for the request.
- * - parseResponse states whether the result should be parsed as a JSON
- * object using getResponseObject.
- *
- * @typedef {{
- * method: string,
- * url: string,
- * body: (string|number|Object|null|undefined),
- * errFn: (function(?Response, string=)|null|undefined),
- * contentType: (string|null|undefined),
- * headers: (Object|undefined),
- * parseResponse: (boolean|undefined),
- * anonymizedUrl: (string|undefined),
- * reportUrlAsIs: (boolean|undefined),
- * }}
- */
- Defs.SendRequest;
-
- /**
- * @typedef {{
- * changeNum: (string|number),
- * method: string,
- * patchNum: (string|number|undefined),
- * endpoint: string,
- * body: (string|number|Object|null|undefined),
- * errFn: (function(?Response, string=)|null|undefined),
- * contentType: (string|null|undefined),
- * headers: (Object|undefined),
- * parseResponse: (boolean|undefined),
- * anonymizedEndpoint: (string|undefined),
- * reportEndpointAsIs: (boolean|undefined),
- * }}
- */
- Defs.ChangeSendRequest;
-
const DiffViewMode = {
SIDE_BY_SIDE: 'SIDE_BY_SIDE',
UNIFIED: 'UNIFIED_DIFF',
};
const JSON_PREFIX = ')]}\'';
const MAX_PROJECT_RESULTS = 25;
- const MAX_UNIFIED_DEFAULT_WINDOW_WIDTH_PX = 900;
+ // This value is somewhat arbitrary and not based on research or calculations.
+ const MAX_UNIFIED_DEFAULT_WINDOW_WIDTH_PX = 850;
const PARENT_PATCH_NUM = 'PARENT';
- const FAILED_TO_FETCH_ERROR = 'Failed to fetch';
const Requests = {
SEND_DIFF_DRAFT: 'sendDiffDraft',
@@ -137,57 +39,11 @@
const ANONYMIZED_REVISION_BASE_URL = ANONYMIZED_CHANGE_BASE_URL +
'/revisions/*';
- /**
- * Wrapper around Map for caching server responses. Site-based so that
- * changes to CANONICAL_PATH will result in a different cache going into
- * effect.
- */
- class SiteBasedCache {
- constructor() {
- // Container of per-canonical-path caches.
- this._data = new Map();
- }
-
- // Returns the cache for the current canonical path.
- _cache() {
- if (!this._data.has(window.CANONICAL_PATH)) {
- this._data.set(window.CANONICAL_PATH, new Map());
- }
- return this._data.get(window.CANONICAL_PATH);
- }
-
- has(key) {
- return this._cache().has(key);
- }
-
- get(key) {
- return this._cache().get(key);
- }
-
- set(key, value) {
- this._cache().set(key, value);
- }
-
- delete(key) {
- this._cache().delete(key);
- }
-
- invalidatePrefix(prefix) {
- const newMap = new Map();
- for (const [key, value] of this._cache().entries()) {
- if (!key.startsWith(prefix)) {
- newMap.set(key, value);
- }
- }
- this._data.set(window.CANONICAL_PATH, newMap);
- }
- }
-
Polymer({
is: 'gr-rest-api-interface',
- _legacyUndefinedCheck: true,
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.PathListBehavior,
Gerrit.PatchSetBehavior,
Gerrit.RESTClientBehavior,
@@ -228,7 +84,7 @@
},
_sharedFetchPromises: {
type: Object,
- value: {}, // Intentional to share the object across instances.
+ value: new FetchPromisesCache(), // Shared across instances.
},
_pendingRequests: {
type: Object,
@@ -253,164 +109,56 @@
JSON_PREFIX,
- /**
- * Wraps calls to the underlying authenticated fetch function (_auth.fetch)
- * with timing and logging.
- *
- * @param {Defs.FetchRequest} req
- */
- _fetch(req) {
- const start = Date.now();
- const xhr = this._auth.fetch(req.url, req.fetchOptions);
-
- // Log the call after it completes.
- xhr.then(res => this._logCall(req, start, res.status));
-
- // Return the XHR directly (without the log).
- return xhr;
- },
-
- /**
- * Log information about a REST call. Because the elapsed time is determined
- * by this method, it should be called immediately after the request
- * finishes.
- *
- * @param {Defs.FetchRequest} req
- * @param {number} startTime the time that the request was started.
- * @param {number} status the HTTP status of the response. The status value
- * is used here rather than the response object so there is no way this
- * method can read the body stream.
- */
- _logCall(req, startTime, status) {
- const method = (req.fetchOptions && req.fetchOptions.method) ?
- req.fetchOptions.method : 'GET';
- const elapsed = (Date.now() - startTime);
- console.log([
- 'HTTP',
- status,
- method,
- elapsed + 'ms',
- req.anonymizedUrl || req.url,
- ].join(' '));
- if (req.anonymizedUrl) {
- this.fire('rpc-log',
- {status, method, elapsed, anonymizedUrl: req.anonymizedUrl});
+ created() {
+ /* Polymer 1 and Polymer 2 have slightly different lifecycle.
+ * Differences are not very well documented (see
+ * https://github.com/Polymer/old-docs-site/issues/2322).
+ * In Polymer 1, created() is called when properties values is not set
+ * and ready() is always called later, even if element is not added
+ * to a DOM. I.e. in Polymer 1 _cache and other properties are undefined,
+ * while in Polymer 2 they are set to default values.
+ * In Polymer 2, created() is called after properties values set and
+ * ready() is called only after element is attached to a DOM.
+ * There are several places in the code, where element is created with
+ * document.createElement('gr-rest-api-interface') and is not added
+ * to a DOM.
+ * In such cases, Polymer 1 calls both created() and ready() methods,
+ * but Polymer 2 calls only created() method.
+ * To workaround these differences, we should try to create _restApiHelper
+ * in both methods.
+ */
+ //
+
+ this._initRestApiHelper();
+ },
+
+ ready() {
+ // See comments in created()
+ this._initRestApiHelper();
+ },
+
+ _initRestApiHelper() {
+ if (this._restApiHelper) {
+ return;
}
- },
-
- /**
- * Fetch JSON from url provided.
- * Returns a Promise that resolves to a native Response.
- * Doesn't do error checking. Supports cancel condition. Performs auth.
- * Validates auth expiry errors.
- *
- * @param {Defs.FetchJSONRequest} req
- */
- _fetchRawJSON(req) {
- const urlWithParams = this._urlWithParams(req.url, req.params);
- const fetchReq = {
- url: urlWithParams,
- fetchOptions: req.fetchOptions,
- anonymizedUrl: req.reportUrlAsIs ? urlWithParams : req.anonymizedUrl,
- };
- return this._fetch(fetchReq).then(res => {
- if (req.cancelCondition && req.cancelCondition()) {
- res.body.cancel();
- return;
- }
- return res;
- }).catch(err => {
- const isLoggedIn = !!this._cache.get('/accounts/self/detail');
- if (isLoggedIn && err && err.message === FAILED_TO_FETCH_ERROR) {
- this.checkCredentials();
- return;
- }
- if (req.errFn) {
- req.errFn.call(undefined, null, err);
- } else {
- this.fire('network-error', {error: err});
- }
- throw err;
- });
- },
-
- /**
- * Fetch JSON from url provided.
- * Returns a Promise that resolves to a parsed response.
- * Same as {@link _fetchRawJSON}, plus error handling.
- *
- * @param {Defs.FetchJSONRequest} req
- */
- _fetchJSON(req) {
- return this._fetchRawJSON(req).then(response => {
- if (!response) {
- return;
- }
- if (!response.ok) {
- if (req.errFn) {
- req.errFn.call(null, response);
- return;
- }
- this.fire('server-error', {request: req, response});
- return;
- }
- return response && this.getResponseObject(response);
- });
- },
-
- /**
- * @param {string} url
- * @param {?Object|string=} opt_params URL params, key-value hash.
- * @return {string}
- */
- _urlWithParams(url, opt_params) {
- if (!opt_params) { return this.getBaseUrl() + url; }
-
- const params = [];
- for (const p in opt_params) {
- if (!opt_params.hasOwnProperty(p)) { continue; }
- if (opt_params[p] == null) {
- params.push(encodeURIComponent(p));
- continue;
- }
- for (const value of [].concat(opt_params[p])) {
- params.push(`${encodeURIComponent(p)}=${encodeURIComponent(value)}`);
- }
+ if (this._cache && this._auth && this._sharedFetchPromises
+ && this._credentialCheck) {
+ this._restApiHelper = new GrRestApiHelper(this._cache, this._auth,
+ this._sharedFetchPromises, this._credentialCheck, this);
}
- return this.getBaseUrl() + url + '?' + params.join('&');
},
- /**
- * @param {!Object} response
- * @return {?}
- */
- getResponseObject(response) {
- return this._readResponsePayload(response)
- .then(payload => payload.parsed);
+ _fetchSharedCacheURL(req) {
+ // Cache is shared across instances
+ return this._restApiHelper.fetchCacheURL(req);
},
/**
* @param {!Object} response
- * @return {!Object}
- */
- _readResponsePayload(response) {
- return response.text().then(text => {
- let result;
- try {
- result = this._parsePrefixedJSON(text);
- } catch (_) {
- result = null;
- }
- return {parsed: result, raw: text};
- });
- },
-
- /**
- * @param {string} source
* @return {?}
*/
- _parsePrefixedJSON(source) {
- return JSON.parse(source.substring(JSON_PREFIX.length));
+ getResponseObject(response) {
+ return this._restApiHelper.getResponseObject(response);
},
getConfig(noCache) {
@@ -421,7 +169,7 @@
});
}
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/config/server/info',
reportUrlAsIs: true,
});
@@ -471,7 +219,7 @@
// supports it.
const url = `/projects/${encodeURIComponent(repo)}/config`;
this._cache.delete(url);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url,
body: config,
@@ -485,7 +233,7 @@
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
const encodeName = encodeURIComponent(repo);
- return this._send({
+ return this._restApiHelper.send({
method: 'POST',
url: `/projects/${encodeName}/gc`,
body: '',
@@ -503,7 +251,7 @@
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
const encodeName = encodeURIComponent(config.name);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/projects/${encodeName}`,
body: config,
@@ -519,7 +267,7 @@
createGroup(config, opt_errFn) {
if (!config.name) { return ''; }
const encodeName = encodeURIComponent(config.name);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/groups/${encodeName}`,
body: config,
@@ -529,7 +277,7 @@
},
getGroupConfig(group, opt_errFn) {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: `/groups/${encodeURIComponent(group)}/detail`,
errFn: opt_errFn,
anonymizedUrl: '/groups/*/detail',
@@ -547,7 +295,7 @@
// supports it.
const encodeName = encodeURIComponent(repo);
const encodeRef = encodeURIComponent(ref);
- return this._send({
+ return this._restApiHelper.send({
method: 'DELETE',
url: `/projects/${encodeName}/branches/${encodeRef}`,
body: '',
@@ -567,7 +315,7 @@
// supports it.
const encodeName = encodeURIComponent(repo);
const encodeRef = encodeURIComponent(ref);
- return this._send({
+ return this._restApiHelper.send({
method: 'DELETE',
url: `/projects/${encodeName}/tags/${encodeRef}`,
body: '',
@@ -588,7 +336,7 @@
// supports it.
const encodeName = encodeURIComponent(name);
const encodeBranch = encodeURIComponent(branch);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/projects/${encodeName}/branches/${encodeBranch}`,
body: revision,
@@ -609,7 +357,7 @@
// supports it.
const encodeName = encodeURIComponent(name);
const encodeTag = encodeURIComponent(tag);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/projects/${encodeName}/tags/${encodeTag}`,
body: revision,
@@ -625,8 +373,8 @@
getIsGroupOwner(groupName) {
const encodeName = encodeURIComponent(groupName);
const req = {
- url: `/groups/?owned&q=${encodeName}`,
- anonymizedUrl: '/groups/owned&q=*',
+ url: `/groups/?owned&g=${encodeName}`,
+ anonymizedUrl: '/groups/owned&g=*',
};
return this._fetchSharedCacheURL(req)
.then(configs => configs.hasOwnProperty(groupName));
@@ -634,7 +382,7 @@
getGroupMembers(groupName, opt_errFn) {
const encodeName = encodeURIComponent(groupName);
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: `/groups/${encodeName}/members/`,
errFn: opt_errFn,
anonymizedUrl: '/groups/*/members',
@@ -642,7 +390,7 @@
},
getIncludedGroup(groupName) {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: `/groups/${encodeURIComponent(groupName)}/groups/`,
anonymizedUrl: '/groups/*/groups',
});
@@ -650,7 +398,7 @@
saveGroupName(groupId, name) {
const encodeId = encodeURIComponent(groupId);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/groups/${encodeId}/name`,
body: {name},
@@ -660,7 +408,7 @@
saveGroupOwner(groupId, ownerId) {
const encodeId = encodeURIComponent(groupId);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/groups/${encodeId}/owner`,
body: {owner: ownerId},
@@ -670,7 +418,7 @@
saveGroupDescription(groupId, description) {
const encodeId = encodeURIComponent(groupId);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/groups/${encodeId}/description`,
body: {description},
@@ -680,7 +428,7 @@
saveGroupOptions(groupId, options) {
const encodeId = encodeURIComponent(groupId);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/groups/${encodeId}/options`,
body: options,
@@ -699,7 +447,7 @@
saveGroupMembers(groupName, groupMembers) {
const encodeName = encodeURIComponent(groupName);
const encodeMember = encodeURIComponent(groupMembers);
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/groups/${encodeName}/members/${encodeMember}`,
parseResponse: true,
@@ -716,7 +464,7 @@
errFn: opt_errFn,
anonymizedUrl: '/groups/*/groups/*',
};
- return this._send(req).then(response => {
+ return this._restApiHelper.send(req).then(response => {
if (response.ok) {
return this.getResponseObject(response);
}
@@ -726,7 +474,7 @@
deleteGroupMembers(groupName, groupMembers) {
const encodeName = encodeURIComponent(groupName);
const encodeMember = encodeURIComponent(groupMembers);
- return this._send({
+ return this._restApiHelper.send({
method: 'DELETE',
url: `/groups/${encodeName}/members/${encodeMember}`,
anonymizedUrl: '/groups/*/members/*',
@@ -736,7 +484,7 @@
deleteIncludedGroup(groupName, includedGroup) {
const encodeName = encodeURIComponent(groupName);
const encodeIncludedGroup = encodeURIComponent(includedGroup);
- return this._send({
+ return this._restApiHelper.send({
method: 'DELETE',
url: `/groups/${encodeName}/groups/${encodeIncludedGroup}`,
anonymizedUrl: '/groups/*/groups/*',
@@ -823,7 +571,7 @@
prefs.download_scheme = prefs.download_scheme.toLowerCase();
}
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: '/accounts/self/preferences',
body: prefs,
@@ -839,7 +587,7 @@
saveDiffPreferences(prefs, opt_errFn) {
// Invalidate the cache.
this._cache.delete('/accounts/self/preferences.diff');
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: '/accounts/self/preferences.diff',
body: prefs,
@@ -855,7 +603,7 @@
saveEditPreferences(prefs, opt_errFn) {
// Invalidate the cache.
this._cache.delete('/accounts/self/preferences.edit');
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: '/accounts/self/preferences.edit',
body: prefs,
@@ -889,14 +637,14 @@
},
getExternalIds() {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/accounts/self/external.ids',
reportUrlAsIs: true,
});
},
deleteAccountIdentity(id) {
- return this._send({
+ return this._restApiHelper.send({
method: 'POST',
url: '/accounts/self/external.ids:delete',
body: id,
@@ -910,7 +658,7 @@
* @return {!Promise<!Object>}
*/
getAccountDetails(userId) {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: `/accounts/${encodeURIComponent(userId)}/detail`,
anonymizedUrl: '/accounts/*/detail',
});
@@ -928,7 +676,7 @@
* @param {function(?Response, string=)=} opt_errFn
*/
addAccountEmail(email, opt_errFn) {
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: '/accounts/self/emails/' + encodeURIComponent(email),
errFn: opt_errFn,
@@ -941,7 +689,7 @@
* @param {function(?Response, string=)=} opt_errFn
*/
deleteAccountEmail(email, opt_errFn) {
- return this._send({
+ return this._restApiHelper.send({
method: 'DELETE',
url: '/accounts/self/emails/' + encodeURIComponent(email),
errFn: opt_errFn,
@@ -961,7 +709,7 @@
errFn: opt_errFn,
anonymizedUrl: '/accounts/self/emails/*/preferred',
};
- return this._send(req).then(() => {
+ return this._restApiHelper.send(req).then(() => {
// If result of getAccountEmails is in cache, update it in the cache
// so we don't have to invalidate it.
const cachedEmails = this._cache.get('/accounts/self/emails');
@@ -1005,7 +753,7 @@
parseResponse: true,
reportUrlAsIs: true,
};
- return this._send(req)
+ return this._restApiHelper.send(req)
.then(newName => this._updateCachedAccount({name: newName}));
},
@@ -1022,7 +770,7 @@
parseResponse: true,
reportUrlAsIs: true,
};
- return this._send(req)
+ return this._restApiHelper.send(req)
.then(newName => this._updateCachedAccount({username: newName}));
},
@@ -1039,33 +787,33 @@
parseResponse: true,
reportUrlAsIs: true,
};
- return this._send(req)
+ return this._restApiHelper.send(req)
.then(newStatus => this._updateCachedAccount({status: newStatus}));
},
getAccountStatus(userId) {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: `/accounts/${encodeURIComponent(userId)}/status`,
anonymizedUrl: '/accounts/*/status',
});
},
getAccountGroups() {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/accounts/self/groups',
reportUrlAsIs: true,
});
},
getAccountAgreements() {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/accounts/self/agreements',
reportUrlAsIs: true,
});
},
saveAccountAgreement(name) {
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: '/accounts/self/agreements',
body: name,
@@ -1092,6 +840,8 @@
getLoggedIn() {
return this.getAccount().then(account => {
return account != null;
+ }).catch(() => {
+ return false;
});
},
@@ -1108,29 +858,7 @@
},
checkCredentials() {
- if (this._credentialCheck.checking) {
- return;
- }
- this._credentialCheck.checking = true;
- const req = {url: '/accounts/self/detail', reportUrlAsIs: true};
- // Skip the REST response cache.
- return this._fetchRawJSON(req).then(res => {
- if (!res) { return; }
- if (res.status === 403) {
- this.fire('auth-error');
- this._cache.delete('/accounts/self/detail');
- } else if (res.ok) {
- return this.getResponseObject(res);
- }
- }).then(res => {
- this._credentialCheck.checking = false;
- if (res) {
- this._cache.delete('/accounts/self/detail');
- }
- return res;
- }).catch(err => {
- this._credentialCheck.checking = false;
- });
+ return this._restApiHelper.checkCredentials();
},
getDefaultPreferences() {
@@ -1146,6 +874,8 @@
const req = {url: '/accounts/self/preferences', reportUrlAsIs: true};
return this._fetchSharedCacheURL(req).then(res => {
if (this._isNarrowScreen()) {
+ // Note that this can be problematic, because the diff will stay
+ // unified even after increasing the window width.
res.default_diff_view = DiffViewMode.UNIFIED;
} else {
res.default_diff_view = res.diff_view;
@@ -1176,7 +906,7 @@
* @param {function(?Response, string=)=} opt_errFn
*/
saveWatchedProjects(projects, opt_errFn) {
- return this._send({
+ return this._restApiHelper.send({
method: 'POST',
url: '/accounts/self/watched.projects',
body: projects,
@@ -1191,7 +921,7 @@
* @param {function(?Response, string=)=} opt_errFn
*/
deleteWatchedProjects(projects, opt_errFn) {
- return this._send({
+ return this._restApiHelper.send({
method: 'POST',
url: '/accounts/self/watched.projects:delete',
body: projects,
@@ -1200,45 +930,6 @@
});
},
- /**
- * @param {Defs.FetchJSONRequest} req
- */
- _fetchSharedCacheURL(req) {
- if (this._sharedFetchPromises[req.url]) {
- return this._sharedFetchPromises[req.url];
- }
- // TODO(andybons): Periodic cache invalidation.
- if (this._cache.has(req.url)) {
- return Promise.resolve(this._cache.get(req.url));
- }
- this._sharedFetchPromises[req.url] = this._fetchJSON(req)
- .then(response => {
- if (response !== undefined) {
- this._cache.set(req.url, response);
- }
- this._sharedFetchPromises[req.url] = undefined;
- return response;
- }).catch(err => {
- this._sharedFetchPromises[req.url] = undefined;
- throw err;
- });
- return this._sharedFetchPromises[req.url];
- },
-
- /**
- * @param {string} prefix
- */
- _invalidateSharedFetchPromisesPrefix(prefix) {
- const newObject = {};
- Object.entries(this._sharedFetchPromises).forEach(([key, value]) => {
- if (!key.startsWith(prefix)) {
- newObject[key] = value;
- }
- });
- this._sharedFetchPromises = newObject;
- this._cache.invalidatePrefix(prefix);
- },
-
_isNarrowScreen() {
return window.innerWidth < MAX_UNIFIED_DEFAULT_WINDOW_WIDTH_PX;
},
@@ -1280,7 +971,7 @@
params,
reportUrlAsIs: true,
};
- return this._fetchJSON(req).then(response => {
+ return this._restApiHelper.fetchJSON(req).then(response => {
// Response may be an array of changes OR an array of arrays of
// changes.
if (opt_query instanceof Array) {
@@ -1336,13 +1027,13 @@
this.ListChangesOption.ALL_COMMITS,
this.ListChangesOption.ALL_REVISIONS,
this.ListChangesOption.CHANGE_ACTIONS,
- this.ListChangesOption.CURRENT_ACTIONS,
this.ListChangesOption.DETAILED_LABELS,
this.ListChangesOption.DOWNLOAD_COMMANDS,
this.ListChangesOption.MESSAGES,
this.ListChangesOption.SUBMITTABLE,
this.ListChangesOption.WEB_LINKS,
this.ListChangesOption.SKIP_MERGEABLE,
+ this.ListChangesOption.SKIP_DIFFSTAT,
];
return this.getConfig(false).then(config => {
if (config.receive && config.receive.enable_signed_push) {
@@ -1364,7 +1055,8 @@
const optionsHex = this.listChangesOptionsToHex(
this.ListChangesOption.ALL_COMMITS,
this.ListChangesOption.ALL_REVISIONS,
- this.ListChangesOption.SKIP_MERGEABLE
+ this.ListChangesOption.SKIP_MERGEABLE,
+ this.ListChangesOption.SKIP_DIFFSTAT
);
return this._getChangeDetail(changeNum, optionsHex, opt_errFn,
opt_cancelCondition);
@@ -1378,9 +1070,10 @@
*/
_getChangeDetail(changeNum, optionsHex, opt_errFn, opt_cancelCondition) {
return this.getChangeActionURL(changeNum, null, '/detail').then(url => {
- const urlWithParams = this._urlWithParams(url, optionsHex);
+ const urlWithParams = this._restApiHelper
+ .urlWithParams(url, optionsHex);
const params = {O: optionsHex};
- const req = {
+ let req = {
url,
errFn: opt_errFn,
cancelCondition: opt_cancelCondition,
@@ -1388,9 +1081,10 @@
fetchOptions: this._etags.getOptions(urlWithParams),
anonymizedUrl: '/changes/*~*/detail?O=' + optionsHex,
};
- return this._fetchRawJSON(req).then(response => {
+ req = this._restApiHelper.addAcceptJsonHeader(req);
+ return this._restApiHelper.fetchRawJSON(req).then(response => {
if (response && response.status === 304) {
- return Promise.resolve(this._parsePrefixedJSON(
+ return Promise.resolve(this._restApiHelper.parsePrefixedJSON(
this._etags.getCachedPayload(urlWithParams)));
}
@@ -1404,7 +1098,7 @@
}
const payloadPromise = response ?
- this._readResponsePayload(response) :
+ this._restApiHelper.readResponsePayload(response) :
Promise.resolve(null);
return payloadPromise.then(payload => {
@@ -1433,7 +1127,7 @@
/**
* @param {number|string} changeNum
- * @param {Defs.patchRange} patchRange
+ * @param {Gerrit.PatchRange} patchRange
* @param {number=} opt_parentIndex
*/
getChangeFiles(changeNum, patchRange, opt_parentIndex) {
@@ -1454,7 +1148,7 @@
/**
* @param {number|string} changeNum
- * @param {Defs.patchRange} patchRange
+ * @param {Gerrit.PatchRange} patchRange
*/
getChangeEditFiles(changeNum, patchRange) {
let endpoint = '/edit?list';
@@ -1487,7 +1181,7 @@
/**
* @param {number|string} changeNum
- * @param {Defs.patchRange} patchRange
+ * @param {Gerrit.PatchRange} patchRange
* @return {!Promise<!Array<!Object>>}
*/
getChangeOrEditFiles(changeNum, patchRange) {
@@ -1498,18 +1192,6 @@
return this.getChangeFiles(changeNum, patchRange);
},
- /**
- * The closure compiler doesn't realize this.specialFilePathCompare is
- * valid.
- *
- * @suppress {checkTypes}
- */
- getChangeFilePathsAsSpeciallySortedArray(changeNum, patchRange) {
- return this.getChangeFiles(changeNum, patchRange).then(files => {
- return Object.keys(files).sort(this.specialFilePathCompare);
- });
- },
-
getChangeRevisionActions(changeNum, patchNum) {
const req = {
changeNum,
@@ -1517,15 +1199,7 @@
patchNum,
reportEndpointAsIs: true,
};
- return this._getChangeURLAndFetch(req).then(revisionActions => {
- // The rebase button on change screen is always enabled.
- if (revisionActions.rebase) {
- revisionActions.rebase.rebaseOnCurrent =
- !!revisionActions.rebase.enabled;
- revisionActions.rebase.enabled = true;
- }
- return revisionActions;
- });
+ return this._getChangeURLAndFetch(req);
},
/**
@@ -1534,9 +1208,24 @@
* @param {function(?Response, string=)=} opt_errFn
*/
getChangeSuggestedReviewers(changeNum, inputVal, opt_errFn) {
+ return this._getChangeSuggestedGroup('REVIEWER', changeNum, inputVal,
+ opt_errFn);
+ },
+
+ /**
+ * @param {number|string} changeNum
+ * @param {string} inputVal
+ * @param {function(?Response, string=)=} opt_errFn
+ */
+ getChangeSuggestedCCs(changeNum, inputVal, opt_errFn) {
+ return this._getChangeSuggestedGroup('CC', changeNum, inputVal,
+ opt_errFn);
+ },
+
+ _getChangeSuggestedGroup(reviewerState, changeNum, inputVal, opt_errFn) {
// More suggestions may obscure content underneath in the reply dialog,
// see issue 10793.
- const params = {n: 6};
+ const params = {'n': 6, 'reviewer-state': reviewerState};
if (inputVal) { params.q = inputVal; }
return this._getChangeURLAndFetch({
changeNum,
@@ -1618,11 +1307,11 @@
},
invalidateGroupsCache() {
- this._invalidateSharedFetchPromisesPrefix('/groups/?');
+ this._restApiHelper.invalidateFetchPromisesPrefix('/groups/?');
},
invalidateReposCache() {
- this._invalidateSharedFetchPromisesPrefix('/projects/?');
+ this._restApiHelper.invalidateFetchPromisesPrefix('/projects/?');
},
/**
@@ -1660,7 +1349,7 @@
setRepoHead(repo, ref) {
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/projects/${encodeURIComponent(repo)}/HEAD`,
body: {ref},
@@ -1684,7 +1373,7 @@
const url = `/projects/${repo}/branches?n=${count}&S=${offset}${filter}`;
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url,
errFn: opt_errFn,
anonymizedUrl: '/projects/*/branches?*',
@@ -1708,7 +1397,7 @@
encodedFilter;
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url,
errFn: opt_errFn,
anonymizedUrl: '/projects/*/tags',
@@ -1727,7 +1416,7 @@
const encodedFilter = this._computeFilter(filter);
const n = pluginsPerPage + 1;
const url = `/plugins/?all&n=${n}&S=${offset}${encodedFilter}`;
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url,
errFn: opt_errFn,
anonymizedUrl: '/plugins/?all',
@@ -1737,7 +1426,7 @@
getRepoAccessRights(repoName, opt_errFn) {
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: `/projects/${encodeURIComponent(repoName)}/access`,
errFn: opt_errFn,
anonymizedUrl: '/projects/*/access',
@@ -1747,7 +1436,7 @@
setRepoAccessRights(repoName, repoInfo) {
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
- return this._send({
+ return this._restApiHelper.send({
method: 'POST',
url: `/projects/${encodeURIComponent(repoName)}/access`,
body: repoInfo,
@@ -1756,7 +1445,7 @@
},
setRepoAccessRightsForReview(projectName, projectInfo) {
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: `/projects/${encodeURIComponent(projectName)}/access:review`,
body: projectInfo,
@@ -1773,7 +1462,7 @@
getSuggestedGroups(inputVal, opt_n, opt_errFn) {
const params = {s: inputVal};
if (opt_n) { params.n = opt_n; }
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/groups/',
errFn: opt_errFn,
params,
@@ -1793,7 +1482,7 @@
type: 'ALL',
};
if (opt_n) { params.n = opt_n; }
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/projects/',
errFn: opt_errFn,
params,
@@ -1812,7 +1501,7 @@
}
const params = {suggest: null, q: inputVal};
if (opt_n) { params.n = opt_n; }
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/accounts/',
errFn: opt_errFn,
params,
@@ -1843,7 +1532,7 @@
throw Error('Unsupported HTTP method: ' + method);
}
- return this._send({method, url, body});
+ return this._restApiHelper.send({method, url, body});
});
},
@@ -1873,7 +1562,7 @@
O: options,
q: 'status:open is:mergeable conflicts:' + changeNum,
};
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/changes/',
params,
anonymizedUrl: '/changes/conflicts:*',
@@ -1895,7 +1584,7 @@
O: options,
q: query,
};
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/changes/',
params,
anonymizedUrl: '/changes/change:*',
@@ -1918,7 +1607,7 @@
O: options,
q: query,
};
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/changes/',
params,
anonymizedUrl: '/changes/topic:*',
@@ -1964,7 +1653,7 @@
this.getChangeActionURL(changeNum, patchNum, '/review'),
];
return Promise.all(promises).then(([, url]) => {
- return this._send({
+ return this._restApiHelper.send({
method: 'POST',
url,
body: review,
@@ -1998,7 +1687,7 @@
*/
createChange(project, branch, subject, opt_topic, opt_isPrivate,
opt_workInProgress, opt_baseChange, opt_baseCommit) {
- return this._send({
+ return this._restApiHelper.send({
method: 'POST',
url: '/changes/',
body: {
@@ -2175,7 +1864,7 @@
return this.getFromProjectLookup(changeNum).then(project => {
const url = '/accounts/self/starred.changes/' +
(project ? encodeURIComponent(project) + '~' : '') + changeNum;
- return this._send({
+ return this._restApiHelper.send({
method: starred ? 'PUT' : 'DELETE',
url,
anonymizedUrl: '/accounts/self/starred.changes/*',
@@ -2192,60 +1881,7 @@
},
/**
- * Send an XHR.
- *
- * @param {Defs.SendRequest} req
- * @return {Promise}
- */
- _send(req) {
- const options = {method: req.method};
- if (req.body) {
- options.headers = new Headers();
- options.headers.set(
- 'Content-Type', req.contentType || 'application/json');
- options.body = typeof req.body === 'string' ?
- req.body : JSON.stringify(req.body);
- }
- if (req.headers) {
- if (!options.headers) { options.headers = new Headers(); }
- for (const header in req.headers) {
- if (!req.headers.hasOwnProperty(header)) { continue; }
- options.headers.set(header, req.headers[header]);
- }
- }
- const url = req.url.startsWith('http') ?
- req.url : this.getBaseUrl() + req.url;
- const fetchReq = {
- url,
- fetchOptions: options,
- anonymizedUrl: req.reportUrlAsIs ? url : req.anonymizedUrl,
- };
- const xhr = this._fetch(fetchReq).then(response => {
- if (!response.ok) {
- if (req.errFn) {
- return req.errFn.call(undefined, response);
- }
- this.fire('server-error', {request: fetchReq, response});
- }
- return response;
- }).catch(err => {
- this.fire('network-error', {error: err});
- if (req.errFn) {
- return req.errFn.call(undefined, null, err);
- } else {
- throw err;
- }
- });
-
- if (req.parseResponse) {
- return xhr.then(res => this.getResponseObject(res));
- }
-
- return xhr;
- },
-
- /**
- * Public version of the _send method preserved for plugins.
+ * Public version of the _restApiHelper.send method preserved for plugins.
*
* @param {string} method
* @param {string} url
@@ -2259,7 +1895,7 @@
*/
send(method, url, opt_body, opt_errFn, opt_contentType,
opt_headers) {
- return this._send({
+ return this._restApiHelper.send({
method,
url,
body: opt_body,
@@ -2524,7 +2160,7 @@
},
getCommitInfo(project, commit) {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/projects/' + encodeURIComponent(project) +
'/commits/' + encodeURIComponent(commit),
anonymizedUrl: '/projects/*/comments/*',
@@ -2532,7 +2168,7 @@
},
_fetchB64File(url) {
- return this._fetch({url: this.getBaseUrl() + url})
+ return this._restApiHelper.fetch({url: this.getBaseUrl() + url})
.then(response => {
if (!response.ok) {
return Promise.reject(new Error(response.statusText));
@@ -2656,7 +2292,7 @@
},
deleteAccountHttpPassword() {
- return this._send({
+ return this._restApiHelper.send({
method: 'DELETE',
url: '/accounts/self/password.http',
reportUrlAsIs: true,
@@ -2669,7 +2305,7 @@
* parameter.
*/
generateAccountHttpPassword() {
- return this._send({
+ return this._restApiHelper.send({
method: 'PUT',
url: '/accounts/self/password.http',
body: {generate: true},
@@ -2693,7 +2329,7 @@
contentType: 'text/plain',
reportUrlAsIs: true,
};
- return this._send(req)
+ return this._restApiHelper.send(req)
.then(response => {
if (response.status < 200 && response.status >= 300) {
return Promise.reject(new Error('error'));
@@ -2707,7 +2343,7 @@
},
deleteAccountSSHKey(id) {
- return this._send({
+ return this._restApiHelper.send({
method: 'DELETE',
url: '/accounts/self/sshkeys/' + id,
anonymizedUrl: '/accounts/self/sshkeys/*',
@@ -2715,7 +2351,7 @@
},
getAccountGPGKeys() {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/accounts/self/gpgkeys',
reportUrlAsIs: true,
});
@@ -2728,7 +2364,7 @@
body: key,
reportUrlAsIs: true,
};
- return this._send(req)
+ return this._restApiHelper.send(req)
.then(response => {
if (response.status < 200 && response.status >= 300) {
return Promise.reject(new Error('error'));
@@ -2742,7 +2378,7 @@
},
deleteAccountGPGKey(id) {
- return this._send({
+ return this._restApiHelper.send({
method: 'DELETE',
url: '/accounts/self/gpgkeys/' + id,
anonymizedUrl: '/accounts/self/gpgkeys/*',
@@ -2775,7 +2411,7 @@
body: {token},
reportUrlAsIs: true,
};
- return this._send(req).then(response => {
+ return this._restApiHelper.send(req).then(response => {
if (response.status === 204) {
return 'Email confirmed successfully.';
}
@@ -2784,7 +2420,7 @@
},
getCapabilities(opt_errFn) {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: '/config/server/capabilities',
errFn: opt_errFn,
reportUrlAsIs: true,
@@ -2792,7 +2428,7 @@
},
getTopMenus(opt_errFn) {
- return this._fetchJSON({
+ return this._fetchSharedCacheURL({
url: '/config/server/top-menus',
errFn: opt_errFn,
reportUrlAsIs: true,
@@ -2890,7 +2526,7 @@
*/
getChange(changeNum, opt_errFn) {
// Cannot use _changeBaseURL, as this function is used by _projectLookup.
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: `/changes/?q=change:${changeNum}`,
errFn: opt_errFn,
anonymizedUrl: '/changes/?q=change:*',
@@ -2941,7 +2577,7 @@
* Alias for _changeBaseURL.then(send).
*
* @todo(beckysiegel) clean up comments
- * @param {Defs.ChangeSendRequest} req
+ * @param {Gerrit.ChangeSendRequest} req
* @return {!Promise<!Object>}
*/
_getChangeURLAndSend(req) {
@@ -2951,7 +2587,7 @@
req.endpoint : req.anonymizedEndpoint;
return this._changeBaseURL(req.changeNum, req.patchNum).then(url => {
- return this._send({
+ return this._restApiHelper.send({
method: req.method,
url: url + req.endpoint,
body: req.body,
@@ -2968,7 +2604,7 @@
/**
* Alias for _changeBaseURL.then(_fetchJSON).
*
- * @param {Defs.ChangeFetchRequest} req
+ * @param {Gerrit.ChangeFetchRequest} req
* @return {!Promise<!Object>}
*/
_getChangeURLAndFetch(req) {
@@ -2977,7 +2613,7 @@
const anonymizedBaseUrl = req.patchNum ?
ANONYMIZED_REVISION_BASE_URL : ANONYMIZED_CHANGE_BASE_URL;
return this._changeBaseURL(req.changeNum, req.patchNum).then(url => {
- return this._fetchJSON({
+ return this._restApiHelper.fetchJSON({
url: url + req.endpoint,
errFn: req.errFn,
params: req.params,
@@ -3108,7 +2744,7 @@
},
deleteDraftComments(query) {
- return this._send({
+ return this._restApiHelper.send({
method: 'POST',
url: '/accounts/self/drafts:delete',
body: {query},
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index ef4e401e64..635e0f5065 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-rest-api-interface</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
@@ -61,93 +63,8 @@ limitations under the License.
sandbox.restore();
});
- test('JSON prefix is properly removed', done => {
- element._fetchJSON('/dummy/url').then(obj => {
- assert.deepEqual(obj, {hello: 'bonjour'});
- done();
- });
- });
-
- test('cached results', done => {
- let n = 0;
- sandbox.stub(element, '_fetchJSON', () => {
- return Promise.resolve(++n);
- });
- const promises = [];
- promises.push(element._fetchSharedCacheURL('/foo'));
- promises.push(element._fetchSharedCacheURL('/foo'));
- promises.push(element._fetchSharedCacheURL('/foo'));
-
- Promise.all(promises).then(results => {
- assert.deepEqual(results, [1, 1, 1]);
- element._fetchSharedCacheURL('/foo').then(foo => {
- assert.equal(foo, 1);
- done();
- });
- });
- });
-
- test('cached promise', done => {
- const promise = Promise.reject(new Error('foo'));
- element._cache.set('/foo', promise);
- element._fetchSharedCacheURL({url: '/foo'}).catch(p => {
- assert.equal(p.message, 'foo');
- done();
- });
- });
-
- test('cache invalidation', () => {
- element._cache.set('/foo/bar', 1);
- element._cache.set('/bar', 2);
- element._sharedFetchPromises['/foo/bar'] = 3;
- element._sharedFetchPromises['/bar'] = 4;
- element._invalidateSharedFetchPromisesPrefix('/foo/');
- assert.isFalse(element._cache.has('/foo/bar'));
- assert.isTrue(element._cache.has('/bar'));
- assert.isUndefined(element._sharedFetchPromises['/foo/bar']);
- assert.strictEqual(4, element._sharedFetchPromises['/bar']);
- });
-
- test('params are properly encoded', () => {
- let url = element._urlWithParams('/path/', {
- sp: 'hola',
- gr: 'guten tag',
- noval: null,
- });
- assert.equal(url,
- window.CANONICAL_PATH + '/path/?sp=hola&gr=guten%20tag&noval');
-
- url = element._urlWithParams('/path/', {
- sp: 'hola',
- en: ['hey', 'hi'],
- });
- assert.equal(url, window.CANONICAL_PATH + '/path/?sp=hola&en=hey&en=hi');
-
- // Order must be maintained with array params.
- url = element._urlWithParams('/path/', {
- l: ['c', 'b', 'a'],
- });
- assert.equal(url, window.CANONICAL_PATH + '/path/?l=c&l=b&l=a');
- });
-
- test('request callbacks can be canceled', done => {
- let cancelCalled = false;
- window.fetch.returns(Promise.resolve({
- body: {
- cancel() { cancelCalled = true; },
- },
- }));
- const cancelCondition = () => { return true; };
- element._fetchJSON({url: '/dummy/url', cancelCondition}).then(
- obj => {
- assert.isUndefined(obj);
- assert.isTrue(cancelCalled);
- done();
- });
- });
-
test('parent diff comments are properly grouped', done => {
- sandbox.stub(element, '_fetchJSON', () => {
+ sandbox.stub(element._restApiHelper, 'fetchJSON', () => {
return Promise.resolve({
'/COMMIT_MSG': [],
'sieve.go': [
@@ -290,7 +207,7 @@ limitations under the License.
test('differing patch diff comments are properly grouped', done => {
sandbox.stub(element, 'getFromProjectLookup')
.returns(Promise.resolve('test'));
- sandbox.stub(element, '_fetchJSON', request => {
+ sandbox.stub(element._restApiHelper, 'fetchJSON', request => {
const url = request.url;
if (url === '/changes/test~42/revisions/1') {
return Promise.resolve({
@@ -404,37 +321,6 @@ limitations under the License.
]);
});
- suite('rebase action', () => {
- let resolve_fetchJSON;
- setup(() => {
- sandbox.stub(element, '_fetchJSON').returns(
- new Promise(resolve => {
- resolve_fetchJSON = resolve;
- }));
- });
-
- test('no rebase on current', done => {
- element.getChangeRevisionActions('42', '1337').then(
- response => {
- assert.isTrue(response.rebase.enabled);
- assert.isFalse(response.rebase.rebaseOnCurrent);
- done();
- });
- resolve_fetchJSON({rebase: {}});
- });
-
- test('rebase on current', done => {
- element.getChangeRevisionActions('42', '1337').then(
- response => {
- assert.isTrue(response.rebase.enabled);
- assert.isTrue(response.rebase.rebaseOnCurrent);
- done();
- });
- resolve_fetchJSON({rebase: {enabled: true}});
- });
- });
-
-
test('server error', done => {
const getResponseObjectStub = sandbox.stub(element, 'getResponseObject');
window.fetch.returns(Promise.resolve({ok: false}));
@@ -442,7 +328,7 @@ limitations under the License.
element.addEventListener('server-error', resolve);
});
- element._fetchJSON({}).then(response => {
+ element._restApiHelper.fetchJSON({}).then(response => {
assert.isUndefined(response);
assert.isTrue(getResponseObjectStub.notCalled);
serverErrorEventPromise.then(() => done());
@@ -458,12 +344,31 @@ limitations under the License.
Promise.reject(new Error('Failed to fetch')));
window.fetch.onSecondCall().returns(Promise.resolve(fakeAuthResponse));
// Emulate logged in.
+ element._restApiHelper._cache.set('/accounts/self/detail', {});
+ const serverErrorStub = sandbox.stub();
+ element.addEventListener('server-error', serverErrorStub);
+ const authErrorStub = sandbox.stub();
+ element.addEventListener('auth-error', authErrorStub);
+ element._restApiHelper.fetchJSON({url: '/bar'}).finally(r => {
+ flush(() => {
+ assert.isTrue(authErrorStub.called);
+ assert.isFalse(serverErrorStub.called);
+ assert.isFalse(element._cache.has('/accounts/self/detail'));
+ done();
+ });
+ });
+ });
+
+ test('auth failure - test all failed to fetch', done => {
+ window.fetch.returns(
+ Promise.reject(new Error('Failed to fetch')));
+ // Emulate logged in.
element._cache.set('/accounts/self/detail', {});
const serverErrorStub = sandbox.stub();
element.addEventListener('server-error', serverErrorStub);
const authErrorStub = sandbox.stub();
element.addEventListener('auth-error', authErrorStub);
- element._fetchJSON('/bar').then(r => {
+ element._restApiHelper.fetchJSON({url: '/bar'}).finally(r => {
flush(() => {
assert.isTrue(authErrorStub.called);
assert.isFalse(serverErrorStub.called);
@@ -473,6 +378,15 @@ limitations under the License.
});
});
+ test('getLoggedIn returns false when network/auth failure', done => {
+ window.fetch.returns(
+ Promise.reject(new Error('Failed to fetch')));
+ element.getLoggedIn().then(isLoggedIn => {
+ assert.isFalse(isLoggedIn);
+ done();
+ });
+ });
+
test('checkCredentials', done => {
const responses = [
{
@@ -505,7 +419,8 @@ limitations under the License.
test('checkCredentials promise rejection', () => {
window.fetch.restore();
element._cache.set('/accounts/self/detail', true);
- sandbox.spy(element, 'checkCredentials');
+ const checkCredentialsSpy =
+ sandbox.spy(element._restApiHelper, 'checkCredentials');
sandbox.stub(window, 'fetch', url => {
return Promise.reject(new Error('Failed to fetch'));
});
@@ -517,13 +432,22 @@ limitations under the License.
// The second fetch call also fails, which leads to a second
// invocation of checkCredentials, which should immediately
// return instead of making further fetch calls.
- assert.isTrue(element.checkCredentials.calledTwice);
+ assert.isTrue(checkCredentialsSpy .calledTwice);
assert.isTrue(window.fetch.calledTwice);
});
});
+ test('checkCredentials accepts only json', () => {
+ const authFetchStub = sandbox.stub(element._auth, 'fetch')
+ .returns(Promise.resolve());
+ element.checkCredentials();
+ assert.isTrue(authFetchStub.called);
+ assert.equal(authFetchStub.lastCall.args[1].headers.get('Accept'),
+ 'application/json');
+ });
+
test('legacy n,z key in change url is replaced', () => {
- const stub = sandbox.stub(element, '_fetchJSON')
+ const stub = sandbox.stub(element._restApiHelper, 'fetchJSON')
.returns(Promise.resolve([]));
element.getChanges(1, null, 'n,z');
assert.equal(stub.lastCall.args[0].params.S, 0);
@@ -531,38 +455,38 @@ limitations under the License.
test('saveDiffPreferences invalidates cache line', () => {
const cacheKey = '/accounts/self/preferences.diff';
- sandbox.stub(element, '_send');
+ const sendStub = sandbox.stub(element._restApiHelper, 'send');
element._cache.set(cacheKey, {tab_size: 4});
element.saveDiffPreferences({tab_size: 8});
- assert.isTrue(element._send.called);
- assert.isFalse(element._cache.has(cacheKey));
+ assert.isTrue(sendStub.called);
+ assert.isFalse(element._restApiHelper._cache.has(cacheKey));
});
test('getAccount when resp is null does not add anything to the cache',
done => {
const cacheKey = '/accounts/self/detail';
- const stub = sandbox.stub(element, '_fetchSharedCacheURL',
+ const stub = sandbox.stub(element._restApiHelper, 'fetchCacheURL',
() => Promise.resolve());
element.getAccount().then(() => {
- assert.isTrue(element._fetchSharedCacheURL.called);
- assert.isFalse(element._cache.has(cacheKey));
+ assert.isTrue(stub.called);
+ assert.isFalse(element._restApiHelper._cache.has(cacheKey));
done();
});
- element._cache.set(cacheKey, 'fake cache');
+ element._restApiHelper._cache.set(cacheKey, 'fake cache');
stub.lastCall.args[0].errFn();
});
test('getAccount does not add to the cache when resp.status is 403',
done => {
const cacheKey = '/accounts/self/detail';
- const stub = sandbox.stub(element, '_fetchSharedCacheURL',
+ const stub = sandbox.stub(element._restApiHelper, 'fetchCacheURL',
() => Promise.resolve());
element.getAccount().then(() => {
- assert.isTrue(element._fetchSharedCacheURL.called);
- assert.isFalse(element._cache.has(cacheKey));
+ assert.isTrue(stub.called);
+ assert.isFalse(element._restApiHelper._cache.has(cacheKey));
done();
});
element._cache.set(cacheKey, 'fake cache');
@@ -571,15 +495,15 @@ limitations under the License.
test('getAccount when resp is successful', done => {
const cacheKey = '/accounts/self/detail';
- const stub = sandbox.stub(element, '_fetchSharedCacheURL',
+ const stub = sandbox.stub(element._restApiHelper, 'fetchCacheURL',
() => Promise.resolve());
element.getAccount().then(response => {
- assert.isTrue(element._fetchSharedCacheURL.called);
- assert.equal(element._cache.get(cacheKey), 'fake cache');
+ assert.isTrue(stub.called);
+ assert.equal(element._restApiHelper._cache.get(cacheKey), 'fake cache');
done();
});
- element._cache.set(cacheKey, 'fake cache');
+ element._restApiHelper._cache.set(cacheKey, 'fake cache');
stub.lastCall.args[0].errFn({});
});
@@ -591,7 +515,7 @@ limitations under the License.
sandbox.stub(element, '_isNarrowScreen', () => {
return smallScreen;
});
- sandbox.stub(element, '_fetchSharedCacheURL', () => {
+ sandbox.stub(element._restApiHelper, 'fetchCacheURL', () => {
return Promise.resolve(testJSON);
});
};
@@ -656,10 +580,10 @@ limitations under the License.
});
test('savPreferences normalizes download scheme', () => {
- sandbox.stub(element, '_send');
+ const sendStub = sandbox.stub(element._restApiHelper, 'send');
element.savePreferences({download_scheme: 'HTTP'});
- assert.isTrue(element._send.called);
- assert.equal(element._send.lastCall.args[0].body.download_scheme, 'http');
+ assert.isTrue(sendStub.called);
+ assert.equal(sendStub.lastCall.args[0].body.download_scheme, 'http');
});
test('getDiffPreferences returns correct defaults', done => {
@@ -685,10 +609,10 @@ limitations under the License.
});
test('saveDiffPreferences set show_tabs to false', () => {
- sandbox.stub(element, '_send');
+ const sendStub = sandbox.stub(element._restApiHelper, 'send');
element.saveDiffPreferences({show_tabs: false});
- assert.isTrue(element._send.called);
- assert.equal(element._send.lastCall.args[0].body.show_tabs, false);
+ assert.isTrue(sendStub.called);
+ assert.equal(sendStub.lastCall.args[0].body.show_tabs, false);
});
test('getEditPreferences returns correct defaults', done => {
@@ -718,34 +642,36 @@ limitations under the License.
});
test('saveEditPreferences set show_tabs to false', () => {
- sandbox.stub(element, '_send');
+ const sendStub = sandbox.stub(element._restApiHelper, 'send');
element.saveEditPreferences({show_tabs: false});
- assert.isTrue(element._send.called);
- assert.equal(element._send.lastCall.args[0].body.show_tabs, false);
+ assert.isTrue(sendStub.called);
+ assert.equal(sendStub.lastCall.args[0].body.show_tabs, false);
});
test('confirmEmail', () => {
- sandbox.spy(element, '_send');
+ const sendStub = sandbox.spy(element._restApiHelper, 'send');
element.confirmEmail('foo');
- assert.isTrue(element._send.calledOnce);
- assert.equal(element._send.lastCall.args[0].method, 'PUT');
- assert.equal(element._send.lastCall.args[0].url,
+ assert.isTrue(sendStub.calledOnce);
+ assert.equal(sendStub.lastCall.args[0].method, 'PUT');
+ assert.equal(sendStub.lastCall.args[0].url,
'/config/server/email.confirm');
- assert.deepEqual(element._send.lastCall.args[0].body, {token: 'foo'});
+ assert.deepEqual(sendStub.lastCall.args[0].body, {token: 'foo'});
});
test('setAccountStatus', () => {
- sandbox.stub(element, '_send').returns(Promise.resolve('OOO'));
+ const sendStub = sandbox.stub(element._restApiHelper, 'send')
+ .returns(Promise.resolve('OOO'));
element._cache.set('/accounts/self/detail', {});
return element.setAccountStatus('OOO').then(() => {
- assert.isTrue(element._send.calledOnce);
- assert.equal(element._send.lastCall.args[0].method, 'PUT');
- assert.equal(element._send.lastCall.args[0].url,
+ assert.isTrue(sendStub.calledOnce);
+ assert.equal(sendStub.lastCall.args[0].method, 'PUT');
+ assert.equal(sendStub.lastCall.args[0].url,
'/accounts/self/status');
- assert.deepEqual(element._send.lastCall.args[0].body,
- {status: 'OOO'});
- assert.deepEqual(element._cache.get('/accounts/self/detail'),
+ assert.deepEqual(sendStub.lastCall.args[0].body,
{status: 'OOO'});
+ assert.deepEqual(element._restApiHelper
+ ._cache.get('/accounts/self/detail'),
+ {status: 'OOO'});
});
});
@@ -834,18 +760,20 @@ limitations under the License.
const change_num = '1';
const file_name = 'index.php';
const file_contents = '<?php';
- sandbox.stub(element, '_send').returns(
+ sandbox.stub(element._restApiHelper, 'send').returns(
Promise.resolve([change_num, file_name, file_contents]));
sandbox.stub(element, 'getResponseObject')
.returns(Promise.resolve([change_num, file_name, file_contents]));
element._cache.set('/changes/' + change_num + '/edit/' + file_name, {});
return element.saveChangeEdit(change_num, file_name, file_contents)
.then(() => {
- assert.isTrue(element._send.calledOnce);
- assert.equal(element._send.lastCall.args[0].method, 'PUT');
- assert.equal(element._send.lastCall.args[0].url,
+ assert.isTrue(element._restApiHelper.send.calledOnce);
+ assert.equal(element._restApiHelper.send.lastCall.args[0].method,
+ 'PUT');
+ assert.equal(element._restApiHelper.send.lastCall.args[0].url,
'/changes/test~1/edit/' + file_name);
- assert.equal(element._send.lastCall.args[0].body, file_contents);
+ assert.equal(element._restApiHelper.send.lastCall.args[0].body,
+ file_contents);
});
});
@@ -853,17 +781,18 @@ limitations under the License.
element._projectLookup = {1: 'test'};
const change_num = '1';
const message = 'this is a commit message';
- sandbox.stub(element, '_send').returns(
+ sandbox.stub(element._restApiHelper, 'send').returns(
Promise.resolve([change_num, message]));
sandbox.stub(element, 'getResponseObject')
.returns(Promise.resolve([change_num, message]));
element._cache.set('/changes/' + change_num + '/message', {});
return element.putChangeCommitMessage(change_num, message).then(() => {
- assert.isTrue(element._send.calledOnce);
- assert.equal(element._send.lastCall.args[0].method, 'PUT');
- assert.equal(element._send.lastCall.args[0].url,
+ assert.isTrue(element._restApiHelper.send.calledOnce);
+ assert.equal(element._restApiHelper.send.lastCall.args[0].method, 'PUT');
+ assert.equal(element._restApiHelper.send.lastCall.args[0].url,
'/changes/test~1/message');
- assert.deepEqual(element._send.lastCall.args[0].body, {message});
+ assert.deepEqual(element._restApiHelper.send.lastCall.args[0].body,
+ {message});
});
});
@@ -919,7 +848,7 @@ limitations under the License.
});
test('createRepo encodes name', () => {
- const sendStub = sandbox.stub(element, '_send')
+ const sendStub = sandbox.stub(element._restApiHelper, 'send')
.returns(Promise.resolve());
return element.createRepo({name: 'x/y'}).then(() => {
assert.isTrue(sendStub.calledOnce);
@@ -965,64 +894,65 @@ limitations under the License.
suite('getRepos', () => {
const defaultQuery = 'state%3Aactive%20OR%20state%3Aread-only';
-
+ let fetchCacheURLStub;
setup(() => {
- sandbox.stub(element, '_fetchSharedCacheURL');
+ fetchCacheURLStub =
+ sandbox.stub(element._restApiHelper, 'fetchCacheURL');
});
test('normal use', () => {
element.getRepos('test', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/projects/?n=26&S=0&query=test');
element.getRepos(null, 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
`/projects/?n=26&S=0&query=${defaultQuery}`);
element.getRepos('test', 25, 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/projects/?n=26&S=25&query=test');
});
test('with blank', () => {
element.getRepos('test/test', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/projects/?n=26&S=0&query=inname%3Atest%20AND%20inname%3Atest');
});
test('with hyphen', () => {
element.getRepos('foo-bar', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
});
test('with leading hyphen', () => {
element.getRepos('-bar', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/projects/?n=26&S=0&query=inname%3Abar');
});
test('with trailing hyphen', () => {
element.getRepos('foo-bar-', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
});
test('with underscore', () => {
element.getRepos('foo_bar', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
});
test('with underscore', () => {
element.getRepos('foo_bar', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
});
test('hyphen only', () => {
element.getRepos('-', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
`/projects/?n=26&S=0&query=${defaultQuery}`);
});
});
@@ -1051,43 +981,45 @@ limitations under the License.
});
suite('getGroups', () => {
+ let fetchCacheURLStub;
setup(() => {
- sandbox.stub(element, '_fetchSharedCacheURL');
+ fetchCacheURLStub =
+ sandbox.stub(element._restApiHelper, 'fetchCacheURL');
});
test('normal use', () => {
element.getGroups('test', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/groups/?n=26&S=0&m=test');
element.getGroups(null, 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/groups/?n=26&S=0');
element.getGroups('test', 25, 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/groups/?n=26&S=25&m=test');
});
test('regex', () => {
element.getGroups('^test.*', 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/groups/?n=26&S=0&r=%5Etest.*');
element.getGroups('^test.*', 25, 25);
- assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ assert.equal(fetchCacheURLStub.lastCall.args[0].url,
'/groups/?n=26&S=25&r=%5Etest.*');
});
});
test('gerrit auth is used', () => {
sandbox.stub(Gerrit.Auth, 'fetch').returns(Promise.resolve());
- element._fetchJSON('foo');
+ element._restApiHelper.fetchJSON({url: 'foo'});
assert(Gerrit.Auth.fetch.called);
});
test('getSuggestedAccounts does not return _fetchJSON', () => {
- const _fetchJSONSpy = sandbox.spy(element, '_fetchJSON');
+ const _fetchJSONSpy = sandbox.spy(element._restApiHelper, 'fetchJSON');
return element.getSuggestedAccounts().then(accts => {
assert.isFalse(_fetchJSONSpy.called);
assert.equal(accts.length, 0);
@@ -1095,7 +1027,7 @@ limitations under the License.
});
test('_fetchJSON gets called by getSuggestedAccounts', () => {
- const _fetchJSONStub = sandbox.stub(element, '_fetchJSON',
+ const _fetchJSONStub = sandbox.stub(element._restApiHelper, 'fetchJSON',
() => Promise.resolve());
return element.getSuggestedAccounts('own').then(() => {
assert.deepEqual(_fetchJSONStub.lastCall.args[0].params, {
@@ -1167,24 +1099,35 @@ limitations under the License.
const errFn = sinon.stub();
sandbox.stub(element, 'getChangeActionURL')
.returns(Promise.resolve(''));
- sandbox.stub(element, '_fetchRawJSON')
+ sandbox.stub(element._restApiHelper, 'fetchRawJSON')
.returns(Promise.resolve({ok: false, status: 500}));
return element._getChangeDetail(123, '516714', errFn).then(() => {
assert.isTrue(errFn.called);
});
});
+ test('_getChangeDetail accepts only json', () => {
+ const authFetchStub = sandbox.stub(element._auth, 'fetch')
+ .returns(Promise.resolve());
+ const errFn = sinon.stub();
+ element._getChangeDetail(123, '516714', errFn);
+ assert.isTrue(authFetchStub.called);
+ assert.equal(authFetchStub.lastCall.args[1].headers.get('Accept'),
+ 'application/json');
+ });
+
test('_getChangeDetail populates _projectLookup', () => {
sandbox.stub(element, 'getChangeActionURL')
.returns(Promise.resolve(''));
- sandbox.stub(element, '_fetchRawJSON')
+ sandbox.stub(element._restApiHelper, 'fetchRawJSON')
.returns(Promise.resolve({ok: true}));
const mockResponse = {_number: 1, project: 'test'};
- sandbox.stub(element, '_readResponsePayload').returns(Promise.resolve({
- parsed: mockResponse,
- raw: JSON.stringify(mockResponse),
- }));
+ sandbox.stub(element._restApiHelper, 'readResponsePayload')
+ .returns(Promise.resolve({
+ parsed: mockResponse,
+ raw: JSON.stringify(mockResponse),
+ }));
return element._getChangeDetail(1, '516714').then(() => {
assert.equal(Object.keys(element._projectLookup).length, 1);
assert.equal(element._projectLookup[1], 'test');
@@ -1202,7 +1145,8 @@ limitations under the License.
const mockResponse = {foo: 'bar', baz: 42};
mockResponseSerial = element.JSON_PREFIX +
JSON.stringify(mockResponse);
- sandbox.stub(element, '_urlWithParams').returns(requestUrl);
+ sandbox.stub(element._restApiHelper, 'urlWithParams')
+ .returns(requestUrl);
sandbox.stub(element, 'getChangeActionURL')
.returns(Promise.resolve(requestUrl));
collectSpy = sandbox.spy(element._etags, 'collect');
@@ -1210,11 +1154,12 @@ limitations under the License.
});
test('contributes to cache', () => {
- sandbox.stub(element, '_fetchRawJSON').returns(Promise.resolve({
- text: () => Promise.resolve(mockResponseSerial),
- status: 200,
- ok: true,
- }));
+ sandbox.stub(element._restApiHelper, 'fetchRawJSON')
+ .returns(Promise.resolve({
+ text: () => Promise.resolve(mockResponseSerial),
+ status: 200,
+ ok: true,
+ }));
return element._getChangeDetail(123, '516714').then(detail => {
assert.isFalse(getPayloadSpy.called);
@@ -1225,11 +1170,12 @@ limitations under the License.
});
test('uses cache on HTTP 304', () => {
- sandbox.stub(element, '_fetchRawJSON').returns(Promise.resolve({
- text: () => Promise.resolve(mockResponseSerial),
- status: 304,
- ok: true,
- }));
+ sandbox.stub(element._restApiHelper, 'fetchRawJSON')
+ .returns(Promise.resolve({
+ text: () => Promise.resolve(mockResponseSerial),
+ status: 304,
+ ok: true,
+ }));
return element._getChangeDetail(123, {}).then(detail => {
assert.isFalse(collectSpy.called);
@@ -1274,7 +1220,7 @@ limitations under the License.
suite('getChanges populates _projectLookup', () => {
test('multiple queries', () => {
- sandbox.stub(element, '_fetchJSON')
+ sandbox.stub(element._restApiHelper, 'fetchJSON')
.returns(Promise.resolve([
[
{_number: 1, project: 'test'},
@@ -1294,7 +1240,7 @@ limitations under the License.
});
test('no query', () => {
- sandbox.stub(element, '_fetchJSON')
+ sandbox.stub(element._restApiHelper, 'fetchJSON')
.returns(Promise.resolve([
{_number: 1, project: 'test'},
{_number: 2, project: 'test'},
@@ -1314,7 +1260,7 @@ limitations under the License.
test('_getChangeURLAndFetch', () => {
element._projectLookup = {1: 'test'};
- const fetchStub = sandbox.stub(element, '_fetchJSON')
+ const fetchStub = sandbox.stub(element._restApiHelper, 'fetchJSON')
.returns(Promise.resolve());
const req = {changeNum: 1, endpoint: '/test', patchNum: 1};
return element._getChangeURLAndFetch(req).then(() => {
@@ -1325,7 +1271,7 @@ limitations under the License.
test('_getChangeURLAndSend', () => {
element._projectLookup = {1: 'test'};
- const sendStub = sandbox.stub(element, '_send')
+ const sendStub = sandbox.stub(element._restApiHelper, 'send')
.returns(Promise.resolve());
const req = {
@@ -1347,16 +1293,17 @@ limitations under the License.
const mockObject = {foo: 'bar', baz: 'foo'};
const serial = element.JSON_PREFIX + JSON.stringify(mockObject);
const mockResponse = {text: () => Promise.resolve(serial)};
- return element._readResponsePayload(mockResponse).then(payload => {
- assert.deepEqual(payload.parsed, mockObject);
- assert.equal(payload.raw, serial);
- });
+ return element._restApiHelper.readResponsePayload(mockResponse)
+ .then(payload => {
+ assert.deepEqual(payload.parsed, mockObject);
+ assert.equal(payload.raw, serial);
+ });
});
test('_parsePrefixedJSON', () => {
const obj = {x: 3, y: {z: 4}, w: 23};
const serial = element.JSON_PREFIX + JSON.stringify(obj);
- const result = element._parsePrefixedJSON(serial);
+ const result = element._restApiHelper.parsePrefixedJSON(serial);
assert.deepEqual(result, obj);
});
});
@@ -1378,7 +1325,7 @@ limitations under the License.
});
test('generateAccountHttpPassword', () => {
- const sendSpy = sandbox.spy(element, '_send');
+ const sendSpy = sandbox.spy(element._restApiHelper, 'send');
return element.generateAccountHttpPassword().then(() => {
assert.isTrue(sendSpy.calledOnce);
assert.deepEqual(sendSpy.lastCall.args[0].body, {generate: true});
@@ -1463,11 +1410,12 @@ limitations under the License.
});
test('getDashboard', () => {
- const fetchStub = sandbox.stub(element, '_fetchSharedCacheURL');
+ const fetchCacheURLStub = sandbox.stub(element._restApiHelper,
+ 'fetchCacheURL');
element.getDashboard('gerrit/project', 'default:main');
- assert.isTrue(fetchStub.calledOnce);
+ assert.isTrue(fetchCacheURLStub.calledOnce);
assert.equal(
- fetchStub.lastCall.args[0].url,
+ fetchCacheURLStub.lastCall.args[0].url,
'/projects/gerrit%2Fproject/dashboards/default%3Amain');
});
@@ -1535,7 +1483,7 @@ limitations under the License.
});
test('_fetch forwards request and logs', () => {
- const logStub = sandbox.stub(element, '_logCall');
+ const logStub = sandbox.stub(element._restApiHelper, '_logCall');
const response = {status: 404, text: sinon.stub()};
const url = 'my url';
const fetchOptions = {method: 'DELETE'};
@@ -1543,7 +1491,7 @@ limitations under the License.
const startTime = 123;
sandbox.stub(Date, 'now').returns(startTime);
const req = {url, fetchOptions};
- return element._fetch(req).then(() => {
+ return element._restApiHelper.fetch(req).then(() => {
assert.isTrue(logStub.calledOnce);
assert.isTrue(logStub.calledWith(req, startTime, response.status));
assert.isFalse(response.text.called);
@@ -1555,10 +1503,11 @@ limitations under the License.
const handler = sinon.stub();
element.addEventListener('rpc-log', handler);
- element._logCall({url: 'url'}, 100, 200);
+ element._restApiHelper._logCall({url: 'url'}, 100, 200);
assert.isFalse(handler.called);
- element._logCall({url: 'url', anonymizedUrl: 'not url'}, 100, 200);
+ element._restApiHelper
+ ._logCall({url: 'url', anonymizedUrl: 'not url'}, 100, 200);
flushAsynchronousOperations();
assert.isTrue(handler.calledOnce);
});
@@ -1567,7 +1516,7 @@ limitations under the License.
sandbox.stub(element, 'getFromProjectLookup')
.returns(Promise.resolve('test'));
const sendStub =
- sandbox.stub(element, '_send').returns(Promise.resolve());
+ sandbox.stub(element._restApiHelper, 'send').returns(Promise.resolve());
await element.saveChangeStarred(123, true);
assert.isTrue(sendStub.calledOnce);
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.js
new file mode 100644
index 0000000000..3908a001bb
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.js
@@ -0,0 +1,431 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function(window) {
+ 'use strict';
+
+ const JSON_PREFIX = ')]}\'';
+ const FAILED_TO_FETCH_ERROR = 'Failed to fetch';
+
+ /**
+ * Wrapper around Map for caching server responses. Site-based so that
+ * changes to CANONICAL_PATH will result in a different cache going into
+ * effect.
+ */
+ class SiteBasedCache {
+ constructor() {
+ // Container of per-canonical-path caches.
+ this._data = new Map();
+ if (window.INITIAL_DATA != undefined) {
+ // Put all data shipped with index.html into the cache. This makes it
+ // so that we spare more round trips to the server when the app loads
+ // initially.
+ Object
+ .entries(window.INITIAL_DATA)
+ .forEach(e => this._cache().set(e[0], e[1]));
+ }
+ }
+
+ // Returns the cache for the current canonical path.
+ _cache() {
+ if (!this._data.has(window.CANONICAL_PATH)) {
+ this._data.set(window.CANONICAL_PATH, new Map());
+ }
+ return this._data.get(window.CANONICAL_PATH);
+ }
+
+ has(key) {
+ return this._cache().has(key);
+ }
+
+ get(key) {
+ return this._cache().get(key);
+ }
+
+ set(key, value) {
+ this._cache().set(key, value);
+ }
+
+ delete(key) {
+ this._cache().delete(key);
+ }
+
+ invalidatePrefix(prefix) {
+ const newMap = new Map();
+ for (const [key, value] of this._cache().entries()) {
+ if (!key.startsWith(prefix)) {
+ newMap.set(key, value);
+ }
+ }
+ this._data.set(window.CANONICAL_PATH, newMap);
+ }
+ }
+
+ class FetchPromisesCache {
+ constructor() {
+ this._data = {};
+ }
+
+ has(key) {
+ return !!this._data[key];
+ }
+
+ get(key) {
+ return this._data[key];
+ }
+
+ set(key, value) {
+ this._data[key] = value;
+ }
+
+ invalidatePrefix(prefix) {
+ const newData = {};
+ Object.entries(this._data).forEach(([key, value]) => {
+ if (!key.startsWith(prefix)) {
+ newData[key] = value;
+ }
+ });
+ this._data = newData;
+ }
+ }
+
+ class GrRestApiHelper {
+ /**
+ * @param {SiteBasedCache} cache
+ * @param {object} auth
+ * @param {FetchPromisesCache} fetchPromisesCache
+ * @param {object} credentialCheck
+ * @param {object} restApiInterface
+ */
+ constructor(cache, auth, fetchPromisesCache, credentialCheck,
+ restApiInterface) {
+ this._cache = cache;// TODO: make it public
+ this._auth = auth;
+ this._fetchPromisesCache = fetchPromisesCache;
+ this._credentialCheck = credentialCheck;
+ this._restApiInterface = restApiInterface;
+ }
+
+ /**
+ * Wraps calls to the underlying authenticated fetch function (_auth.fetch)
+ * with timing and logging.
+ *
+ * @param {Gerrit.FetchRequest} req
+ */
+ fetch(req) {
+ const start = Date.now();
+ const xhr = this._auth.fetch(req.url, req.fetchOptions);
+
+ // Log the call after it completes.
+ xhr.then(res => this._logCall(req, start, res ? res.status : null));
+
+ // Return the XHR directly (without the log).
+ return xhr;
+ }
+
+ /**
+ * Log information about a REST call. Because the elapsed time is determined
+ * by this method, it should be called immediately after the request
+ * finishes.
+ *
+ * @param {Gerrit.FetchRequest} req
+ * @param {number} startTime the time that the request was started.
+ * @param {number} status the HTTP status of the response. The status value
+ * is used here rather than the response object so there is no way this
+ * method can read the body stream.
+ */
+ _logCall(req, startTime, status) {
+ const method = (req.fetchOptions && req.fetchOptions.method) ?
+ req.fetchOptions.method : 'GET';
+ const endTime = Date.now();
+ const elapsed = (endTime - startTime);
+ const startAt = new Date(startTime);
+ const endAt = new Date(endTime);
+ console.log([
+ 'HTTP',
+ status,
+ method,
+ elapsed + 'ms',
+ req.anonymizedUrl || req.url,
+ `(${startAt.toISOString()}, ${endAt.toISOString()})`,
+ ].join(' '));
+ if (req.anonymizedUrl) {
+ this.fire('rpc-log',
+ {status, method, elapsed, anonymizedUrl: req.anonymizedUrl});
+ }
+ }
+
+ /**
+ * Fetch JSON from url provided.
+ * Returns a Promise that resolves to a native Response.
+ * Doesn't do error checking. Supports cancel condition. Performs auth.
+ * Validates auth expiry errors.
+ *
+ * @param {Gerrit.FetchJSONRequest} req
+ */
+ fetchRawJSON(req) {
+ const urlWithParams = this.urlWithParams(req.url, req.params);
+ const fetchReq = {
+ url: urlWithParams,
+ fetchOptions: req.fetchOptions,
+ anonymizedUrl: req.reportUrlAsIs ? urlWithParams : req.anonymizedUrl,
+ };
+ return this.fetch(fetchReq).then(res => {
+ if (req.cancelCondition && req.cancelCondition()) {
+ res.body.cancel();
+ return;
+ }
+ return res;
+ }).catch(err => {
+ const isLoggedIn = !!this._cache.get('/accounts/self/detail');
+ if (isLoggedIn && err && err.message === FAILED_TO_FETCH_ERROR) {
+ this.checkCredentials();
+ } else {
+ if (req.errFn) {
+ req.errFn.call(undefined, null, err);
+ } else {
+ this.fire('network-error', {error: err});
+ }
+ }
+ throw err;
+ });
+ }
+
+ /**
+ * Fetch JSON from url provided.
+ * Returns a Promise that resolves to a parsed response.
+ * Same as {@link fetchRawJSON}, plus error handling.
+ *
+ * @param {Gerrit.FetchJSONRequest} req
+ */
+ fetchJSON(req) {
+ req = this.addAcceptJsonHeader(req);
+ return this.fetchRawJSON(req).then(response => {
+ if (!response) {
+ return;
+ }
+ if (!response.ok) {
+ if (req.errFn) {
+ req.errFn.call(null, response);
+ return;
+ }
+ this.fire('server-error', {request: req, response});
+ return;
+ }
+ return response && this.getResponseObject(response);
+ });
+ }
+
+ /**
+ * @param {string} url
+ * @param {?Object|string=} opt_params URL params, key-value hash.
+ * @return {string}
+ */
+ urlWithParams(url, opt_params) {
+ if (!opt_params) { return this.getBaseUrl() + url; }
+
+ const params = [];
+ for (const p in opt_params) {
+ if (!opt_params.hasOwnProperty(p)) { continue; }
+ if (opt_params[p] == null) {
+ params.push(encodeURIComponent(p));
+ continue;
+ }
+ for (const value of [].concat(opt_params[p])) {
+ params.push(`${encodeURIComponent(p)}=${encodeURIComponent(value)}`);
+ }
+ }
+ return this.getBaseUrl() + url + '?' + params.join('&');
+ }
+
+ /**
+ * @param {!Object} response
+ * @return {?}
+ */
+ getResponseObject(response) {
+ return this.readResponsePayload(response)
+ .then(payload => payload.parsed);
+ }
+
+ /**
+ * @param {!Object} response
+ * @return {!Object}
+ */
+ readResponsePayload(response) {
+ return response.text().then(text => {
+ let result;
+ try {
+ result = this.parsePrefixedJSON(text);
+ } catch (_) {
+ result = null;
+ }
+ return {parsed: result, raw: text};
+ });
+ }
+
+ /**
+ * @param {string} source
+ * @return {?}
+ */
+ parsePrefixedJSON(source) {
+ return JSON.parse(source.substring(JSON_PREFIX.length));
+ }
+
+ /**
+ * @param {Gerrit.FetchJSONRequest} req
+ * @return {Gerrit.FetchJSONRequest}
+ */
+ addAcceptJsonHeader(req) {
+ if (!req.fetchOptions) req.fetchOptions = {};
+ if (!req.fetchOptions.headers) req.fetchOptions.headers = new Headers();
+ if (!req.fetchOptions.headers.has('Accept')) {
+ req.fetchOptions.headers.append('Accept', 'application/json');
+ }
+ return req;
+ }
+
+ getBaseUrl() {
+ return this._restApiInterface.getBaseUrl();
+ }
+
+ fire(type, detail, options) {
+ return this._restApiInterface.fire(type, detail, options);
+ }
+
+ /**
+ * @param {Gerrit.FetchJSONRequest} req
+ */
+ fetchCacheURL(req) {
+ if (this._fetchPromisesCache.has(req.url)) {
+ return this._fetchPromisesCache.get(req.url);
+ }
+ // TODO(andybons): Periodic cache invalidation.
+ if (this._cache.has(req.url)) {
+ return Promise.resolve(this._cache.get(req.url));
+ }
+ this._fetchPromisesCache.set(req.url,
+ this.fetchJSON(req).then(response => {
+ if (response !== undefined) {
+ this._cache.set(req.url, response);
+ }
+ this._fetchPromisesCache.set(req.url, undefined);
+ return response;
+ }).catch(err => {
+ this._fetchPromisesCache.set(req.url, undefined);
+ throw err;
+ })
+ );
+ return this._fetchPromisesCache.get(req.url);
+ }
+
+ /**
+ * Send an XHR.
+ *
+ * @param {Gerrit.SendRequest} req
+ * @return {Promise}
+ */
+ send(req) {
+ const options = {method: req.method};
+ if (req.body) {
+ options.headers = new Headers();
+ options.headers.set(
+ 'Content-Type', req.contentType || 'application/json');
+ options.body = typeof req.body === 'string' ?
+ req.body : JSON.stringify(req.body);
+ }
+ if (req.headers) {
+ if (!options.headers) { options.headers = new Headers(); }
+ for (const header in req.headers) {
+ if (!req.headers.hasOwnProperty(header)) { continue; }
+ options.headers.set(header, req.headers[header]);
+ }
+ }
+ const url = req.url.startsWith('http') ?
+ req.url : this.getBaseUrl() + req.url;
+ const fetchReq = {
+ url,
+ fetchOptions: options,
+ anonymizedUrl: req.reportUrlAsIs ? url : req.anonymizedUrl,
+ };
+ const xhr = this.fetch(fetchReq).then(response => {
+ if (!response.ok) {
+ if (req.errFn) {
+ return req.errFn.call(undefined, response);
+ }
+ this.fire('server-error', {request: fetchReq, response});
+ }
+ return response;
+ }).catch(err => {
+ this.fire('network-error', {error: err});
+ if (req.errFn) {
+ return req.errFn.call(undefined, null, err);
+ } else {
+ throw err;
+ }
+ });
+
+ if (req.parseResponse) {
+ return xhr.then(res => this.getResponseObject(res));
+ }
+
+ return xhr;
+ }
+
+ checkCredentials() {
+ if (this._credentialCheck.checking) {
+ return;
+ }
+ this._credentialCheck.checking = true;
+ let req = {url: '/accounts/self/detail', reportUrlAsIs: true};
+ req = this.addAcceptJsonHeader(req);
+ // Skip the REST response cache.
+ return this.fetchRawJSON(req).then(res => {
+ if (!res) { return; }
+ if (res.status === 403) {
+ this.fire('auth-error');
+ this._cache.delete('/accounts/self/detail');
+ } else if (res.ok) {
+ return this.getResponseObject(res);
+ }
+ }).then(res => {
+ this._credentialCheck.checking = false;
+ if (res) {
+ this._cache.set('/accounts/self/detail', res);
+ }
+ return res;
+ }).catch(err => {
+ this._credentialCheck.checking = false;
+ if (err && err.message === FAILED_TO_FETCH_ERROR) {
+ this.fire('auth-error');
+ this._cache.delete('/accounts/self/detail');
+ }
+ });
+ }
+
+ /**
+ * @param {string} prefix
+ */
+ invalidateFetchPromisesPrefix(prefix) {
+ this._fetchPromisesCache.invalidatePrefix(prefix);
+ this._cache.invalidatePrefix(prefix);
+ }
+ }
+
+ window.SiteBasedCache = SiteBasedCache;
+ window.FetchPromisesCache = FetchPromisesCache;
+ window.GrRestApiHelper = GrRestApiHelper;
+})(window);
+
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html
new file mode 100644
index 0000000000..4eaf1bcdcd
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html
@@ -0,0 +1,177 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-rest-api-helper</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../../test/common-test-setup.html"/>
+<script src="../../../../scripts/util.js"></script>
+<script src="../gr-auth.js"></script>
+<script src="gr-rest-api-helper.js"></script>
+
+<script>void(0);</script>
+
+<script>
+ suite('gr-rest-api-helper tests', () => {
+ let helper;
+ let sandbox;
+ let cache;
+ let fetchPromisesCache;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ cache = new SiteBasedCache();
+ fetchPromisesCache = new FetchPromisesCache();
+ const credentialCheck = {checking: false};
+
+ window.CANONICAL_PATH = 'testhelper';
+
+ const mockRestApiInterface = {
+ getBaseUrl: sinon.stub().returns(window.CANONICAL_PATH),
+ fire: sinon.stub(),
+ };
+
+ const testJSON = ')]}\'\n{"hello": "bonjour"}';
+ sandbox.stub(window, 'fetch').returns(Promise.resolve({
+ ok: true,
+ text() {
+ return Promise.resolve(testJSON);
+ },
+ }));
+
+ helper = new GrRestApiHelper(cache, Gerrit.Auth, fetchPromisesCache,
+ credentialCheck, mockRestApiInterface);
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ suite('fetchJSON()', () => {
+ test('Sets header to accept application/json', () => {
+ const authFetchStub = sandbox.stub(helper._auth, 'fetch')
+ .returns(Promise.resolve());
+ helper.fetchJSON({url: '/dummy/url'});
+ assert.isTrue(authFetchStub.called);
+ assert.equal(authFetchStub.lastCall.args[1].headers.get('Accept'),
+ 'application/json');
+ });
+
+ test('Use header option accept when provided', () => {
+ const authFetchStub = sandbox.stub(helper._auth, 'fetch')
+ .returns(Promise.resolve());
+ const headers = new Headers();
+ headers.append('Accept', '*/*');
+ const fetchOptions = {headers};
+ helper.fetchJSON({url: '/dummy/url', fetchOptions});
+ assert.isTrue(authFetchStub.called);
+ assert.equal(authFetchStub.lastCall.args[1].headers.get('Accept'),
+ '*/*');
+ });
+ });
+
+ test('JSON prefix is properly removed', done => {
+ helper.fetchJSON({url: '/dummy/url'}).then(obj => {
+ assert.deepEqual(obj, {hello: 'bonjour'});
+ done();
+ });
+ });
+
+ test('cached results', done => {
+ let n = 0;
+ sandbox.stub(helper, 'fetchJSON', () => {
+ return Promise.resolve(++n);
+ });
+ const promises = [];
+ promises.push(helper.fetchCacheURL('/foo'));
+ promises.push(helper.fetchCacheURL('/foo'));
+ promises.push(helper.fetchCacheURL('/foo'));
+
+ Promise.all(promises).then(results => {
+ assert.deepEqual(results, [1, 1, 1]);
+ helper.fetchCacheURL('/foo').then(foo => {
+ assert.equal(foo, 1);
+ done();
+ });
+ });
+ });
+
+ test('cached promise', done => {
+ const promise = Promise.reject(new Error('foo'));
+ cache.set('/foo', promise);
+ helper.fetchCacheURL({url: '/foo'}).catch(p => {
+ assert.equal(p.message, 'foo');
+ done();
+ });
+ });
+
+ test('cache invalidation', () => {
+ cache.set('/foo/bar', 1);
+ cache.set('/bar', 2);
+ fetchPromisesCache.set('/foo/bar', 3);
+ fetchPromisesCache.set('/bar', 4);
+ helper.invalidateFetchPromisesPrefix('/foo/');
+ assert.isFalse(cache.has('/foo/bar'));
+ assert.isTrue(cache.has('/bar'));
+ assert.isUndefined(fetchPromisesCache.get('/foo/bar'));
+ assert.strictEqual(4, fetchPromisesCache.get('/bar'));
+ });
+
+ test('params are properly encoded', () => {
+ let url = helper.urlWithParams('/path/', {
+ sp: 'hola',
+ gr: 'guten tag',
+ noval: null,
+ });
+ assert.equal(url,
+ window.CANONICAL_PATH + '/path/?sp=hola&gr=guten%20tag&noval');
+
+ url = helper.urlWithParams('/path/', {
+ sp: 'hola',
+ en: ['hey', 'hi'],
+ });
+ assert.equal(url, window.CANONICAL_PATH + '/path/?sp=hola&en=hey&en=hi');
+
+ // Order must be maintained with array params.
+ url = helper.urlWithParams('/path/', {
+ l: ['c', 'b', 'a'],
+ });
+ assert.equal(url, window.CANONICAL_PATH + '/path/?l=c&l=b&l=a');
+ });
+
+ test('request callbacks can be canceled', done => {
+ let cancelCalled = false;
+ window.fetch.returns(Promise.resolve({
+ body: {
+ cancel() { cancelCalled = true; },
+ },
+ }));
+ const cancelCondition = () => { return true; };
+ helper.fetchJSON({url: '/dummy/url', cancelCondition}).then(
+ obj => {
+ assert.isUndefined(obj);
+ assert.isTrue(cancelCalled);
+ done();
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
index 8ddb255f90..d883ef6f5a 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.js
@@ -21,7 +21,6 @@
if (window.GrReviewerUpdatesParser) { return; }
function GrReviewerUpdatesParser(change) {
- // TODO (viktard): Polyfill Object.assign for IE.
this.result = Object.assign({}, change);
this._lastState = {};
}
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
index 202c52a246..fdf79afcbc 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-reviewer-updates-parser</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<script src="../../../scripts/util.js"></script>
<script src="gr-reviewer-updates-parser.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
index 05c2ceee5b..015d71e7ad 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../test/common-test-setup.html"/>
<dom-module id="mock-diff-response">
<template></template>
@@ -153,7 +153,7 @@ limitations under the License.
Polymer({
is: 'mock-diff-response',
- _legacyUndefinedCheck: true,
+
properties: {
diffResponse: {
type: Object,
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select.html b/polygerrit-ui/app/elements/shared/gr-select/gr-select.html
index e73d41cf9a..f1ef86ac4e 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.html
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.html
@@ -15,7 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
+
<dom-module id="gr-select">
<slot></slot>
<script src="gr-select.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
index 85e1a616a1..05267bac75 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
@@ -19,7 +19,7 @@
Polymer({
is: 'gr-select',
- _legacyUndefinedCheck: true,
+
properties: {
bindValue: {
type: String,
@@ -28,6 +28,10 @@
},
},
+ behaviors: [
+ Gerrit.FireBehavior,
+ ],
+
listeners: {
'change': '_valueChanged',
'dom-change': '_updateValue',
@@ -55,9 +59,15 @@
this.bindValue = this.nativeSelect.value;
},
+ focus() {
+ this.nativeSelect.focus();
+ },
+
ready() {
// If not set via the property, set bind-value to the element value.
- if (!this.bindValue) { this.bindValue = this.nativeSelect.value; }
+ if (this.bindValue == undefined && this.nativeSelect.options.length > 0) {
+ this.bindValue = this.nativeSelect.value;
+ }
},
});
})();
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html b/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
index 1748ec062a..b3abe5fc9b 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-select</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-select.html">
@@ -38,6 +40,15 @@ limitations under the License.
</template>
</test-fixture>
+<test-fixture id="noOptions">
+ <template>
+ <gr-select>
+ <select>
+ </select>
+ </gr-select>
+ </template>
+</test-fixture>
+
<script>
suite('gr-select tests', () => {
let element;
@@ -46,6 +57,10 @@ limitations under the License.
element = fixture('basic');
});
+ test('bindValue must be set to the first option value', () => {
+ assert.equal(element.bindValue, '1');
+ });
+
test('value of 0 should still trigger value updates', () => {
element.bindValue = 0;
assert.equal(element.nativeSelect.value, 0);
@@ -88,4 +103,16 @@ limitations under the License.
assert.isTrue(changeStub.called);
});
});
+
+ suite('gr-select no options tests', () => {
+ let element;
+
+ setup(() => {
+ element = fixture('noOptions');
+ });
+
+ test('bindValue must not be changed', () => {
+ assert.isUndefined(element.bindValue);
+ });
+ });
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.html b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.html
index fe6ed88b4e..15e282f457 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.html
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-copy-clipboard/gr-copy-clipboard.html">
@@ -23,25 +23,30 @@ limitations under the License.
<template>
<style include="shared-styles">
.commandContainer {
- margin-bottom: .75em;
+ margin-bottom: var(--spacing-m);
}
.commandContainer {
background-color: var(--shell-command-background-color);
- padding: .5em .5em .5em 2.5em;
+ /* Should be spacing-m larger than the :before width. */
+ padding: var(--spacing-m) var(--spacing-m) var(--spacing-m) calc(3*var(--spacing-m) + 0.5em);
position: relative;
width: 100%;
}
.commandContainer:before {
- background: var(--shell-command-decoration-background-color);
- bottom: 0;
- box-sizing: border-box;
content: '$';
- display: block;
- left: 0;
- padding: .8em;
position: absolute;
+ display: block;
+ box-sizing: border-box;
+ background: var(--shell-command-decoration-background-color);
top: 0;
- width: 2em;
+ bottom: 0;
+ left: 0;
+ /* Should be spacing-m smaller than the .commandContainer padding-left. */
+ width: calc(2*var(--spacing-m) + 0.5em);
+ /* Should vertically match the padding of .commandContainer. */
+ padding: var(--spacing-m);
+ /* Should roughly match the height of .commandContainer without padding. */
+ line-height: 26px;
}
.commandContainer gr-copy-clipboard {
--text-container-style: {
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
index 901b8ce2c0..2c546cc250 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-shell-command',
- _legacyUndefinedCheck: true,
properties: {
command: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html
index a49f76fc1d..3f2f8bab4b 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-shell-command</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-shell-command.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.html b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.html
index 6fc2f3f411..7215b26939 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.html
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.html
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="gr-storage">
<script src="gr-storage.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
index 83cd06cb81..f6ade6e4f1 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
@@ -30,7 +30,6 @@
Polymer({
is: 'gr-storage',
- _legacyUndefinedCheck: true,
properties: {
_lastCleanup: Number,
@@ -76,14 +75,6 @@
this._storage.removeItem(this._getEditableContentKey(key));
},
- getPreferences() {
- return this._getObject('localPrefs');
- },
-
- savePreferences(localPrefs) {
- this._setObject('localPrefs', localPrefs || null);
- },
-
_getDraftKey(location) {
const range = location.range ?
`${location.range.start_line}-${location.range.start_character}` +
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
index 6b89af2654..0482584fa3 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-storage</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-storage.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
index 10c91112ac..6803eb93b1 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
@@ -14,25 +14,36 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.html">
<link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
-<link rel="import" href="../../../bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="/bower_components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="/bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<dom-module id="gr-textarea">
<template>
<style include="shared-styles">
:host {
- display: block;
+ display: flex;
position: relative;
}
:host(.monospace) {
font-family: var(--monospace-font-family);
+ font-size: var(--font-size-mono);
+ line-height: var(--line-height-mono);
+ font-weight: var(--font-weight-normal);
+ }
+ :host(.code) {
+ font-family: var(--monospace-font-family);
+ font-size: var(--font-size-code);
+ line-height: var(--line-height-code);
+ font-weight: var(--font-weight-normal);
}
#emojiSuggestions {
font-family: var(--font-family);
@@ -54,6 +65,9 @@ limitations under the License.
iron-autogrow-textarea {
padding: 2px;
position: relative;
+
+ /** This is needed for firefox */
+ --iron-autogrow-textarea_-_white-space: pre-wrap;
}
#textarea.noBorder {
border: none;
@@ -92,6 +106,7 @@ limitations under the License.
max-rows="[[maxRows]]"
value="{{text}}"
on-bind-value-changed="_onValueChanged"></iron-autogrow-textarea>
+ <gr-reporting id="reporting"></gr-reporting>
</template>
<script src="gr-textarea.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
index 7929fbec6d..4c4b0389ae 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
@@ -20,41 +20,40 @@
const MAX_ITEMS_DROPDOWN = 10;
const ALL_SUGGESTIONS = [
+ {value: '😊', match: 'smile :)'},
+ {value: '👍', match: 'thumbs up'},
+ {value: '😄', match: 'laugh :D'},
+ {value: '🎉', match: 'party'},
+ {value: '😞', match: 'sad :('},
+ {value: '😂', match: 'tears :\')'},
+ {value: '🙏', match: 'pray'},
+ {value: '😐', match: 'neutral :|'},
+ {value: '😮', match: 'shock :O'},
+ {value: '👎', match: 'thumbs down'},
+ {value: '😎', match: 'cool |;)'},
+ {value: '😕', match: 'confused'},
+ {value: '👌', match: 'ok'},
+ {value: '🔥', match: 'fire'},
+ {value: '👊', match: 'fistbump'},
{value: '💯', match: '100'},
{value: '💔', match: 'broken heart'},
{value: '🍺', match: 'beer'},
{value: '✔', match: 'check'},
- {value: '😎', match: 'cool'},
- {value: '😕', match: 'confused'},
- {value: '😭', match: 'crying'},
- {value: '🔥', match: 'fire'},
- {value: '👊', match: 'fistbump'},
+ {value: '😋', match: 'tongue'},
+ {value: '😭', match: 'crying :\'('},
{value: '🐨', match: 'koala'},
- {value: '😄', match: 'laugh'},
{value: '🤓', match: 'glasses'},
{value: '😆', match: 'grin'},
- {value: '😐', match: 'neutral'},
- {value: '👌', match: 'ok'},
- {value: '🎉', match: 'party'},
{value: '💩', match: 'poop'},
- {value: '🙏', match: 'pray'},
- {value: '😞', match: 'sad'},
- {value: '😮', match: 'shock'},
- {value: '😊', match: 'smile'},
{value: '😢', match: 'tear'},
- {value: '😂', match: 'tears'},
- {value: '😋', match: 'tongue'},
- {value: '👍', match: 'thumbs up'},
- {value: '👎', match: 'thumbs down'},
{value: '😒', match: 'unamused'},
- {value: '😉', match: 'wink'},
+ {value: '😉', match: 'wink ;)'},
{value: '🍷', match: 'wine'},
- {value: '😜', match: 'winking tongue'},
+ {value: '😜', match: 'winking tongue ;)'},
];
Polymer({
is: 'gr-textarea',
- _legacyUndefinedCheck: true,
/**
* @event bind-value-changed
@@ -75,15 +74,21 @@
type: Boolean,
value: false,
},
+ /** Text input should be rendered in monspace font. */
monospace: {
type: Boolean,
value: false,
},
+ /** Text input should be rendered in code font, which is smaller than the
+ standard monospace font. */
+ code: {
+ type: Boolean,
+ value: false,
+ },
/** @type(?number) */
_colonIndex: Number,
_currentSearchString: {
type: String,
- value: '',
observer: '_determineSuggestions',
},
_hideAutocomplete: {
@@ -101,6 +106,7 @@
},
behaviors: [
+ Gerrit.FireBehavior,
Gerrit.KeyboardShortcutBehavior,
],
@@ -113,10 +119,12 @@
},
ready() {
- this._resetEmojiDropdown();
if (this.monospace) {
this.classList.add('monospace');
}
+ if (this.code) {
+ this.classList.add('code');
+ }
if (this.hideBorder) {
this.$.textarea.classList.add('noBorder');
}
@@ -153,6 +161,7 @@
e.stopPropagation();
this.$.emojiSuggestions.cursorUp();
this.$.textarea.textarea.focus();
+ this.disableEnterKeyForSelectingEmoji = false;
},
_handleDownKey(e) {
@@ -161,24 +170,34 @@
e.stopPropagation();
this.$.emojiSuggestions.cursorDown();
this.$.textarea.textarea.focus();
+ this.disableEnterKeyForSelectingEmoji = false;
},
_handleEnterByKey(e) {
- if (this._hideAutocomplete) { return; }
+ if (this._hideAutocomplete || this.disableEnterKeyForSelectingEmoji) {
+ return;
+ }
e.preventDefault();
e.stopPropagation();
- this.text = this._getText(this.$.emojiSuggestions.getCurrentText());
- this._resetEmojiDropdown();
+ this._setEmoji(this.$.emojiSuggestions.getCurrentText());
},
_handleEmojiSelect(e) {
- this.text = this._getText(e.detail.selected.dataset.value);
+ this._setEmoji(e.detail.selected.dataset.value);
+ },
+
+ _setEmoji(text) {
+ const colonIndex = this._colonIndex;
+ this.text = this._getText(text);
+ this.$.textarea.selectionStart = colonIndex + 1;
+ this.$.textarea.selectionEnd = colonIndex + 1;
+ this.$.reporting.reportInteraction('select-emoji');
this._resetEmojiDropdown();
},
_getText(value) {
return this.text.substr(0, this._colonIndex || 0) +
- value + this.text.substr(this.$.textarea.selectionStart) + ' ';
+ value + this.text.substr(this.$.textarea.selectionStart);
},
/**
* Uses a hidden element with the same width and styling of the textarea and
@@ -218,41 +237,45 @@
// If cursor is not in textarea (just opened with colon as last char),
// Don't do anything.
if (!e.currentTarget.focused) { return; }
- const newChar = e.detail.value[this.$.textarea.selectionStart - 1];
- // When a colon is detected, set a colon index, but don't do anything else
- // yet.
- if (newChar === ':') {
- this._colonIndex = this.$.textarea.selectionStart - 1;
- // If the colon index exists, continue to determine what needs to be done
- // with the dropdown. It may be open or closed at this point.
- } else if (this._colonIndex !== null) {
- // The search string is a substring of the textarea's value from (1
- // position after) the colon index to the cursor position.
- this._currentSearchString = e.detail.value.substr(this._colonIndex + 1,
- this.$.textarea.selectionStart);
- // Under the following conditions, close and reset the dropdown:
- // - The cursor is no longer at the end of the current search string
- // - The search string is an space or new line
- // - The colon has been removed
- // - There are no suggestions that match the search string
- if (this.$.textarea.selectionStart !==
- this._currentSearchString.length + this._colonIndex + 1 ||
- this._currentSearchString === ' ' ||
- this._currentSearchString === '\n' ||
- !(e.detail.value[this._colonIndex] === ':') ||
- !this._suggestions.length) {
- this._resetEmojiDropdown();
- // Otherwise open the dropdown and set the position to be just below the
- // cursor.
- } else if (this.$.emojiSuggestions.isHidden) {
- this._updateCaratPosition();
+ const charAtCursor = e.detail && e.detail.value ?
+ e.detail.value[this.$.textarea.selectionStart - 1] : '';
+ if (charAtCursor !== ':' && this._colonIndex == null) { return; }
+
+ // When a colon is detected, set a colon index. We are interested only on
+ // colons after space or in beginning of textarea
+ if (charAtCursor === ':') {
+ if (this.$.textarea.selectionStart < 2 ||
+ e.detail.value[this.$.textarea.selectionStart - 2] === ' ') {
+ this._colonIndex = this.$.textarea.selectionStart - 1;
}
- this.$.textarea.textarea.focus();
}
+
+ this._currentSearchString = e.detail.value.substr(this._colonIndex + 1,
+ this.$.textarea.selectionStart - this._colonIndex - 1);
+ // Under the following conditions, close and reset the dropdown:
+ // - The cursor is no longer at the end of the current search string
+ // - The search string is an space or new line
+ // - The colon has been removed
+ // - There are no suggestions that match the search string
+ if (this.$.textarea.selectionStart !==
+ this._currentSearchString.length + this._colonIndex + 1 ||
+ this._currentSearchString === ' ' ||
+ this._currentSearchString === '\n' ||
+ !(e.detail.value[this._colonIndex] === ':') ||
+ !this._suggestions.length) {
+ this._resetEmojiDropdown();
+ // Otherwise open the dropdown and set the position to be just below the
+ // cursor.
+ } else if (this.$.emojiSuggestions.isHidden) {
+ this._updateCaratPosition();
+ }
+ this.$.textarea.textarea.focus();
},
+
_openEmojiDropdown() {
this.$.emojiSuggestions.open();
+ this.$.reporting.reportInteraction('open-emoji-dropdown');
},
_formatSuggestions(matchedSuggestions) {
@@ -268,11 +291,14 @@
_determineSuggestions(emojiText) {
if (!emojiText.length) {
this._formatSuggestions(ALL_SUGGESTIONS);
+ this.disableEnterKeyForSelectingEmoji = true;
+ } else {
+ const matches = ALL_SUGGESTIONS.filter(suggestion => {
+ return suggestion.match.includes(emojiText);
+ }).slice(0, MAX_ITEMS_DROPDOWN);
+ this._formatSuggestions(matches);
+ this.disableEnterKeyForSelectingEmoji = false;
}
- const matches = ALL_SUGGESTIONS.filter(suggestion => {
- return suggestion.match.includes(emojiText);
- }).splice(0, MAX_ITEMS_DROPDOWN);
- this._formatSuggestions(matches);
},
_resetEmojiDropdown() {
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
index f010fb3234..b884ecd0da 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-textarea</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-textarea.html">
@@ -31,6 +33,18 @@ limitations under the License.
</template>
</test-fixture>
+<test-fixture id="monospace">
+ <template>
+ <gr-textarea monospace="true"></gr-textarea>
+ </template>
+</test-fixture>
+
+<test-fixture id="hideBorder">
+ <template>
+ <gr-textarea hide-border="true"></gr-textarea>
+ </template>
+</test-fixture>
+
<script>
suite('gr-textarea tests', () => {
let element;
@@ -39,6 +53,7 @@ limitations under the License.
setup(() => {
sandbox = sinon.sandbox.create();
element = fixture('basic');
+ sandbox.stub(element.$.reporting, 'reportInteraction');
});
teardown(() => {
@@ -47,16 +62,10 @@ limitations under the License.
test('monospace is set properly', () => {
assert.isFalse(element.classList.contains('monospace'));
- element.monospace = true;
- element.ready();
- assert.isTrue(element.classList.contains('monospace'));
});
test('hideBorder is set properly', () => {
assert.isFalse(element.$.textarea.classList.contains('noBorder'));
- element.hideBorder = true;
- element.ready();
- assert.isTrue(element.$.textarea.classList.contains('noBorder'));
});
test('emoji selector is not open with the textarea lacks focus', () => {
@@ -82,6 +91,49 @@ limitations under the License.
element.$.textarea.selectionStart = 1;
element.$.textarea.selectionEnd = 1;
element.text = ':';
+ flushAsynchronousOperations();
+ assert.isFalse(element.$.emojiSuggestions.isHidden);
+ assert.equal(element._colonIndex, 0);
+ assert.isFalse(element._hideAutocomplete);
+ assert.equal(element._currentSearchString, '');
+ });
+
+ test('emoji selector opens when a colon is typed after space',
+ () => {
+ MockInteractions.focus(element.$.textarea);
+ // Needed for Safari tests. selectionStart is not updated when text is
+ // updated.
+ element.$.textarea.selectionStart = 2;
+ element.$.textarea.selectionEnd = 2;
+ element.text = ' :';
+ flushAsynchronousOperations();
+ assert.isFalse(element.$.emojiSuggestions.isHidden);
+ assert.equal(element._colonIndex, 1);
+ assert.isFalse(element._hideAutocomplete);
+ assert.equal(element._currentSearchString, '');
+ });
+
+ test('emoji selector doesn\`t open when a colon is typed after character',
+ () => {
+ MockInteractions.focus(element.$.textarea);
+ // Needed for Safari tests. selectionStart is not updated when text is
+ // updated.
+ element.$.textarea.selectionStart = 5;
+ element.$.textarea.selectionEnd = 5;
+ element.text = 'test:';
+ flushAsynchronousOperations();
+ assert.isTrue(element.$.emojiSuggestions.isHidden);
+ assert.isTrue(element._hideAutocomplete);
+ });
+
+ test('emoji selector opens when a colon is typed and some substring',
+ () => {
+ MockInteractions.focus(element.$.textarea);
+ // Needed for Safari tests. selectionStart is not updated when text is
+ // updated.
+ element.$.textarea.selectionStart = 1;
+ element.$.textarea.selectionEnd = 1;
+ element.text = ':';
element.$.textarea.selectionStart = 2;
element.$.textarea.selectionEnd = 2;
element.text = ':t';
@@ -92,6 +144,30 @@ limitations under the License.
assert.equal(element._currentSearchString, 't');
});
+ test('emoji selector opens when a colon is typed in middle of text',
+ () => {
+ MockInteractions.focus(element.$.textarea);
+ // Needed for Safari tests. selectionStart is not updated when text is
+ // updated.
+ element.$.textarea.selectionStart = 1;
+ element.$.textarea.selectionEnd = 1;
+ // Since selectionStart is on Chrome set always on end of text, we
+ // stub it to 1
+ const text = ': hello';
+ sandbox.stub(element.$, 'textarea', {
+ selectionStart: 1,
+ value: text,
+ textarea: {
+ focus: () => {},
+ },
+ });
+ element.text = text;
+ flushAsynchronousOperations();
+ assert.isFalse(element.$.emojiSuggestions.isHidden);
+ assert.equal(element._colonIndex, 0);
+ assert.isFalse(element._hideAutocomplete);
+ assert.equal(element._currentSearchString, '');
+ });
test('emoji selector closes when text changes before the colon', () => {
const resetStub = sandbox.stub(element, '_resetEmojiDropdown');
MockInteractions.focus(element.$.textarea);
@@ -131,8 +207,10 @@ limitations under the License.
element._determineSuggestions(emojiText);
assert.isTrue(formatSpy.called);
assert.isTrue(formatSpy.lastCall.calledWithExactly(
- [{dataValue: '😢', value: '😢', match: 'tear', text: '😢 tear'},
- {dataValue: '😂', value: '😂', match: 'tears', text: '😂 tears'}]));
+ [{dataValue: '😂', value: '😂', match: 'tears :\')',
+ text: '😂 tears :\')'},
+ {dataValue: '😢', value: '😢', match: 'tear', text: '😢 tear'},
+ ]));
});
test('_formatSuggestions', () => {
@@ -153,7 +231,7 @@ limitations under the License.
const selectedItem = {dataset: {value: '😂'}};
const event = {detail: {selected: selectedItem}};
element._handleEmojiSelect(event);
- assert.equal(element.text, 'test test 😂 ');
+ assert.equal(element.text, 'test test 😂');
});
test('_updateCaratPosition', () => {
@@ -228,9 +306,70 @@ limitations under the License.
MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 13);
assert.isTrue(enterSpy.called);
flushAsynchronousOperations();
- // A space is automatically added at the end.
- assert.equal(element.text, '💯 ');
+ assert.equal(element.text, '💯');
});
+
+ test('enter key - ignored on just colon without more information', () => {
+ const enterSpy = sandbox.spy(element.$.emojiSuggestions,
+ 'getCursorTarget');
+ MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 13);
+ assert.isFalse(enterSpy.called);
+ MockInteractions.focus(element.$.textarea);
+ element.$.textarea.selectionStart = 1;
+ element.$.textarea.selectionEnd = 1;
+ element.text = ':';
+ flushAsynchronousOperations();
+ MockInteractions.pressAndReleaseKeyOn(element.$.textarea, 13);
+ assert.isFalse(enterSpy.called);
+ });
+ });
+ });
+
+ suite('gr-textarea monospace', () => {
+ // gr-textarea set monospace class in the ready() method.
+ // In Polymer2, ready() is called from the fixture(...) method,
+ // If ready() is called again later, some nested elements doesn't
+ // handle it correctly. A separate test-fixture is used to set
+ // properties before ready() is called.
+
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('monospace');
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('monospace is set properly', () => {
+ assert.isTrue(element.classList.contains('monospace'));
+ });
+ });
+
+ suite('gr-textarea hideBorder', () => {
+ // gr-textarea set noBorder class in the ready() method.
+ // In Polymer2, ready() is called from the fixture(...) method,
+ // If ready() is called again later, some nested elements doesn't
+ // handle it correctly. A separate test-fixture is used to set
+ // properties before ready() is called.
+
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('hideBorder');
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('hideBorder is set properly', () => {
+ assert.isTrue(element.$.textarea.classList.contains('noBorder'));
});
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
index 65f1fda83c..b4fefe1e12 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
<dom-module id="gr-tooltip-content">
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
index b46cafb3b3..c5de8f4844 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-tooltip-content',
- _legacyUndefinedCheck: true,
properties: {
title: {
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
index 438d4360e1..f9350c670f 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-storage</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-tooltip-content.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html
index 9947d61502..75d9c4bb14 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
<dom-module id="gr-tooltip">
@@ -34,7 +34,7 @@ limitations under the License.
max-width: var(--tooltip-max-width);
}
:host .tooltip {
- padding: .5em .85em;
+ padding: var(--spacing-m) var(--spacing-l);
}
:host .arrowPositionBelow,
:host([position-below]) .arrowPositionAbove {
@@ -54,11 +54,11 @@ limitations under the License.
}
.arrowPositionAbove {
border-top: var(--gr-tooltip-arrow-size) solid var(--tooltip-background-color);
- bottom: -var(--gr-tooltip-arrow-size);
+ bottom: calc(-1 * var(--gr-tooltip-arrow-size));
}
.arrowPositionBelow {
border-bottom: var(--gr-tooltip-arrow-size) solid var(--tooltip-background-color);
- top: -var(--gr-tooltip-arrow-size);
+ top: calc(-1 * var(--gr-tooltip-arrow-size));
}
</style>
<div class="tooltip">
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
index 3e16beb6f2..fb87b55831 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
@@ -19,7 +19,6 @@
Polymer({
is: 'gr-tooltip',
- _legacyUndefinedCheck: true,
properties: {
text: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
index 3a472880e8..f59f6e106d 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
@@ -17,9 +17,11 @@ limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-storage</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-tooltip.html">
diff --git a/polygerrit-ui/app/elements/shared/revision-info/revision-info.html b/polygerrit-ui/app/elements/shared/revision-info/revision-info.html
index 0846728b86..48e488a47f 100644
--- a/polygerrit-ui/app/elements/shared/revision-info/revision-info.html
+++ b/polygerrit-ui/app/elements/shared/revision-info/revision-info.html
@@ -36,6 +36,9 @@ limitations under the License.
* @return {Number}
*/
RevisionInfo.prototype.getMaxParents = function() {
+ if (!this._change || !this._change.revisions) {
+ return 0;
+ }
return Object.values(this._change.revisions)
.reduce((acc, rev) => Math.max(rev.commit.parents.length, acc), 0);
};
@@ -48,6 +51,9 @@ limitations under the License.
*/
RevisionInfo.prototype.getParentCountMap = function() {
const result = {};
+ if (!this._change || !this._change.revisions) {
+ return {};
+ }
Object.values(this._change.revisions)
.forEach(rev => { result[rev._number] = rev.commit.parents.length; });
return result;
@@ -75,9 +81,7 @@ limitations under the License.
return rev.commit.parents[parentIndex].commit;
};
- if (!window.Gerrit) {
- window.Gerrit = {};
- }
+ window.Gerrit = window.Gerrit || {};
window.Gerrit.RevisionInfo = RevisionInfo;
})();
</script>
diff --git a/polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html b/polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html
index 433872db88..7e5810bc2c 100644
--- a/polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html
+++ b/polygerrit-ui/app/elements/shared/revision-info/revision-info_test.html
@@ -18,9 +18,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>revision-info</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="revision-info.html">
diff --git a/polygerrit-ui/app/elements/test/plugin.html b/polygerrit-ui/app/elements/test/plugin.html
index bd29b9065e..ecd90075af 100644
--- a/polygerrit-ui/app/elements/test/plugin.html
+++ b/polygerrit-ui/app/elements/test/plugin.html
@@ -1,18 +1,44 @@
<dom-module id="my-plugin">
<script>
- Gerrit.install(plugin =>
- plugin.registerStyleModule('app-theme', 'myplugin-app-theme')
- );
+ Gerrit.install(plugin => {
+ plugin.registerStyleModule('app-theme', 'myplugin-app-theme');
+ plugin.registerStyleModule('app-theme-light', 'myplugin-app-theme-light');
+ plugin.registerStyleModule('app-theme-dark', 'myplugin-app-theme-dark');
+ });
</script>
</dom-module>
<dom-module id="myplugin-app-theme">
- <style>
- html {
- --primary-text-color: #F00BAA;
- --header-background-color: #F01BAA;
- --header-title-content: "MyGerrit";
- --footer-background-color: #F02BAA;
- }
- </style>
+ <template>
+ <style>
+ html {
+ --primary-text-color: #F00BAA;
+ }
+ </style>
+ </template>
+</dom-module>
+
+<dom-module id="myplugin-app-theme-light">
+ <template>
+ <style>
+ html {
+ --header-background-color: #F01BAA;
+ --header-title-content: "MyGerrit";
+ --footer-background-color: #F02BAA;
+ }
+ </style>
+ </template>
+</dom-module>
+
+<dom-module id="myplugin-app-theme-dark">
+ <template>
+ <style>
+ html {
+ --primary-text-color: red;
+ --header-background-color: black;
+ --header-title-content: "MyGerrit Dark";
+ --footer-background-color: yellow;
+ }
+ </style>
+ </template>
</dom-module>
diff --git a/polygerrit-ui/app/embed/embed.html b/polygerrit-ui/app/embed/embed.html
index 1b2f20fb23..64e0137182 100644
--- a/polygerrit-ui/app/embed/embed.html
+++ b/polygerrit-ui/app/embed/embed.html
@@ -15,12 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
- // Needed for JSCompiler to understand it's global.
- // eslint-disable-next-line no-unused-vars, prefer-const
- let Gerrit = window.Gerrit || {};
- window.Gerrit = Gerrit;
+ window.Gerrit = window.Gerrit || {};
</script>
-<link rel="import" href="../bower_components/polymer/polymer.html">
+<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../elements/change/gr-change-view/gr-change-view.html">
<link rel="import" href="../elements/core/gr-search-bar/gr-search-bar.html">
<link rel="import" href="../elements/diff/gr-diff-view/gr-diff-view.html">
diff --git a/polygerrit-ui/app/embed/embed_test.html b/polygerrit-ui/app/embed/embed_test.html
index 7ca75c9f8c..1e3f5d7f44 100644
--- a/polygerrit-ui/app/embed/embed_test.html
+++ b/polygerrit-ui/app/embed/embed_test.html
@@ -18,10 +18,12 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>embed_test</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
-<link rel="import" href="../polygerrit_ui/elements/embed.html"/>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="embed.html"/>
<script>void(0);</script>
diff --git a/polygerrit-ui/app/embed/gr-diff.html b/polygerrit-ui/app/embed/gr-diff.html
index 6aa9370114..f5f74bde8c 100644
--- a/polygerrit-ui/app/embed/gr-diff.html
+++ b/polygerrit-ui/app/embed/gr-diff.html
@@ -15,11 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
- // Needed for JSCompiler to understand it's global.
- // eslint-disable-next-line no-unused-vars, prefer-const
- let Gerrit = window.Gerrit || {};
- window.Gerrit = Gerrit;
+ window.Gerrit = window.Gerrit || {};
</script>
-<link rel="import" href="../styles/themes/app-theme.html">
<link rel="import" href="../elements/diff/gr-diff/gr-diff.html">
<link rel="import" href="../elements/diff/gr-diff-cursor/gr-diff-cursor.html">
diff --git a/polygerrit-ui/app/embed/test.html b/polygerrit-ui/app/embed/test.html
index eed2fefbfe..955eaeed1a 100644
--- a/polygerrit-ui/app/embed/test.html
+++ b/polygerrit-ui/app/embed/test.html
@@ -19,8 +19,8 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>Embed Test Runner</title>
<meta charset="utf-8">
-<script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<script>
- WCT.loadSuites(['embed_test.html']);
+ WCT.loadSuites(['../embed/embed_test.html']);
</script>
diff --git a/polygerrit-ui/app/embed_test.sh b/polygerrit-ui/app/embed_test.sh
index d48279665a..0d8f58ff28 100755
--- a/polygerrit-ui/app/embed_test.sh
+++ b/polygerrit-ui/app/embed_test.sh
@@ -4,20 +4,19 @@ set -ex
t=$(mktemp -d || mktemp -d -t wct-XXXXXXXXXX)
components=$TEST_SRCDIR/gerrit/polygerrit-ui/app/test_components.zip
-code=$TEST_SRCDIR/gerrit/polygerrit-ui/app/polygerrit_embed_ui.zip
-index=$TEST_SRCDIR/gerrit/polygerrit-ui/app/embed/test.html
-tests=$TEST_SRCDIR/gerrit/polygerrit-ui/app/embed/*_test.html
+code=$TEST_SRCDIR/gerrit/polygerrit-ui/app/pg_code.zip
+echo $t
unzip -qd $t $components
unzip -qd $t $code
+# Purge test/ directory contents coming from pg_code.zip.
+rm -rf $t/test
mkdir -p $t/test
-cp $index $t/test/
-cp $tests $t/test/
+cp $TEST_SRCDIR/gerrit/polygerrit-ui/app/embed/test.html $t/test/
if [ "${WCT_HEADLESS_MODE:-0}" != "0" ]; then
CHROME_OPTIONS=[\'start-maximized\',\'headless\',\'disable-gpu\',\'no-sandbox\']
- # TODO(paladox): Fix Firefox support for headless mode
- FIREFOX_OPTIONS=[\'\']
+ FIREFOX_OPTIONS=[\'-headless\']
else
CHROME_OPTIONS=[\'start-maximized\']
FIREFOX_OPTIONS=[\'\']
@@ -62,9 +61,9 @@ module.exports = {
};
EOF
-export PATH="$(dirname $WCT):$(dirname $NPM):$PATH"
+export PATH="$(dirname $NPM):$PATH"
cd $t
test -n "${WCT}"
-$(basename ${WCT}) ${WCT_ARGS}
+${WCT} ${WCT_ARGS}
diff --git a/polygerrit-ui/app/gr-diff/gr-diff-root.html b/polygerrit-ui/app/gr-diff/gr-diff-root.html
index 132654c14d..b3f0d34d93 100644
--- a/polygerrit-ui/app/gr-diff/gr-diff-root.html
+++ b/polygerrit-ui/app/gr-diff/gr-diff-root.html
@@ -1,7 +1,4 @@
<script>
- // Needed for JSCompiler to understand it's global.
- // eslint-disable-next-line no-unused-vars, prefer-const
- let Gerrit = window.Gerrit || {};
- window.Gerrit = Gerrit;
+ window.Gerrit = window.Gerrit || {};
</script>
<link rel="import" href="../elements/diff/gr-diff/gr-diff.html">
diff --git a/polygerrit-ui/app/rules.bzl b/polygerrit-ui/app/rules.bzl
index 98387a02c8..7ef0ee3850 100644
--- a/polygerrit-ui/app/rules.bzl
+++ b/polygerrit-ui/app/rules.bzl
@@ -14,17 +14,14 @@ def polygerrit_bundle(name, srcs, outs, app):
# See: https://github.com/google/closure-compiler/issues/2042
compilation_level = "WHITESPACE_ONLY",
defs = [
- "--polymer_version=1",
+ "--polymer_version=2",
"--jscomp_off=duplicate",
- "--force_inject_library=es6_runtime",
],
- language = "ECMASCRIPT5",
+ language = "ECMASCRIPT_2017",
deps = [name + "_closure_lib"],
dependency_mode = "PRUNE_LEGACY",
)
- # TODO(davido): Remove JSC_REFERENCE_BEFORE_DECLARE when this is fixed upstream:
- # https://github.com/Polymer/polymer-resin/issues/7
closure_js_library(
name = name + "_closure_lib",
srcs = [appName + ".js"],
@@ -33,9 +30,7 @@ def polygerrit_bundle(name, srcs, outs, app):
# and remove this supression
suppress = [
"JSC_JSDOC_MISSING_TYPE_WARNING",
- "JSC_REFERENCE_BEFORE_DECLARE",
"JSC_UNNECESSARY_ESCAPE",
- "JSC_UNUSED_LOCAL_ASSIGNMENT",
],
deps = [
"//lib/polymer_externs:polymer_closure",
diff --git a/polygerrit-ui/app/samples/bind-parameters.html b/polygerrit-ui/app/samples/bind-parameters.html
index a7eb39a542..a28c462bc0 100644
--- a/polygerrit-ui/app/samples/bind-parameters.html
+++ b/polygerrit-ui/app/samples/bind-parameters.html
@@ -15,7 +15,7 @@
<script>
Polymer({
is: 'my-bind-sample',
- _legacyUndefinedCheck: true,
+
properties: {
computedExample: {
type: String,
diff --git a/polygerrit-ui/app/samples/coverage-plugin.html b/polygerrit-ui/app/samples/coverage-plugin.html
index 0d38c63e71..d1d96a8736 100644
--- a/polygerrit-ui/app/samples/coverage-plugin.html
+++ b/polygerrit-ui/app/samples/coverage-plugin.html
@@ -32,6 +32,11 @@
const coverageData = {};
let displayCoverage = false;
const annotationApi = plugin.annotationApi();
+ const styleApi = plugin.styles();
+
+ const coverageStyle = styleApi.css('background-color: #EF9B9B !important');
+ const emptyStyle = styleApi.css('');
+
annotationApi.addLayer(context => {
if (Object.keys(coverageData).length === 0) {
// Coverage data is not ready yet.
@@ -41,16 +46,16 @@
const line = context.line;
// Highlight lines missing coverage with this background color if
// coverage should be displayed, else do nothing.
- const cssClass = displayCoverage
- ? Gerrit.css('background-color: #EF9B9B')
- : Gerrit.css('');
+ const annotationStyle = displayCoverage
+ ? coverageStyle
+ : emptyStyle;
if (coverageData[path] &&
coverageData[path].changeNum === context.changeNum &&
coverageData[path].patchNum === context.patchNum) {
const linesMissingCoverage = coverageData[path].linesMissingCoverage;
if (linesMissingCoverage.includes(line.afterNumber)) {
- context.annotateRange(0, line.text.length, cssClass, 'right');
- context.annotateLineNumber(cssClass, 'right');
+ context.annotateRange(0, line.text.length, annotationStyle, 'right');
+ context.annotateLineNumber(annotationStyle, 'right');
}
}
}).enableToggleCheckbox('Display Coverage', checkbox => {
diff --git a/polygerrit-ui/app/samples/repo-command.html b/polygerrit-ui/app/samples/repo-command.html
index 37aca04fc3..526d35051c 100644
--- a/polygerrit-ui/app/samples/repo-command.html
+++ b/polygerrit-ui/app/samples/repo-command.html
@@ -29,7 +29,7 @@
<script>
Polymer({
is: 'repo-command-low',
- _legacyUndefinedCheck: true,
+
attached() {
console.log(this.repoName);
console.log(this.config);
diff --git a/polygerrit-ui/app/samples/some-screen.html b/polygerrit-ui/app/samples/some-screen.html
index 527ebcebbd..da025a20fd 100644
--- a/polygerrit-ui/app/samples/some-screen.html
+++ b/polygerrit-ui/app/samples/some-screen.html
@@ -38,7 +38,7 @@
<script>
Polymer({
is: 'some-screen-main',
- _legacyUndefinedCheck: true,
+
properties: {
rootUrl: String,
},
diff --git a/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js
new file mode 100644
index 0000000000..6e503c73b0
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils.js
@@ -0,0 +1,71 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function(window) {
+ 'use strict';
+
+ if (window.GrDisplayNameUtils) {
+ return;
+ }
+
+ const ANONYMOUS_NAME = 'Anonymous';
+
+ class GrDisplayNameUtils {
+ /**
+ * enableEmail when true enables to fallback to using email if
+ * the account name is not avilable.
+ */
+ static getUserName(config, account, enableEmail) {
+ if (account && account.name) {
+ return account.name;
+ } else if (account && account.username) {
+ return account.username;
+ } else if (enableEmail && account && account.email) {
+ return account.email;
+ } else if (config && config.user &&
+ config.user.anonymous_coward_name !== 'Anonymous Coward') {
+ return config.user.anonymous_coward_name;
+ }
+
+ return ANONYMOUS_NAME;
+ }
+
+ static getAccountDisplayName(config, account, enableEmail) {
+ const reviewerName = this._accountOrAnon(config, account, enableEmail);
+ const reviewerEmail = this._accountEmail(account.email);
+ const reviewerStatus = account.status ? '(' + account.status + ')' : '';
+ return [reviewerName, reviewerEmail, reviewerStatus]
+ .filter(p => p.length > 0).join(' ');
+ }
+
+ static _accountOrAnon(config, reviewer, enableEmail) {
+ return this.getUserName(config, reviewer, !!enableEmail);
+ }
+
+ static _accountEmail(email) {
+ if (typeof email !== 'undefined') {
+ return '<' + email + '>';
+ }
+ return '';
+ }
+
+ static getGroupDisplayName(group) {
+ return group.name + ' (group)';
+ }
+ }
+
+ window.GrDisplayNameUtils = GrDisplayNameUtils;
+})(window);
diff --git a/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html
new file mode 100644
index 0000000000..25ca4c5b2c
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-display-name-utils/gr-display-name-utils_test.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-display-name-utils</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../test/common-test-setup.html"/>
+<script src="gr-display-name-utils.js"></script>
+
+<script>
+ suite('gr-display-name-utils tests', () => {
+ // eslint-disable-next-line no-unused-vars
+ const config = {
+ user: {
+ anonymous_coward_name: 'Anonymous Coward',
+ },
+ };
+
+
+ test('getUserName name only', () => {
+ const account = {
+ name: 'test-name',
+ };
+ assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
+ 'test-name');
+ });
+
+ test('getUserName username only', () => {
+ const account = {
+ username: 'test-user',
+ };
+ assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
+ 'test-user');
+ });
+
+ test('getUserName email only', () => {
+ const account = {
+ email: 'test-user@test-url.com',
+ };
+ assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
+ 'test-user@test-url.com');
+ });
+
+ test('getUserName returns not Anonymous Coward as the anon name', () => {
+ assert.deepEqual(GrDisplayNameUtils.getUserName(config, null, true),
+ 'Anonymous');
+ });
+
+ test('getUserName for the config returning the anon name', () => {
+ const config = {
+ user: {
+ anonymous_coward_name: 'Test Anon',
+ },
+ };
+ assert.deepEqual(GrDisplayNameUtils.getUserName(config, null, true),
+ 'Test Anon');
+ });
+
+ test('getAccountDisplayName - account with name only', () => {
+ assert.equal(
+ GrDisplayNameUtils.getAccountDisplayName(config,
+ {name: 'Some user name'}),
+ 'Some user name');
+ });
+
+ test('getAccountDisplayName - account with email only', () => {
+ assert.equal(
+ GrDisplayNameUtils.getAccountDisplayName(config,
+ {email: 'my@example.com'}),
+ 'Anonymous <my@example.com>');
+ });
+
+ test('getAccountDisplayName - account with email only - allowEmail', () => {
+ assert.equal(
+ GrDisplayNameUtils.getAccountDisplayName(config,
+ {email: 'my@example.com'}, true),
+ 'my@example.com <my@example.com>');
+ });
+
+ test('getAccountDisplayName - account with name and status', () => {
+ assert.equal(
+ GrDisplayNameUtils.getAccountDisplayName(config, {
+ name: 'Some name',
+ status: 'OOO',
+ }),
+ 'Some name (OOO)');
+ });
+
+ test('getAccountDisplayName - account with name and email', () => {
+ assert.equal(
+ GrDisplayNameUtils.getAccountDisplayName(config, {
+ name: 'Some name',
+ email: 'my@example.com',
+ }),
+ 'Some name <my@example.com>');
+ });
+
+ test('getAccountDisplayName - account with name, email and status', () => {
+ assert.equal(
+ GrDisplayNameUtils.getAccountDisplayName(config, {
+ name: 'Some name',
+ email: 'my@example.com',
+ status: 'OOO',
+ }),
+ 'Some name <my@example.com> (OOO)');
+ });
+
+ test('getGroupDisplayName', () => {
+ assert.equal(
+ GrDisplayNameUtils.getGroupDisplayName({name: 'Some user name'}),
+ 'Some user name (group)');
+ });
+
+ test('_accountEmail', () => {
+ assert.equal(
+ GrDisplayNameUtils._accountEmail('email@gerritreview.com'),
+ '<email@gerritreview.com>');
+ assert.equal(GrDisplayNameUtils._accountEmail(undefined), '');
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider.js b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider.js
new file mode 100644
index 0000000000..67001d2941
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider.js
@@ -0,0 +1,46 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function(window) {
+ 'use strict';
+
+ if (window.GrEmailSuggestionsProvider) {
+ return;
+ }
+
+ class GrEmailSuggestionsProvider {
+ constructor(restAPI) {
+ this._restAPI = restAPI;
+ }
+
+ getSuggestions(input) {
+ return this._restAPI.getSuggestedAccounts(`${input}`)
+ .then(accounts => {
+ if (!accounts) { return []; }
+ return accounts;
+ });
+ }
+
+ makeSuggestionItem(account) {
+ return {
+ name: GrDisplayNameUtils.getAccountDisplayName(null, account, true),
+ value: {account, count: 1},
+ };
+ }
+ }
+
+ window.GrEmailSuggestionsProvider = GrEmailSuggestionsProvider;
+})(window);
diff --git a/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
new file mode 100644
index 0000000000..0266ab91d9
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-email-suggestions-provider/gr-email-suggestions-provider_test.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-email-suggestions-provider</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
+<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="gr-email-suggestions-provider.js"></script>
+
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+</test-fixture>
+
+<script>
+ suite('GrEmailSuggestionsProvider tests', () => {
+ let sandbox;
+ let restAPI;
+ let provider;
+ const account1 = {
+ name: 'Some name',
+ email: 'some@example.com',
+ };
+ const account2 = {
+ email: 'other@example.com',
+ _account_id: 3,
+ };
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+
+ stub('gr-rest-api-interface', {
+ getConfig() { return Promise.resolve({}); },
+ });
+ restAPI = fixture('basic');
+ provider = new GrEmailSuggestionsProvider(restAPI);
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('getSuggestions', done => {
+ const getSuggestedAccountsStub =
+ sandbox.stub(restAPI, 'getSuggestedAccounts')
+ .returns(Promise.resolve([account1, account2]));
+
+ provider.getSuggestions('Some input').then(res => {
+ assert.deepEqual(res, [account1, account2]);
+ assert.isTrue(getSuggestedAccountsStub.calledOnce);
+ assert.equal(getSuggestedAccountsStub.lastCall.args[0], 'Some input');
+ done();
+ });
+ });
+
+ test('makeSuggestionItem', () => {
+ assert.deepEqual(provider.makeSuggestionItem(account1), {
+ name: 'Some name <some@example.com>',
+ value: {
+ account: account1,
+ count: 1,
+ },
+ });
+
+ assert.deepEqual(provider.makeSuggestionItem(account2), {
+ name: 'other@example.com <other@example.com>',
+ value: {
+ account: account2,
+ count: 1,
+ },
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js
new file mode 100644
index 0000000000..a95670be9e
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider.js
@@ -0,0 +1,47 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function(window) {
+ 'use strict';
+
+ if (window.GrGroupSuggestionsProvider) {
+ return;
+ }
+
+ class GrGroupSuggestionsProvider {
+ constructor(restAPI) {
+ this._restAPI = restAPI;
+ }
+
+ getSuggestions(input) {
+ return this._restAPI.getSuggestedGroups(`${input}`)
+ .then(groups => {
+ if (!groups) { return []; }
+ const keys = Object.keys(groups);
+ return keys.map(key => {
+ return Object.assign({}, groups[key], {name: key});
+ });
+ });
+ }
+
+ makeSuggestionItem(suggestion) {
+ return {name: suggestion.name,
+ value: {group: {name: suggestion.name, id: suggestion.id}}};
+ }
+ }
+
+ window.GrGroupSuggestionsProvider = GrGroupSuggestionsProvider;
+})(window);
diff --git a/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
new file mode 100644
index 0000000000..b60aaa9538
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-group-suggestions-provider/gr-group-suggestions-provider_test.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-group-suggestions-provider</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
+<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="gr-group-suggestions-provider.js"></script>
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+</test-fixture>
+
+<script>
+ suite('GrGroupSuggestionsProvider tests', () => {
+ let sandbox;
+ let restAPI;
+ let provider;
+ const group1 = {
+ name: 'Some name',
+ id: 1,
+ };
+ const group2 = {
+ name: 'Other name',
+ id: 3,
+ url: 'abcd',
+ };
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+
+ stub('gr-rest-api-interface', {
+ getConfig() { return Promise.resolve({}); },
+ });
+ restAPI = fixture('basic');
+ provider = new GrGroupSuggestionsProvider(restAPI);
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('getSuggestions', done => {
+ const getSuggestedAccountsStub =
+ sandbox.stub(restAPI, 'getSuggestedGroups')
+ .returns(Promise.resolve({
+ 'Some name': {id: 1},
+ 'Other name': {id: 3, url: 'abcd'},
+ }));
+
+ provider.getSuggestions('Some input').then(res => {
+ assert.deepEqual(res, [group1, group2]);
+ assert.isTrue(getSuggestedAccountsStub.calledOnce);
+ assert.equal(getSuggestedAccountsStub.lastCall.args[0], 'Some input');
+ done();
+ });
+ });
+
+ test('makeSuggestionItem', () => {
+ assert.deepEqual(provider.makeSuggestionItem(group1), {
+ name: 'Some name',
+ value: {
+ group: {
+ name: 'Some name',
+ id: 1,
+ },
+ },
+ });
+
+ assert.deepEqual(provider.makeSuggestionItem(group2), {
+ name: 'Other name',
+ value: {
+ group: {
+ name: 'Other name',
+ id: 3,
+ },
+ },
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js
new file mode 100644
index 0000000000..ffcdba9846
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js
@@ -0,0 +1,114 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function(window) {
+ 'use strict';
+
+ if (window.GrReviewerSuggestionsProvider) {
+ return;
+ }
+
+ /**
+ * @enum {string}
+ */
+ Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES = {
+ REVIEWER: 'reviewers',
+ CC: 'ccs',
+ ANY: 'any',
+ };
+
+ class GrReviewerSuggestionsProvider {
+ static create(restApi, changeNumber, usersType) {
+ switch (usersType) {
+ case Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.REVIEWER:
+ return new GrReviewerSuggestionsProvider(restApi, changeNumber,
+ input => restApi.getChangeSuggestedReviewers(changeNumber,
+ input));
+ case Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.CC:
+ return new GrReviewerSuggestionsProvider(restApi, changeNumber,
+ input => restApi.getChangeSuggestedCCs(changeNumber, input));
+ case Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY:
+ return new GrReviewerSuggestionsProvider(restApi, changeNumber,
+ input => restApi.getSuggestedAccounts(
+ `cansee:${changeNumber} ${input}`));
+ default:
+ throw new Error(`Unknown users type: ${usersType}`);
+ }
+ }
+
+ constructor(restAPI, changeNumber, apiCall) {
+ this._changeNumber = changeNumber;
+ this._apiCall = apiCall;
+ this._restAPI = restAPI;
+ }
+
+ init() {
+ if (this._initPromise) {
+ return this._initPromise;
+ }
+ const getConfigPromise = this._restAPI.getConfig().then(cfg => {
+ this._config = cfg;
+ });
+ const getLoggedInPromise = this._restAPI.getLoggedIn().then(loggedIn => {
+ this._loggedIn = loggedIn;
+ });
+ this._initPromise = Promise.all([getConfigPromise, getLoggedInPromise])
+ .then(() => {
+ this._initialized = true;
+ });
+ return this._initPromise;
+ }
+
+ getSuggestions(input) {
+ if (!this._initialized || !this._loggedIn) {
+ return Promise.resolve([]);
+ }
+
+ return this._apiCall(input)
+ .then(reviewers => (reviewers || []));
+ }
+
+ makeSuggestionItem(suggestion) {
+ if (suggestion.account) {
+ // Reviewer is an account suggestion from getChangeSuggestedReviewers.
+ return {
+ name: GrDisplayNameUtils.getAccountDisplayName(this._config,
+ suggestion.account, false),
+ value: suggestion,
+ };
+ }
+
+ if (suggestion.group) {
+ // Reviewer is a group suggestion from getChangeSuggestedReviewers.
+ return {
+ name: GrDisplayNameUtils.getGroupDisplayName(suggestion.group),
+ value: suggestion,
+ };
+ }
+
+ if (suggestion._account_id) {
+ // Reviewer is an account suggestion from getSuggestedAccounts.
+ return {
+ name: GrDisplayNameUtils.getAccountDisplayName(this._config,
+ suggestion, false),
+ value: {account: suggestion, count: 1},
+ };
+ }
+ }
+ }
+
+ window.GrReviewerSuggestionsProvider = GrReviewerSuggestionsProvider;
+})(window);
diff --git a/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
new file mode 100644
index 0000000000..ca3c277761
--- /dev/null
+++ b/polygerrit-ui/app/scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html
@@ -0,0 +1,260 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-reviewer-suggestions-provider</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
+<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
+<script src="gr-reviewer-suggestions-provider.js"></script>
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+</test-fixture>
+
+<script>
+ suite('GrReviewerSuggestionsProvider tests', () => {
+ let sandbox;
+ let _nextAccountId = 0;
+ const makeAccount = function(opt_status) {
+ const accountId = ++_nextAccountId;
+ return {
+ _account_id: accountId,
+ name: 'name ' + accountId,
+ email: 'email ' + accountId,
+ status: opt_status,
+ };
+ };
+ let _nextAccountId2 = 0;
+ const makeAccount2 = function(opt_status) {
+ const accountId2 = ++_nextAccountId2;
+ return {
+ _account_id: accountId2,
+ name: 'name ' + accountId2,
+ status: opt_status,
+ };
+ };
+
+ let owner;
+ let existingReviewer1;
+ let existingReviewer2;
+ let suggestion1;
+ let suggestion2;
+ let suggestion3;
+ let restAPI;
+ let provider;
+
+ let redundantSuggestion1;
+ let redundantSuggestion2;
+ let redundantSuggestion3;
+ let change;
+
+ setup(done => {
+ owner = makeAccount();
+ existingReviewer1 = makeAccount();
+ existingReviewer2 = makeAccount();
+ suggestion1 = {account: makeAccount()};
+ suggestion2 = {account: makeAccount()};
+ suggestion3 = {
+ group: {
+ id: 'suggested group id',
+ name: 'suggested group',
+ },
+ };
+
+ stub('gr-rest-api-interface', {
+ getLoggedIn() { return Promise.resolve(true); },
+ getConfig() { return Promise.resolve({}); },
+ });
+
+ restAPI = fixture('basic');
+ change = {
+ _number: 42,
+ owner,
+ reviewers: {
+ CC: [existingReviewer1],
+ REVIEWER: [existingReviewer2],
+ },
+ };
+ sandbox = sinon.sandbox.create();
+ return flush(done);
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+ suite('allowAnyUser set to false', () => {
+ setup(done => {
+ provider = GrReviewerSuggestionsProvider.create(restAPI, change._number,
+ Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.REVIEWER);
+ provider.init().then(done);
+ });
+ suite('stubbed values for _getReviewerSuggestions', () => {
+ setup(() => {
+ stub('gr-rest-api-interface', {
+ getChangeSuggestedReviewers() {
+ redundantSuggestion1 = {account: existingReviewer1};
+ redundantSuggestion2 = {account: existingReviewer2};
+ redundantSuggestion3 = {account: owner};
+ return Promise.resolve([redundantSuggestion1, redundantSuggestion2,
+ redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
+ },
+ });
+ });
+
+ test('makeSuggestionItem formats account or group accordingly', () => {
+ let account = makeAccount();
+ const account3 = makeAccount2();
+ let suggestion = provider.makeSuggestionItem({account});
+ assert.deepEqual(suggestion, {
+ name: account.name + ' <' + account.email + '>',
+ value: {account},
+ });
+
+ const group = {name: 'test'};
+ suggestion = provider.makeSuggestionItem({group});
+ assert.deepEqual(suggestion, {
+ name: group.name + ' (group)',
+ value: {group},
+ });
+
+ suggestion = provider.makeSuggestionItem(account);
+ assert.deepEqual(suggestion, {
+ name: account.name + ' <' + account.email + '>',
+ value: {account, count: 1},
+ });
+
+ suggestion = provider.makeSuggestionItem({account: {}});
+ assert.deepEqual(suggestion, {
+ name: 'Anonymous',
+ value: {account: {}},
+ });
+
+ provider._config = {
+ user: {
+ anonymous_coward_name: 'Anonymous Coward Name',
+ },
+ };
+
+ suggestion = provider.makeSuggestionItem({account: {}});
+ assert.deepEqual(suggestion, {
+ name: 'Anonymous Coward Name',
+ value: {account: {}},
+ });
+
+ account = makeAccount('OOO');
+
+ suggestion = provider.makeSuggestionItem({account});
+ assert.deepEqual(suggestion, {
+ name: account.name + ' <' + account.email + '> (OOO)',
+ value: {account},
+ });
+
+ suggestion = provider.makeSuggestionItem(account);
+ assert.deepEqual(suggestion, {
+ name: account.name + ' <' + account.email + '> (OOO)',
+ value: {account, count: 1},
+ });
+
+ sandbox.stub(GrDisplayNameUtils, '_accountEmail',
+ () => {
+ return '';
+ });
+
+ suggestion = provider.makeSuggestionItem(account3);
+ assert.deepEqual(suggestion, {
+ name: account3.name,
+ value: {account: account3, count: 1},
+ });
+ });
+
+ test('getSuggestions', done => {
+ provider.getSuggestions().then(reviewers => {
+ // Default is no filtering.
+ assert.equal(reviewers.length, 6);
+ assert.deepEqual(reviewers,
+ [redundantSuggestion1, redundantSuggestion2,
+ redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
+ }).then(done);
+ });
+
+ test('getSuggestions short circuits when logged out', () => {
+ // API call is already stubbed.
+ const xhrSpy = restAPI.getChangeSuggestedReviewers;
+ provider._loggedIn = false;
+ return provider.getSuggestions('').then(() => {
+ assert.isFalse(xhrSpy.called);
+ provider._loggedIn = true;
+ return provider.getSuggestions('').then(() => {
+ assert.isTrue(xhrSpy.called);
+ });
+ });
+ });
+ });
+
+ test('getChangeSuggestedReviewers is used', done => {
+ const suggestReviewerStub =
+ sandbox.stub(restAPI, 'getChangeSuggestedReviewers')
+ .returns(Promise.resolve([]));
+ const suggestAccountStub =
+ sandbox.stub(restAPI, 'getSuggestedAccounts')
+ .returns(Promise.resolve([]));
+
+ provider.getSuggestions('').then(() => {
+ assert.isTrue(suggestReviewerStub.calledOnce);
+ assert.isTrue(suggestReviewerStub.calledWith(42, ''));
+ assert.isFalse(suggestAccountStub.called);
+ done();
+ });
+ });
+ });
+
+ suite('allowAnyUser set to true', () => {
+ setup(done => {
+ provider = GrReviewerSuggestionsProvider.create(restAPI, change._number,
+ Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY);
+ provider.init().then(done);
+ });
+
+ test('getSuggestedAccounts is used', done => {
+ const suggestReviewerStub =
+ sandbox.stub(restAPI, 'getChangeSuggestedReviewers')
+ .returns(Promise.resolve([]));
+ const suggestAccountStub =
+ sandbox.stub(restAPI, 'getSuggestedAccounts')
+ .returns(Promise.resolve([]));
+
+ provider.getSuggestions('').then(() => {
+ assert.isFalse(suggestReviewerStub.called);
+ assert.isTrue(suggestAccountStub.calledOnce);
+ assert.isTrue(suggestAccountStub.calledWith('cansee:42 '));
+ done();
+ });
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/scripts/util.js b/polygerrit-ui/app/scripts/util.js
index 624992b3e4..e26d6d99b8 100644
--- a/polygerrit-ui/app/scripts/util.js
+++ b/polygerrit-ui/app/scripts/util.js
@@ -75,5 +75,94 @@
return wrappedPromise;
};
+ /**
+ * Get computed style value.
+ *
+ * If ShadyCSS is provided, use ShadyCSS api.
+ * If `getComputedStyleValue` is provided on the elment, use it.
+ * Otherwise fallback to native method (in polymer 2).
+ *
+ */
+ util.getComputedStyleValue = (name, el) => {
+ let style;
+ if (window.ShadyCSS) {
+ style = ShadyCSS.getComputedStyleValue(el, name);
+ } else if (el.getComputedStyleValue) {
+ style = el.getComputedStyleValue(name);
+ } else {
+ style = getComputedStyle(el).getPropertyValue(name);
+ }
+ return style;
+ };
+
+ /**
+ * Query selector on a dom element.
+ *
+ * This is shadow DOM compatible, but only works when selector is within
+ * one shadow host, won't work if your selector is crossing
+ * multiple shadow hosts.
+ *
+ */
+ util.querySelector = (el, selector) => {
+ let nodes = [el];
+ let element = null;
+ while (nodes.length) {
+ const node = nodes.pop();
+
+ // Skip if it's an invalid node.
+ if (!node || !node.querySelector) continue;
+
+ // Try find it with native querySelector directly
+ element = node.querySelector(selector);
+
+ if (element) {
+ break;
+ } else if (node.shadowRoot) {
+ // If shadowHost detected, add the host and its children
+ nodes = nodes.concat(Array.from(node.children));
+ nodes.push(node.shadowRoot);
+ } else {
+ nodes = nodes.concat(Array.from(node.children));
+ }
+ }
+ return element;
+ };
+
+ /**
+ * Query selector all dom elements matching with certain selector.
+ *
+ * This is shadow DOM compatible, but only works when selector is within
+ * one shadow host, won't work if your selector is crossing
+ * multiple shadow hosts.
+ *
+ * Note: this can be very expensive, only use when have to.
+ */
+ util.querySelectorAll = (el, selector) => {
+ let nodes = [el];
+ const results = new Set();
+ while (nodes.length) {
+ const node = nodes.pop();
+
+ if (!node || !node.querySelectorAll) continue;
+
+ // Try find all from regular children
+ [...node.querySelectorAll(selector)]
+ .forEach(el => results.add(el));
+
+ // Add all nodes with shadowRoot and loop through
+ const allShadowNodes = [...node.querySelectorAll('*')]
+ .filter(child => !!child.shadowRoot)
+ .map(child => child.shadowRoot);
+ nodes = nodes.concat(allShadowNodes);
+
+ // Add shadowRoot of current node if has one
+ // as its not included in node.querySelectorAll('*')
+ if (node.shadowRoot) {
+ nodes.push(node.shadowRoot);
+ }
+ }
+ return [...results];
+ };
+
window.util = util;
})(window);
diff --git a/polygerrit-ui/app/styles/dashboard-header-styles.html b/polygerrit-ui/app/styles/dashboard-header-styles.html
index ccc17b0234..88d50c06b9 100644
--- a/polygerrit-ui/app/styles/dashboard-header-styles.html
+++ b/polygerrit-ui/app/styles/dashboard-header-styles.html
@@ -34,7 +34,7 @@ limitations under the License.
}
.info {
display: inline-block;
- padding: 1em;
+ padding: var(--spacing-l);
vertical-align: top;
}
.info > div > span {
diff --git a/polygerrit-ui/app/styles/fonts.css b/polygerrit-ui/app/styles/fonts.css
index 41aec27ade..c83749218a 100644
--- a/polygerrit-ui/app/styles/fonts.css
+++ b/polygerrit-ui/app/styles/fonts.css
@@ -34,7 +34,7 @@
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
- src: local('Roboto'), local('RobotoMono-Regular'),
+ src: local('Roboto'), local('Roboto-Regular'),
url('../fonts/Roboto-Regular.woff2') format('woff2'),
url('../fonts/Roboto-Regular.woff') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
diff --git a/polygerrit-ui/app/styles/gr-change-list-styles.html b/polygerrit-ui/app/styles/gr-change-list-styles.html
index 8f72216ad6..345363bc85 100644
--- a/polygerrit-ui/app/styles/gr-change-list-styles.html
+++ b/polygerrit-ui/app/styles/gr-change-list-styles.html
@@ -17,9 +17,6 @@ limitations under the License.
<dom-module id="gr-change-list-styles">
<template>
<style>
- :host {
- font-size: var(--font-size-normal);
- }
gr-change-list-item,
tr {
border-top: 1px solid var(--border-color);
@@ -99,7 +96,7 @@ limitations under the License.
text-decoration: underline;
}
.cell {
- height: 2.25rem;
+ padding: var(--spacing-s) 0;
}
.star {
padding: 0;
@@ -123,7 +120,7 @@ limitations under the License.
vertical-align: middle;
}
.leftPadding {
- width: var(--default-horizontal-margin);
+ width: var(--spacing-l);
}
.star {
width: 30px;
@@ -165,12 +162,12 @@ limitations under the License.
}
@media only screen and (max-width: 50em) {
:host {
- font-size: var(--font-size-large);
+ font-size: var(--font-size-h3);
}
gr-change-list-item {
flex-wrap: wrap;
justify-content: space-between;
- padding: .25em .5em;
+ padding: var(--spacing-xs) var(--spacing-m);
}
gr-change-list-item[selected],
gr-change-list-item:focus {
@@ -199,10 +196,10 @@ limitations under the License.
}
.groupHeader .cell,
.noChanges .cell {
- padding: 0 .5em;
+ padding: 0 var(--spacing-m);
}
.subject {
- margin-bottom: .25em;
+ margin-bottom: var(--spacing-xs);
width: calc(100% - 2em);
}
.owner,
@@ -214,11 +211,6 @@ limitations under the License.
height: auto;
}
}
- @media only screen and (min-width: 1450px) {
- :host {
- font-size: 14px;
- }
- }
</style>
</template>
</dom-module>
diff --git a/polygerrit-ui/app/styles/gr-change-metadata-shared-styles.html b/polygerrit-ui/app/styles/gr-change-metadata-shared-styles.html
new file mode 100644
index 0000000000..fef3872501
--- /dev/null
+++ b/polygerrit-ui/app/styles/gr-change-metadata-shared-styles.html
@@ -0,0 +1,48 @@
+<!--
+@license
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<dom-module id="gr-change-metadata-shared-styles">
+ <template>
+ <style include="shared-styles"></style>
+ <style>
+ section {
+ display: table-row;
+ }
+
+ section:not(:first-of-type) .title,
+ section:not(:first-of-type) .value {
+ padding-top: var(--spacing-s);
+ }
+
+ .title,
+ .value {
+ display: table-cell;
+ }
+
+ .title {
+ color: var(--deemphasized-text-color);
+ max-width: 20em;
+ padding-left: var(--metadata-horizontal-padding);
+ padding-right: var(--metadata-horizontal-padding);
+ word-break: break-word;
+ }
+
+ .value {
+ padding-right: var(--metadata-horizontal-padding);
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/polygerrit-ui/app/styles/gr-change-view-integration-shared-styles.html b/polygerrit-ui/app/styles/gr-change-view-integration-shared-styles.html
new file mode 100644
index 0000000000..834f64acfc
--- /dev/null
+++ b/polygerrit-ui/app/styles/gr-change-view-integration-shared-styles.html
@@ -0,0 +1,54 @@
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ This is shared styles for change-view-integration endpoints.
+ All plugins that registered that endpoint should include this in
+ the component to have a consistent UX:
+
+ <style include="gr-change-view-integration-shared-styles"></style>
+
+ And use those defined class to apply these styles.
+-->
+<dom-module id="gr-change-view-integration-shared-styles">
+ <template>
+ <style include="shared-styles"></style>
+ <style>
+ .header {
+ color: var(--primary-text-color);
+ background-color: var(--table-header-background-color);
+ justify-content: space-between;
+ padding: var(--spacing-m) var(--spacing-l);
+ border-bottom: 1px solid var(--border-color);
+ }
+ .header .label {
+ font-weight: var(--font-weight-bold);
+ font-size: var(--font-size-h3);
+ margin: 0 var(--spacing-l) 0 0;
+ }
+ .header .note {
+ color: var(--deemphasized-text-color);
+ }
+ .content {
+ background-color: var(--view-background-color);
+ }
+ .header a,
+ .content a {
+ color: var(--link-color);
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/polygerrit-ui/app/styles/gr-form-styles.html b/polygerrit-ui/app/styles/gr-form-styles.html
index 65c1ae3cf1..3fe0a72b19 100644
--- a/polygerrit-ui/app/styles/gr-form-styles.html
+++ b/polygerrit-ui/app/styles/gr-form-styles.html
@@ -27,18 +27,18 @@ limitations under the License.
}
.gr-form-styles h1,
.gr-form-styles h2 {
- margin-bottom: .3em;
+ margin-bottom: var(--spacing-s);
}
.gr-form-styles h4 {
font-weight: var(--font-weight-bold);
}
.gr-form-styles fieldset {
border: none;
- margin-bottom: 2em;
+ margin-bottom: var(--spacing-xxl);
}
.gr-form-styles section {
display: flex;
- margin: .25em 0;
+ margin: var(--spacing-s) 0;
min-height: 2em;
}
.gr-form-styles section * {
@@ -51,12 +51,9 @@ limitations under the License.
.gr-form-styles .title {
color: var(--deemphasized-text-color);
font-weight: var(--font-weight-bold);
- padding-right: .5em;
+ padding-right: var(--spacing-m);
width: 15em;
}
- .gr-form-styles iron-autogrow-textarea {
- font-size: var(--font-size-normal);
- }
.gr-form-styles th {
color: var(--deemphasized-text-color);
text-align: left;
@@ -65,7 +62,7 @@ limitations under the License.
.gr-form-styles td,
.gr-form-styles tfoot th {
height: 2em;
- padding: .25em 0;
+ padding: var(--spacing-s) 0;
vertical-align: middle;
}
.gr-form-styles .emptyHeader {
@@ -86,10 +83,9 @@ limitations under the License.
.gr-form-styles select,
.gr-form-styles textarea {
border: 1px solid var(--border-color);
- border-radius: 2px;
- font-size: var(--font-size-normal);
+ border-radius: var(--border-radius);
height: 2em;
- padding: 0 .15em;
+ padding: 0 var(--spacing-xs);
}
.gr-form-styles td:last-child {
width: 5em;
@@ -104,26 +100,24 @@ limitations under the License.
min-height: 2em;
--iron-autogrow-textarea: {
border: 1px solid var(--border-color);
- border-radius: 2px;
+ border-radius: var(--border-radius);
box-sizing: border-box;
- font-size: var(--font-size-normal);
- padding: .25em .15em 0 .15em;
+ padding: var(--spacing-s) var(--spacing-xs) 0 var(--spacing-xs);
}
}
.gr-form-styles gr-autocomplete {
border: none;
--gr-autocomplete: {
border: 1px solid var(--border-color);
- border-radius: 2px;
- font-size: var(--font-size-normal);
+ border-radius: var(--border-radius);
height: 2em;
- padding: 0 .15em;
+ padding: 0 var(--spacing-xs);
width: 14em;
}
}
@media only screen and (max-width: 40em) {
.gr-form-styles section {
- margin-bottom: 1em;
+ margin-bottom: var(--spacing-l);
}
.gr-form-styles .title,
.gr-form-styles .value {
diff --git a/polygerrit-ui/app/styles/gr-menu-page-styles.html b/polygerrit-ui/app/styles/gr-menu-page-styles.html
index 48ca396e10..d3b95b812c 100644
--- a/polygerrit-ui/app/styles/gr-menu-page-styles.html
+++ b/polygerrit-ui/app/styles/gr-menu-page-styles.html
@@ -22,12 +22,12 @@ limitations under the License.
display: block;
}
main {
- margin: 2em auto;
+ margin: var(--spacing-xxl) auto;
max-width: 50em;
}
.mainHeader {
margin-left: 14em;
- padding: 1em 0 1em 2em;
+ padding: var(--spacing-l) 0 var(--spacing-l) var(--spacing-xxl);
}
main.table,
.mainHeader {
@@ -42,11 +42,11 @@ limitations under the License.
}
.loading {
color: var(--deemphasized-text-color);
- padding: 1em var(--default-horizontal-margin);
+ padding: var(--spacing-l);
}
@media only screen and (max-width: 67em) {
main {
- margin: 2em 0 2em 15em;
+ margin: var(--spacing-xxl) 0 var(--spacing-xxl) 15em;
}
main.table {
margin-left: 14em;
@@ -54,17 +54,17 @@ limitations under the License.
}
@media only screen and (max-width: 53em) {
.loading {
- padding: 0 var(--default-horizontal-margin);
+ padding: 0 var(--spacing-l);
}
main {
- margin: 2em 1em;
+ margin: var(--spacing-xxl) var(--spacing-l);
}
main.table {
margin: 0;
}
.mainHeader {
margin-left: 0;
- padding: .5em 0 .5em 1em;
+ padding: var(--spacing-m) 0 var(--spacing-m) var(--spacing-l);
}
}
</style>
diff --git a/polygerrit-ui/app/styles/gr-page-nav-styles.html b/polygerrit-ui/app/styles/gr-page-nav-styles.html
index 18ec143ea9..ced6ecb6f1 100644
--- a/polygerrit-ui/app/styles/gr-page-nav-styles.html
+++ b/polygerrit-ui/app/styles/gr-page-nav-styles.html
@@ -18,13 +18,13 @@ limitations under the License.
<template>
<style>
.navStyles ul {
- padding: 1em 0;
+ padding: var(--spacing-l) 0;
}
.navStyles li {
border-bottom: 1px solid transparent;
border-top: 1px solid transparent;
display: block;
- padding: 0 calc(var(--default-horizontal-margin) + 0.5em);
+ padding: 0 var(--spacing-xl);
}
.navStyles li a {
display: block;
@@ -33,20 +33,20 @@ limitations under the License.
white-space: nowrap;
}
.navStyles .subsectionItem {
- padding-left: calc(var(--default-horizontal-margin) + 1.5em);
+ padding-left: var(--spacing-xxl);
}
.navStyles .hideSubsection {
display: none;
}
.navStyles li.sectionTitle {
- padding: 0 2em 0 var(--default-horizontal-margin);
+ padding: 0 var(--spacing-xxl) 0 var(--spacing-l);
}
.navStyles li.sectionTitle:not(:first-child) {
- margin-top: 1em;
+ margin-top: var(--spacing-l);
}
.navStyles .title {
font-weight: var(--font-weight-bold);
- margin: .4em 0;
+ margin: var(--spacing-s) 0;
}
.navStyles .selected {
background-color: var(--view-background-color);
@@ -57,7 +57,7 @@ limitations under the License.
.navStyles a {
color: var(--primary-text-color);
display: inline-block;
- margin: .4em 0;
+ margin: var(--spacing-s) 0;
}
</style>
</template>
diff --git a/polygerrit-ui/app/styles/gr-subpage-styles.html b/polygerrit-ui/app/styles/gr-subpage-styles.html
index 098a604bbd..222c38bd22 100644
--- a/polygerrit-ui/app/styles/gr-subpage-styles.html
+++ b/polygerrit-ui/app/styles/gr-subpage-styles.html
@@ -18,7 +18,7 @@ limitations under the License.
<template>
<style>
main {
- margin: 1em 1em;
+ margin: var(--spacing-l);
}
.loading {
display: none;
diff --git a/polygerrit-ui/app/styles/gr-table-styles.html b/polygerrit-ui/app/styles/gr-table-styles.html
index 13089526c3..d4e4bcf490 100644
--- a/polygerrit-ui/app/styles/gr-table-styles.html
+++ b/polygerrit-ui/app/styles/gr-table-styles.html
@@ -24,7 +24,7 @@ limitations under the License.
}
.genericList td {
height: 2.25rem;
- padding: .3rem 0;
+ padding: var(--spacing-s) 0;
vertical-align: middle;
}
.genericList tr {
@@ -38,11 +38,11 @@ limitations under the License.
}
.genericList th,
.genericList td {
- padding-right: 1rem;
+ padding-right: var(--spacing-l);
}
.genericList tr th:first-of-type,
.genericList tr td:first-of-type {
- padding-left: 1rem;
+ padding-left: var(--spacing-l);
}
.genericList tr:first-of-type {
border-top: 1px solid var(--border-color);
@@ -51,7 +51,7 @@ limitations under the License.
.genericList tr td:last-of-type {
border-left: 1px solid var(--border-color);
text-align: center;
- padding: 0 1em;
+ padding: 0 var(--spacing-l);
}
.genericList tr th.delete,
.genericList tr td.delete,
@@ -78,7 +78,7 @@ limitations under the License.
}
.genericList .groupHeader {
background-color: var(--table-subheader-background-color);
- font-size: var(--font-size-large);
+ font-size: var(--font-size-h3);
}
.genericList a {
color: var(--primary-text-color);
@@ -93,7 +93,7 @@ limitations under the License.
.genericList .loadingMsg {
color: var(--deemphasized-text-color);
display: block;
- padding: .3em var(--default-horizontal-margin);
+ padding: var(--spacing-s) var(--spacing-l);
}
.genericList .loadingMsg:not(.loading) {
display: none;
diff --git a/polygerrit-ui/app/styles/gr-voting-styles.html b/polygerrit-ui/app/styles/gr-voting-styles.html
index 3b1ee6476c..eec79be96c 100644
--- a/polygerrit-ui/app/styles/gr-voting-styles.html
+++ b/polygerrit-ui/app/styles/gr-voting-styles.html
@@ -23,6 +23,7 @@ limitations under the License.
border: 1px solid rgba(0,0,0,.12);
border-radius: 1em;
box-shadow: none;
+ box-sizing: border-box;
min-width: 3em;
}
}
diff --git a/polygerrit-ui/app/styles/main.css b/polygerrit-ui/app/styles/main.css
index 618a2d7128..4c85176728 100644
--- a/polygerrit-ui/app/styles/main.css
+++ b/polygerrit-ui/app/styles/main.css
@@ -26,10 +26,10 @@ html {
-webkit-text-size-adjust: none;
/*
* Default browser fonts are 16px. We want users with default settings to see
- * a base font of 13px. 13/16 = .8125. This needs to be in html because
+ * a base font of 14px. 14/16 = .875. This needs to be in html because
* can use rems based on this font-size throughout the app.
*/
- font-size: .8125em;
+ font-size: .875em;
}
html,
body {
@@ -42,6 +42,8 @@ body {
* Work around this using font-size and font-family.
*/
-webkit-text-size-adjust: none;
- font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
- line-height: 1.4;
+ font-family: var(--font-family, ''), 'Roboto', Arial, sans-serif;
+ font-size: var(--font-size-normal, 1rem);
+ line-height: var(--line-height-normal, 1.4);
+ color: var(--primary-text-color, black);
}
diff --git a/polygerrit-ui/app/styles/shared-styles.html b/polygerrit-ui/app/styles/shared-styles.html
index 78abe3abda..53147415e1 100644
--- a/polygerrit-ui/app/styles/shared-styles.html
+++ b/polygerrit-ui/app/styles/shared-styles.html
@@ -17,7 +17,9 @@ limitations under the License.
<dom-module id="shared-styles">
<template>
<style>
+
/* CSS reset */
+
html, body, button, div, span, applet, object, iframe, h1, h2, h3,
h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite,
code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub,
@@ -33,8 +35,11 @@ limitations under the License.
padding: 0;
vertical-align: baseline;
}
- input,
- iron-autogrow-textarea {
+ *::after,
+ *::before {
+ box-sizing: border-box;
+ }
+ input {
background-color: inherit;
border: 1px solid var(--border-color);
box-sizing: border-box;
@@ -42,6 +47,20 @@ limitations under the License.
margin: 0;
padding: 0;
}
+ iron-autogrow-textarea {
+ background-color: inherit;
+ color: var(--primary-text-color);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ box-sizing: border-box;
+ /* iron-autogrow-textarea has a "-webkit-appearance: textarea" :host
+ css rule, which prevents overriding the border color. Clear that. */
+ -webkit-appearance: none;
+
+ --iron-autogrow-textarea: {
+ padding: 4px;
+ };
+ }
a {
color: var(--link-color);
}
@@ -51,9 +70,6 @@ limitations under the License.
button {
font: inherit;
}
- body {
- line-height: 1;
- }
ol, ul {
list-style: none;
}
@@ -69,25 +85,42 @@ limitations under the License.
border-collapse: collapse;
border-spacing: 0;
}
- /* Other Shared Styles*/
- h1 {
- font-size: 2rem;
+
+ /* Fonts */
+
+ .font-normal {
+ font-size: var(--font-size-normal);
+ font-weight: var(--font-weight-normal);
+ line-height: var(--line-height-normal);
+ }
+ .font-small {
+ font-size: var(--font-size-small);
+ font-weight: var(--font-weight-normal);
+ line-height: var(--line-height-small);
+ }
+ h1, .font-h1 {
+ font-size: var(--font-size-h1);
font-weight: var(--font-weight-bold);
+ line-height: var(--line-height-h1);
}
- h2 {
- font-size: 1.5rem;
+ h2, .font-h2 {
+ font-size: var(--font-size-h2);
font-weight: var(--font-weight-bold);
+ line-height: var(--line-height-h2);
}
- h3 {
- font-size: 1.17em;
+ h3, .font-h3 {
+ font-size: var(--font-size-h3);
font-weight: var(--font-weight-bold);
+ line-height: var(--line-height-h3);
}
iron-icon {
color: var(--deemphasized-text-color);
--iron-icon-height: 20px;
--iron-icon-width: 20px;
}
+
/* Stopgap solution until we remove hidden$ attributes. */
+
[hidden] {
display: none !important;
}
@@ -103,12 +136,19 @@ limitations under the License.
--paper-toggle-button-checked-bar-color: var(--link-color);
--paper-toggle-button-checked-button-color: var(--link-color);
}
+ paper-tabs {
+ --paper-tab-content-focused: {
+ /* paper-tabs uses 700 here, which can look awkward */
+ font-weight: var(--font-weight-normal);
+ };
+ --paper-tab-content-unselected: {
+ /* paper-tabs uses 0.8 here, but we want to control the color directly */
+ opacity: 1;
+ color: var(--deemphasized-text-color);
+ }; }
strong {
font-weight: var(--font-weight-bold);
}
- :host {
- color: var(--primary-text-color);
- }
</style>
</template>
</dom-module>
diff --git a/polygerrit-ui/app/styles/themes/app-theme.html b/polygerrit-ui/app/styles/themes/app-theme.html
index ec47c53fdc..bb477c28a0 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.html
+++ b/polygerrit-ui/app/styles/themes/app-theme.html
@@ -16,139 +16,167 @@ limitations under the License.
-->
<custom-style><style is="custom-style">
html {
- /* Following vars have LTS for plugin API. */
- --primary-text-color: #000;
- /* For backwords compatibility we keep this as --header-background-color. */
- --header-background-color: #eee;
- --header-title-content: 'Gerrit';
- --header-icon: none;
- --header-icon-size: 0em;
- --header-text-color: #000;
- --header-title-font-size: 1.75rem;
- --footer-background-color: #eee;
- --border-color: #ddd;
- /* This allows to add a border in custom themes */
- --header-border-bottom: 1px solid var(--border-color);
- --header-border-image: '';
- --footer-border-top: 1px solid var(--border-color);
-
- /* Following are not part of plugin API. */
- --selection-background-color: rgba(161, 194, 250, 0.1);
- --hover-background-color: rgba(161, 194, 250, 0.2);
- --expanded-background-color: #eee;
- --view-background-color: #fff;
- --default-horizontal-margin: 1rem;
-
- --deemphasized-text-color: #757575;
- /* Used on text color for change list that doesn't need user's attention. */
- --reviewed-text-color: var(--primary-text-color);
- --font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
- /* Used on text for change list that needs user's attention. */
- --font-weight-bold: 500;
- --monospace-font-family: 'Roboto Mono', Menlo, 'Lucida Console', Monaco, monospace;
- --iron-overlay-backdrop: {
- transition: none;
- }
- --table-header-background-color: #fafafa;
- --table-subheader-background-color: #eaeaea;
-
- --chip-background-color: #eee;
-
- --dropdown-background-color: #fff;
-
- --select-background-color: rgb(248, 248, 248);
-
- --assignee-highlight-color: #fcfad6;
-
- /* Font sizes */
- --font-size-normal: 1rem;
- --font-size-small: .92rem;
- --font-size-large: 1.154rem;
-
+ /**
+ * When adding a new color variable make sure to also add it to the other
+ * theme files in the same directory.
+ *
+ * For colors prefer lower case hex colors.
+ *
+ * Note that plugins might be using these variables, so removing a variable
+ * can be a breaking change that should go into the release notes.
+ */
+
+ /* text colors */
+ --primary-text-color: black;
--link-color: #2a66d9;
- --primary-button-background-color: var(--link-color);
- --primary-button-text-color: #fff;
- --secondary-button-background-color: #fff;
+ --comment-text-color: black;
+ --deemphasized-text-color: #5F6368;
+ --default-button-text-color: #2a66d9;
+ --error-text-color: red;
+ --primary-button-text-color: white;
+ /* Used on text color for change list that doesn't need user's attention. */
+ --reviewed-text-color: black;
--secondary-button-text-color: #212121;
- --default-button-background-color: #fff;
- --default-button-text-color: var(--link-color);
- --dialog-background-color: #fff;
+ --tooltip-text-color: white;
+ --vote-text-color-recommended: #388e3c;
+ --vote-text-color-disliked: #d32f2f;
- /* Used for both the old patchset header and for indicating that a particular
- change message was selected. */
+ /* background colors */
+ --assignee-highlight-color: #fcfad6;
+ --chip-background-color: #eee;
+ --comment-background-color: #fcfad6;
+ --default-button-background-color: white;
+ --dialog-background-color: white;
+ --dropdown-background-color: white;
+ --edit-mode-background-color: #ebf5fb;
--emphasis-color: #fff9c4;
-
- --error-text-color: red;
-
+ --expanded-background-color: #eee;
+ --hover-background-color: rgba(161, 194, 250, 0.2);
+ --primary-button-background-color: #2a66d9;
+ --secondary-button-background-color: white;
+ --select-background-color: #f8f8f8;
+ --selection-background-color: rgba(161, 194, 250, 0.1);
+ --shell-command-background-color: #f5f5f5;
+ --shell-command-decoration-background-color: #ebebeb;
+ --table-header-background-color: #fafafa;
+ --table-subheader-background-color: #eaeaea;
+ --tooltip-background-color: #333;
+ --unresolved-comment-background-color: #fcfaa6;
+ --view-background-color: white;
--vote-color-approved: #9fcc6b;
- --vote-color-recommended: #c9dfaf;
- --vote-color-rejected: #f7a1ad;
--vote-color-disliked: #f7c4cb;
--vote-color-neutral: #ebf5fb;
+ --vote-color-recommended: #c9dfaf;
+ --vote-color-rejected: #f7a1ad;
- --vote-text-color-recommended: #388E3C;
- --vote-text-color-disliked: #D32F2F;
-
- /* Diff colors */
- --diff-selection-background-color: #c7dbf9;
- --light-remove-highlight-color: #FFEBEE;
- --light-add-highlight-color: #D8FED8;
- --light-remove-add-highlight-color: #FFF8DC;
- --light-rebased-add-highlight-color: #EEEEFF;
- --dark-remove-highlight-color: #FFCDD2;
- --dark-add-highlight-color: #AAF2AA;
- --dark-rebased-remove-highlight-color: #F7E8B7;
- --dark-rebased-add-highlight-color: #D7D7F9;
- --diff-context-control-color: #fff7d4;
- --diff-context-control-border-color: #f6e6a5;
- --diff-tab-indicator-color: var(--deemphasized-text-color);
- --diff-trailing-whitespace-indicator: #ff9ad2;
- --diff-highlight-range-color: rgba(255, 213, 0, 0.5);
- --diff-highlight-range-hover-color: rgba(255, 255, 0, 0.5);
+ /* misc colors */
+ --border-color: #ddd;
- --shell-command-background-color: #f5f5f5;
- --shell-command-decoration-background-color: #ebebeb;
+ /* fonts */
+ --font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
+ --monospace-font-family: 'Roboto Mono', 'SF Mono', 'Lucida Console', Monaco, monospace;
+ --font-size-code: 12px; /* 12px mono */
+ --font-size-mono: .929rem; /* 13px mono */
+ --font-size-small: .857rem; /* 12px */
+ --font-size-normal: 1rem; /* 14px */
+ --font-size-h3: 1.143rem; /* 16px */
+ --font-size-h2: 1.429rem; /* 20px */
+ --font-size-h1: 1.714rem; /* 24px */
+ --line-height-code: 1.334; /* 16px */
+ --line-height-mono: 1.286rem; /* 18px */
+ --line-height-small: 1.143rem; /* 16px */
+ --line-height-normal: 1.429rem; /* 20px */
+ --line-height-h3: 1.714rem; /* 24px */
+ --line-height-h2: 2rem; /* 28px */
+ --line-height-h1: 2.286rem; /* 32px */
+ --font-weight-normal: 400;
+ --font-weight-bold: 500;
- --comment-text-color: #000;
- --comment-background-color: #fcfad6;
- --unresolved-comment-background-color: #fcfaa6;
+ /* spacing */
+ --spacing-xxs: 1px;
+ --spacing-xs: 2px;
+ --spacing-s: 4px;
+ --spacing-m: 8px;
+ --spacing-l: 12px;
+ --spacing-xl: 16px;
+ --spacing-xxl: 24px;
- --edit-mode-background-color: #ebf5fb;
+ /* header and footer */
+ --footer-background-color: #eee;
+ --footer-border-top: 1px solid var(--border-color);
+ --header-background-color: #eee;
+ --header-border-bottom: 1px solid var(--border-color);
+ --header-border-image: '';
+ --header-box-shadow: none;
+ --header-padding: 0 var(--spacing-l);
+ --header-icon-size: 0em;
+ --header-icon: none;
+ --header-text-color: black;
+ --header-title-content: 'Gerrit';
+ --header-title-font-size: 1.75rem;
- --tooltip-background-color: #333;
- --tooltip-text-color: #fff;
+ /* diff colors */
+ --dark-add-highlight-color: #aaf2aa;
+ --dark-rebased-add-highlight-color: #d7d7f9;
+ --dark-rebased-remove-highlight-color: #f7e8b7;
+ --dark-remove-highlight-color: #ffcdd2;
+ --diff-blank-background-color: white;
+ --diff-context-control-background-color: #fff7d4;
+ --diff-context-control-border-color: #f6e6a5;
+ --diff-context-control-color: var(--deemphasized-text-color);
+ --diff-highlight-range-color: rgba(255, 213, 0, 0.5);
+ --diff-highlight-range-hover-color: rgba(255, 255, 0, 0.5);
+ --diff-selection-background-color: #c7dbf9;
+ --diff-tab-indicator-color: var(--deemphasized-text-color);
+ --diff-trailing-whitespace-indicator: #ff9ad2;
+ --light-add-highlight-color: #d8fed8;
+ --light-rebased-add-highlight-color: #eef;
+ --light-remove-add-highlight-color: #fff8dc;
+ --light-remove-highlight-color: #ffebee;
- --syntax-default-color: var(--primary-text-color);
+ /* syntax colors */
+ --syntax-attr-color: #219;
--syntax-attribute-color: var(--primary-text-color);
- --syntax-function-color: var(--primary-text-color);
- --syntax-meta-color: #FF1717;
- --syntax-keyword-color: #9E0069;
- --syntax-number-color: #164;
- --syntax-selector-class-color: #164;
- --syntax-variable-color: black;
- --syntax-template-variable-color: #0000C0;
- --syntax-comment-color: #3F7F5F;
- --syntax-string-color: #2A00FF;
- --syntax-selector-id-color: #2A00FF;
--syntax-built_in-color: #30a;
- --syntax-tag-color: #170;
+ --syntax-comment-color: #3f7f5f;
+ --syntax-default-color: var(--primary-text-color);
+ --syntax-doctag-weight: bold;
+ --syntax-function-color: var(--primary-text-color);
+ --syntax-keyword-color: #9e0069;
--syntax-link-color: #219;
- --syntax-meta-keyword-color: #219;
- --syntax-type-color: var(--color-link);
- --syntax-title-color: #0000C0;
- --syntax-attr-color: #219;
--syntax-literal-color: #219;
- --syntax-selector-pseudo-color: #FA8602;
- --syntax-regexp-color: #FA8602;
- --syntax-selector-attr-color: #FA8602;
- --syntax-template-tag-color: #FA8602;
+ --syntax-meta-color: #ff1717;
+ --syntax-meta-keyword-color: #219;
+ --syntax-number-color: #164;
--syntax-params-color: var(--primary-text-color);
- --syntax-doctag-weight: bold;
+ --syntax-regexp-color: #fa8602;
+ --syntax-selector-attr-color: #fa8602;
+ --syntax-selector-class-color: #164;
+ --syntax-selector-id-color: #2a00ff;
+ --syntax-selector-pseudo-color: #fa8602;
+ --syntax-string-color: #2a00ff;
+ --syntax-tag-color: #170;
+ --syntax-template-tag-color: #fa8602;
+ --syntax-template-variable-color: #0000c0;
+ --syntax-title-color: #0000c0;
+ --syntax-type-color: #2a66d9;
+ --syntax-variable-color: var(--primary-text-color);
+ /* misc */
+ --border-radius: 4px;
--reply-overlay-z-index: 1000;
+ --iron-overlay-backdrop: {
+ transition: none;
+ };
}
@media screen and (max-width: 50em) {
html {
- --default-horizontal-margin: .7rem;
+ --spacing-xxs: 1px;
+ --spacing-xs: 1px;
+ --spacing-s: 2px;
+ --spacing-m: 4px;
+ --spacing-l: 8px;
+ --spacing-xl: 12px;
+ --spacing-xxl: 16px;
}
}
</style></custom-style>
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html
index 718ac25658..957cc25344 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.html
@@ -17,97 +17,124 @@ limitations under the License.
<dom-module id="dark-theme">
<custom-style><style is="custom-style">
html {
- --primary-text-color: #e2e2e2;
- --view-background-color: #212121;
- --border-color: #555555;
- --header-border-bottom: 1px solid var(--border-color);
- --header-border-image: '';
- --footer-border-bottom: 1px solid var(--border-color);
- --table-header-background-color: #353637;
- --table-subheader-background-color: rgb(19, 20, 22);
- --header-background-color: #3c4043;
- --header-text-color: var(--primary-text-color);
- --deemphasized-text-color: #9a9a9a;
- /* Used on text color for change list doesn't need user's attention. */
- --reviewed-text-color: #DADCE0;
- /* Used on text for change list that needs user's attention. */
- --font-weight-bold: 900;
- --footer-background-color: var(--table-header-background-color);
+ /**
+ * Sections and variables must stay consistent with app-theme.html.
+ *
+ * Only modify color variables in this theme file. dark-theme extends
+ * app-theme, so there is no need to repeat all variables, but for colors
+ * it does make sense to list them all: If you override one color, then
+ * you probably want to override all.
+ */
+
+ /* text colors */
+ --primary-text-color: #e8eaed;
+ --link-color: #8ab4f8;
+ --comment-text-color: var(--primary-text-color);
+ --deemphasized-text-color: #9e9e9e;
+ --default-button-text-color: #8ab4f8;
+ --error-text-color: red;
+ --primary-button-text-color: var(--primary-text-color);
+ /* Used on text color for change list doesn't need user's attention. */
+ --reviewed-text-color: #dadce0;
+ --secondary-button-text-color: var(--deemphasized-text-color);
+ --tooltip-text-color: white;
+ --vote-text-color-recommended: #388e3c;
+ --vote-text-color-disliked: #d32f2f;
+
+ /* background colors */
+ --assignee-highlight-color: #3a361c;
+ --chip-background-color: #131416;
+ --comment-background-color: #0b162b;
+ --default-button-background-color: #3c4043;
+ --dialog-background-color: #131416;
+ --dropdown-background-color: #131416;
+ --edit-mode-background-color: #5c0a36;
+ --emphasis-color: #383f4a;
--expanded-background-color: #26282b;
- --link-color: #5487E5;
+ --hover-background-color: rgba(161, 194, 250, 0.2);
--primary-button-background-color: var(--link-color);
- --primary-button-text-color: var(--primary-text-color);
--secondary-button-background-color: var(--primary-text-color);
- --secondary-button-text-color: var(--deemphasized-text-color);
- --default-button-text-color: var(--link-color);
- --default-button-background-color: var(--table-subheader-background-color);
- --dropdown-background-color: var(--table-header-background-color);
- --dialog-background-color: var(--view-background-color);
- --chip-background-color: var(--table-header-background-color);
- --header-title-font-size: 1.75rem;
+ --select-background-color: #3c4043;
+ --selection-background-color: rgba(161, 194, 250, 0.1);
+ --shell-command-background-color: #5f5f5f;
+ --shell-command-decoration-background-color: #999;
+ --table-header-background-color: #131416;
+ --table-subheader-background-color: rgba(158, 158, 158, 0.24);
+ --tooltip-background-color: #111;
+ --unresolved-comment-background-color: #385a9a;
+ --view-background-color: #131416;
+ --vote-color-approved: #7fb66b;
+ --vote-color-disliked: #bf6874;
+ --vote-color-neutral: #597280;
+ --vote-color-recommended: #3f6732;
+ --vote-color-rejected: #ac2d3e;
- --select-background-color: var(--table-subheader-background-color);
+ /* misc colors */
+ --border-color: #5f6368;
- --assignee-highlight-color: rgb(58, 54, 28);
+ /* fonts */
+ --font-weight-bold: 900;
- --diff-selection-background-color: #3A71D8;
- --light-remove-highlight-color: #320404;
- --light-add-highlight-color: #0F401F;
- --light-remove-add-highlight-color: #2f3f2f;
- --light-rebased-remove-highlight-color: rgb(60, 37, 8);
- --light-rebased-add-highlight-color: rgb(72, 113, 101);
- --dark-remove-highlight-color: #62110F;
+ /* spacing */
+
+ /* header and footer */
+ --footer-background-color: #131416;
+ --footer-border-top: 1px solid var(--border-color);
+ --header-background-color: #3c4043;
+ --header-border-bottom: 1px solid var(--border-color);
+ --header-padding: 0 var(--spacing-l);
+ --header-text-color: var(--primary-text-color);
+
+ /* diff colors */
--dark-add-highlight-color: #133820;
- --dark-rebased-remove-highlight-color: rgba(255, 139, 6, 0.15);
--dark-rebased-add-highlight-color: rgba(11, 255, 155, 0.15);
- --diff-context-control-color: var(--table-header-background-color);
+ --dark-rebased-remove-highlight-color: rgba(255, 139, 6, 0.15);
+ --dark-remove-highlight-color: #62110f;
+ --diff-blank-background-color: #212121;
+ --diff-context-control-background-color: #131416;
--diff-context-control-border-color: var(--border-color);
+ --diff-context-control-color: var(--deemphasized-text-color);
--diff-highlight-range-color: rgba(0, 100, 200, 0.5);
--diff-highlight-range-hover-color: rgba(0, 150, 255, 0.5);
- --shell-command-background-color: #5f5f5f;
- --shell-command-decoration-background-color: #999999;
- --comment-text-color: var(--primary-text-color);
- --comment-background-color: #0B162B;
- --unresolved-comment-background-color: rgb(56, 90, 154);
-
- --vote-color-approved: rgb(127, 182, 107);
- --vote-color-recommended: rgb(63, 103, 50);
- --vote-color-rejected: #ac2d3e;
- --vote-color-disliked: #bf6874;
- --vote-color-neutral: #597280;
-
- --edit-mode-background-color: rgb(92, 10, 54);
- --emphasis-color: #383f4a;
-
- --tooltip-background-color: #111;
+ --diff-selection-background-color: #3a71d8;
+ --diff-tab-indicator-color: var(--deemphasized-text-color);
+ --diff-trailing-whitespace-indicator: #ff9ad2;
+ --light-add-highlight-color: #0f401f;
+ --light-rebased-add-highlight-color: #487165;
+ --light-remove-add-highlight-color: #2f3f2f;
+ --light-remove-highlight-color: #320404;
- --syntax-default-color: var(--primary-text-color);
- --syntax-meta-color: #6D7EEE;
- --syntax-keyword-color: #CD4CF0;
- --syntax-number-color: #00998A;
- --syntax-selector-class-color: #FFCB68;
- --syntax-variable-color: #F77669;
- --syntax-template-variable-color: #F77669;
+ /* syntax colors */
+ --syntax-attr-color: #80cbbf;
+ --syntax-attribute-color: var(--primary-text-color);
+ --syntax-built_in-color: #f7c369;
--syntax-comment-color: var(--deemphasized-text-color);
- --syntax-string-color: #C3E88D;
- --syntax-selector-id-color: #F77669;
- --syntax-built_in-color: rgb(247, 195, 105);
- --syntax-tag-color: #F77669;
- --syntax-link-color: #C792EA;
- --syntax-meta-keyword-color: #EEFFF7;
- --syntax-type-color: #DD5F5F;
- --syntax-title-color: #75A5FF;
- --syntax-attr-color: #80CBBF;
- --syntax-literal-color: #EEFFF7;
- --syntax-selector-pseudo-color: #C792EA;
- --syntax-regexp-color: #F77669;
- --syntax-selector-attr-color: #80CBBF;
- --syntax-template-tag-color: #C792EA;
+ --syntax-default-color: var(--primary-text-color);
--syntax-doctag-weight: bold;
+ --syntax-function-color: var(--primary-text-color);
+ --syntax-keyword-color: #cd4cf0;
+ --syntax-link-color: #c792ea;
+ --syntax-literal-color: #eefff7;
+ --syntax-meta-color: #6d7eee;
+ --syntax-meta-keyword-color: #eefff7;
+ --syntax-number-color: #00998a;
--syntax-params-color: var(--primary-text-color);
+ --syntax-regexp-color: #f77669;
+ --syntax-selector-attr-color: #80cbbf;
+ --syntax-selector-class-color: #ffcb68;
+ --syntax-selector-id-color: #f77669;
+ --syntax-selector-pseudo-color: #c792ea;
+ --syntax-string-color: #c3e88d;
+ --syntax-tag-color: #f77669;
+ --syntax-template-tag-color: #c792ea;
+ --syntax-template-variable-color: #f77669;
+ --syntax-title-color: #75a5ff;
+ --syntax-type-color: #dd5f5f;
+ --syntax-variable-color: #f77669;
- --reply-overlay-z-index: 1000;
+ /* misc */
+ /* rules applied to <html> */
background-color: var(--view-background-color);
}
</style></custom-style>
diff --git a/polygerrit-ui/app/template_test_srcs/template_test.js b/polygerrit-ui/app/template_test_srcs/template_test.js
index 3de6227733..d715d7d442 100644
--- a/polygerrit-ui/app/template_test_srcs/template_test.js
+++ b/polygerrit-ui/app/template_test_srcs/template_test.js
@@ -1,45 +1,6 @@
const fs = require('fs');
const twinkie = require('fried-twinkie');
-/**
- * For the purposes of template type checking, externs should be added for
- * anything set on the window object. Note that sub-properties of these
- * declared properties are considered something separate.
- *
- * @todo (beckysiegel) Gerrit's class definitions should be recognized in
- * closure types.
- */
-const EXTERN_NAMES = [
- 'Gerrit',
- 'GrAnnotation',
- 'GrAttributeHelper',
- 'GrChangeActionsInterface',
- 'GrChangeReplyInterface',
- 'GrDiffBuilder',
- 'GrDiffBuilderImage',
- 'GrDiffBuilderSideBySide',
- 'GrDiffBuilderUnified',
- 'GrDiffGroup',
- 'GrDiffLine',
- 'GrDomHooks',
- 'GrEditConstants',
- 'GrEtagDecorator',
- 'GrFileListConstants',
- 'GrGapiAuth',
- 'GrGerritAuth',
- 'GrLinkTextParser',
- 'GrPluginEndpoints',
- 'GrPopupInterface',
- 'GrRangeNormalizer',
- 'GrReporting',
- 'GrReviewerUpdatesParser',
- 'GrCountStringFormatter',
- 'GrThemeApi',
- 'moment',
- 'page',
- 'util',
-];
-
fs.readdir('./polygerrit-ui/temp/behaviors/', (err, data) => {
if (err) {
console.log('error /polygerrit-ui/temp/behaviors/ directory');
@@ -87,30 +48,39 @@ fs.readdir('./polygerrit-ui/temp/behaviors/', (err, data) => {
mappings = mappingSpecificFile;
}
- additionalSources.push({
- path: 'custom-externs.js',
- src: '/** @externs */' +
- EXTERN_NAMES.map( name => { return `var ${name};`; }).join(' '),
- });
-
- const toCheck = [];
- for (key of Object.keys(mappings)) {
- if (mappings[key].html && mappings[key].js) {
- toCheck.push({
- htmlSrcPath: mappings[key].html,
- jsSrcPath: mappings[key].js,
- jsModule: 'polygerrit.' + mappings[key].package,
+ /**
+ * Types in Gerrit.
+ * All types should be under `./polygerrit-ui/app/types` folder and end with `js`.
+ */
+ fs.readdir('./polygerrit-ui/app/types/', (err, typeFiles) => {
+ for (const typeFile of typeFiles) {
+ if (!typeFile.endsWith('.js')) continue;
+ additionalSources.push({
+ path: `./polygerrit-ui/app/types/${typeFile}`,
+ src: fs.readFileSync(
+ `./polygerrit-ui/app/types/${typeFile}`, 'utf-8'),
});
}
- }
- twinkie.checkTemplate(toCheck, additionalSources)
- .then(() => {}, joinedErrors => {
- if (joinedErrors) {
+ const toCheck = [];
+ for (key of Object.keys(mappings)) {
+ if (mappings[key].html && mappings[key].js) {
+ toCheck.push({
+ htmlSrcPath: mappings[key].html,
+ jsSrcPath: mappings[key].js,
+ jsModule: 'polygerrit.' + mappings[key].package,
+ });
+ }
+ }
+
+ twinkie.checkTemplate(toCheck, additionalSources)
+ .then(() => {}, joinedErrors => {
+ if (joinedErrors) {
+ process.exit(1);
+ }
+ }).catch(e => {
+ console.error(e);
process.exit(1);
- }
- }).catch(e => {
- console.error(e);
- process.exit(1);
- });
+ });
+ });
});
diff --git a/polygerrit-ui/app/test/common-test-setup.html b/polygerrit-ui/app/test/common-test-setup.html
index a549dd4c52..c1d8bbdb2f 100644
--- a/polygerrit-ui/app/test/common-test-setup.html
+++ b/polygerrit-ui/app/test/common-test-setup.html
@@ -17,7 +17,7 @@ limitations under the License.
-->
<link rel="import"
- href="../bower_components/polymer-resin/standalone/polymer-resin.html" />
+ href="/bower_components/polymer-resin/standalone/polymer-resin.html" />
<link rel="import" href="../behaviors/safe-types-behavior/safe-types-behavior.html">
<script>
security.polymer_resin.install({
@@ -53,13 +53,13 @@ limitations under the License.
(function() {
setup(() => {
if (!window.Gerrit) { return; }
- if (Gerrit._resetPlugins) {
- Gerrit._resetPlugins();
+ if (Gerrit._testOnly_resetPlugins) {
+ Gerrit._testOnly_resetPlugins();
}
});
})();
</script>
<link rel="import"
- href="../bower_components/iron-test-helpers/iron-test-helpers.html" />
+ href="/bower_components/iron-test-helpers/iron-test-helpers.html" />
<link rel="import" href="test-router.html" />
-<script src="../bower_components/moment/moment.js"></script>
+<script src="/bower_components/moment/moment.js"></script>
diff --git a/polygerrit-ui/app/test/common-test-setup.js b/polygerrit-ui/app/test/common-test-setup.js
new file mode 100644
index 0000000000..7ceff7e803
--- /dev/null
+++ b/polygerrit-ui/app/test/common-test-setup.js
@@ -0,0 +1,26 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Helps looking up the proper iron-input element during the Polymer 2
+ * transition. Polymer 2 uses the <iron-input> element, while Polymer 1 uses
+ * the nested <input is="iron-input"> element.
+ */
+window.ironInput = function(element) {
+ return Polymer.dom(element).querySelector(
+ Polymer.Element ? 'iron-input' : 'input[is=iron-input]');
+};
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index cd3aaeccf3..cca7d045ec 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -19,10 +19,11 @@ limitations under the License.
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>Elements Test Runner</title>
<meta charset="utf-8">
-<script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../bower_components/web-component-tester/browser.js"></script>
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
<script>
const testFiles = [];
+ const scriptsPath = '../scripts/';
const elementsPath = '../elements/';
const behaviorsPath = '../behaviors/';
@@ -61,9 +62,8 @@ limitations under the License.
'change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html',
'change-list/gr-create-change-help/gr-create-change-help_test.html',
'change-list/gr-dashboard-view/gr-dashboard-view_test.html',
+ 'change-list/gr-repo-header/gr-repo-header_test.html',
'change-list/gr-user-header/gr-user-header_test.html',
- 'change/gr-account-entry/gr-account-entry_test.html',
- 'change/gr-account-list/gr-account-list_test.html',
'change/gr-change-actions/gr-change-actions_test.html',
'change/gr-change-metadata/gr-change-metadata-it_test.html',
'change/gr-change-metadata/gr-change-metadata_test.html',
@@ -99,16 +99,18 @@ limitations under the License.
'core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html',
'core/gr-main-header/gr-main-header_test.html',
'core/gr-navigation/gr-navigation_test.html',
- 'core/gr-reporting/gr-jank-detector_test.html',
'core/gr-reporting/gr-reporting_test.html',
'core/gr-router/gr-router_test.html',
'core/gr-search-bar/gr-search-bar_test.html',
'core/gr-smart-search/gr-smart-search_test.html',
'diff/gr-comment-api/gr-comment-api_test.html',
+ 'diff/gr-coverage-layer/gr-coverage-layer_test.html',
'diff/gr-diff-builder/gr-diff-builder_test.html',
+ 'diff/gr-diff-builder/gr-diff-builder-unified_test.html',
'diff/gr-diff-cursor/gr-diff-cursor_test.html',
'diff/gr-diff-highlight/gr-annotation_test.html',
'diff/gr-diff-highlight/gr-diff-highlight_test.html',
+ 'diff/gr-diff-host/gr-diff-host_test.html',
'diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html',
'diff/gr-diff-processor/gr-diff-processor_test.html',
'diff/gr-diff-selection/gr-diff-selection_test.html',
@@ -125,7 +127,9 @@ limitations under the License.
'edit/gr-edit-file-controls/gr-edit-file-controls_test.html',
'edit/gr-editor-view/gr-editor-view_test.html',
'plugins/gr-admin-api/gr-admin-api_test.html',
+ 'plugins/gr-styles-api/gr-styles-api_test.html',
'plugins/gr-attribute-helper/gr-attribute-helper_test.html',
+ 'plugins/gr-dom-hooks/gr-dom-hooks_test.html',
'plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html',
'plugins/gr-event-helper/gr-event-helper_test.html',
'plugins/gr-external-style/gr-external-style_test.html',
@@ -134,7 +138,9 @@ limitations under the License.
'plugins/gr-popup-interface/gr-popup-interface_test.html',
'plugins/gr-repo-api/gr-repo-api_test.html',
'plugins/gr-settings-api/gr-settings-api_test.html',
+ 'plugins/gr-theme-api/gr-theme-api_test.html',
'settings/gr-account-info/gr-account-info_test.html',
+ 'settings/gr-agreements-list/gr-agreements-list_test.html',
'settings/gr-change-table-editor/gr-change-table-editor_test.html',
'settings/gr-cla-view/gr-cla-view_test.html',
'settings/gr-edit-preferences/gr-edit-preferences_test.html',
@@ -149,7 +155,9 @@ limitations under the License.
'settings/gr-ssh-editor/gr-ssh-editor_test.html',
'settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html',
'shared/gr-event-interface/gr-event-interface_test.html',
+ 'shared/gr-account-entry/gr-account-entry_test.html',
'shared/gr-account-label/gr-account-label_test.html',
+ 'shared/gr-account-list/gr-account-list_test.html',
'shared/gr-account-link/gr-account-link_test.html',
'shared/gr-alert/gr-alert_test.html',
'shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html',
@@ -161,33 +169,47 @@ limitations under the License.
'shared/gr-comment-thread/gr-comment-thread_test.html',
'shared/gr-comment/gr-comment_test.html',
'shared/gr-copy-clipboard/gr-copy-clipboard_test.html',
+ 'shared/gr-count-string-formatter/gr-count-string-formatter_test.html',
'shared/gr-cursor-manager/gr-cursor-manager_test.html',
'shared/gr-date-formatter/gr-date-formatter_test.html',
'shared/gr-dialog/gr-dialog_test.html',
'shared/gr-diff-preferences/gr-diff-preferences_test.html',
'shared/gr-download-commands/gr-download-commands_test.html',
+ 'shared/gr-dropdown/gr-dropdown_test.html',
'shared/gr-dropdown-list/gr-dropdown-list_test.html',
'shared/gr-editable-content/gr-editable-content_test.html',
'shared/gr-editable-label/gr-editable-label_test.html',
'shared/gr-formatted-text/gr-formatted-text_test.html',
+ 'shared/gr-hovercard/gr-hovercard_test.html',
+ 'shared/gr-js-api-interface/gr-annotation-actions-context_test.html',
+ 'shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html',
'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',
'shared/gr-js-api-interface/gr-change-reply-js-api_test.html',
+ 'shared/gr-js-api-interface/gr-api-utils_test.html',
'shared/gr-js-api-interface/gr-js-api-interface_test.html',
+ 'shared/gr-js-api-interface/gr-gerrit_test.html',
+ 'shared/gr-js-api-interface/gr-plugin-action-context_test.html',
+ 'shared/gr-js-api-interface/gr-plugin-loader_test.html',
'shared/gr-js-api-interface/gr-plugin-endpoints_test.html',
'shared/gr-js-api-interface/gr-plugin-rest-api_test.html',
'shared/gr-fixed-panel/gr-fixed-panel_test.html',
'shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html',
+ 'shared/gr-label-info/gr-label-info_test.html',
'shared/gr-lib-loader/gr-lib-loader_test.html',
'shared/gr-limited-text/gr-limited-text_test.html',
'shared/gr-linked-chip/gr-linked-chip_test.html',
'shared/gr-linked-text/gr-linked-text_test.html',
'shared/gr-list-view/gr-list-view_test.html',
+ 'shared/gr-overlay/gr-overlay_test.html',
'shared/gr-page-nav/gr-page-nav_test.html',
'shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html',
'shared/gr-rest-api-interface/gr-auth_test.html',
+ 'shared/gr-rest-api-interface/gr-etag-decorator_test.html',
'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
'shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html',
+ 'shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html',
'shared/gr-select/gr-select_test.html',
+ 'shared/gr-shell-command/gr-shell-command_test.html',
'shared/gr-storage/gr-storage_test.html',
'shared/gr-textarea/gr-textarea_test.html',
'shared/gr-tooltip-content/gr-tooltip-content_test.html',
@@ -198,7 +220,6 @@ limitations under the License.
for (let file of elements) {
file = elementsPath + file;
testFiles.push(file);
- testFiles.push(file + '?dom=shadow');
}
// Behaviors tests.
@@ -212,8 +233,9 @@ limitations under the License.
'rest-client-behavior/rest-client-behavior_test.html',
'gr-access-behavior/gr-access-behavior_test.html',
'gr-admin-nav-behavior/gr-admin-nav-behavior_test.html',
- 'gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html',
'gr-change-table-behavior/gr-change-table-behavior_test.html',
+ 'gr-list-view-behavior/gr-list-view-behavior_test.html',
+ 'gr-display-name-behavior/gr-display-name-behavior_test.html',
'gr-patch-set-behavior/gr-patch-set-behavior_test.html',
'gr-path-list-behavior/gr-path-list-behavior_test.html',
'gr-tooltip-behavior/gr-tooltip-behavior_test.html',
@@ -227,5 +249,17 @@ limitations under the License.
testFiles.push(file);
}
+ const scripts = [
+ 'gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html',
+ 'gr-group-suggestions-provider/gr-group-suggestions-provider_test.html',
+ 'gr-display-name-utils/gr-display-name-utils_test.html',
+ 'gr-email-suggestions-provider/gr-email-suggestions-provider_test.html',
+ ];
+ /* eslint-enable max-len */
+ for (let file of scripts) {
+ file = scriptsPath + file;
+ testFiles.push(file);
+ }
+
WCT.loadSuites(testFiles);
</script>
diff --git a/polygerrit-ui/app/types/custom-externs.js b/polygerrit-ui/app/types/custom-externs.js
new file mode 100644
index 0000000000..afa094c02c
--- /dev/null
+++ b/polygerrit-ui/app/types/custom-externs.js
@@ -0,0 +1,63 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * For the purposes of template type checking, externs should be added for
+ * anything set on the window object. Note that sub-properties of these
+ * declared properties are considered something separate.
+ *
+ * This file is only for template type checking, not used in Gerrit code.
+ */
+
+/* eslint-disable no-var */
+/* eslint-disable no-unused-vars */
+/** @externs */
+// @unused
+
+var Gerrit;
+var GrAnnotation;
+var GrAttributeHelper;
+var GrChangeActionsInterface;
+var GrChangeReplyInterface;
+var GrDiffBuilder;
+var GrDiffBuilderImage;
+var GrDiffBuilderSideBySide;
+var GrDiffBuilderUnified;
+var GrDiffGroup;
+var GrDiffLine;
+var GrDomHooks;
+var GrEditConstants;
+var GrEtagDecorator;
+var GrFileListConstants;
+var GrGapiAuth;
+var GrGerritAuth;
+var GrLinkTextParser;
+var GrPluginEndpoints;
+var GrPopupInterface;
+var GrRangeNormalizer;
+var GrReporting;
+var GrReviewerUpdatesParser;
+var GrCountStringFormatter;
+var GrThemeApi;
+var SiteBasedCache;
+var FetchPromisesCache;
+var GrRestApiHelper;
+var GrDisplayNameUtils;
+var GrReviewerSuggestionsProvider;
+var moment;
+var page;
+var util; \ No newline at end of file
diff --git a/polygerrit-ui/app/types/types.js b/polygerrit-ui/app/types/types.js
new file mode 100644
index 0000000000..e17bec8a81
--- /dev/null
+++ b/polygerrit-ui/app/types/types.js
@@ -0,0 +1,279 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Type definitions used across multiple files in Gerrit
+
+window.Gerrit = window.Gerrit || {};
+
+/** @enum {string} */
+Gerrit.CoverageType = {
+ /**
+ * start_character and end_character of the range will be ignored for this
+ * type.
+ */
+ COVERED: 'COVERED',
+ /**
+ * start_character and end_character of the range will be ignored for this
+ * type.
+ */
+ NOT_COVERED: 'NOT_COVERED',
+ PARTIALLY_COVERED: 'PARTIALLY_COVERED',
+ /**
+ * You don't have to use this. If there is no coverage information for a
+ * range, then it implicitly means NOT_INSTRUMENTED. start_character and
+ * end_character of the range will be ignored for this type.
+ */
+ NOT_INSTRUMENTED: 'NOT_INSTRUMENTED',
+};
+
+/**
+ * @typedef {{
+ * start_line: number,
+ * start_character: number,
+ * end_line: number,
+ * end_character: number,
+ * }}
+ */
+Gerrit.Range;
+
+/**
+ * @typedef {{side: string, range: Gerrit.Range, hovering: boolean}}
+ */
+Gerrit.HoveredRange;
+
+/**
+ * @typedef {{
+ * side: string,
+ * type: Gerrit.CoverageType,
+ * code_range: Gerrit.Range,
+ * }}
+ */
+Gerrit.CoverageRange;
+
+/**
+ * @typedef {{
+ * basePatchNum: (string|number),
+ * patchNum: (number),
+ * }}
+ */
+Gerrit.PatchRange;
+
+/**
+ * @typedef {{
+ * changeNum: (string|number),
+ * endpoint: string,
+ * patchNum: (string|number|null|undefined),
+ * errFn: (function(?Response, string=)|null|undefined),
+ * params: (Object|null|undefined),
+ * fetchOptions: (Object|null|undefined),
+ * anonymizedEndpoint: (string|undefined),
+ * reportEndpointAsIs: (boolean|undefined),
+ * }}
+ */
+Gerrit.ChangeFetchRequest;
+
+/**
+ * Object to describe a request for passing into _send.
+ * - method is the HTTP method to use in the request.
+ * - url is the URL for the request
+ * - body is a request payload.
+ * TODO (beckysiegel) remove need for number at least.
+ * - errFn is a function to invoke when the request fails.
+ * - cancelCondition is a function that, if provided and returns true, will
+ * cancel the response after it resolves.
+ * - contentType is the content type of the body.
+ * - headers is a key-value hash to describe HTTP headers for the request.
+ * - parseResponse states whether the result should be parsed as a JSON
+ * object using getResponseObject.
+ *
+ * @typedef {{
+ * method: string,
+ * url: string,
+ * body: (string|number|Object|null|undefined),
+ * errFn: (function(?Response, string=)|null|undefined),
+ * contentType: (string|null|undefined),
+ * headers: (Object|undefined),
+ * parseResponse: (boolean|undefined),
+ * anonymizedUrl: (string|undefined),
+ * reportUrlAsIs: (boolean|undefined),
+ * }}
+ */
+Gerrit.SendRequest;
+
+/**
+ * @typedef {{
+ * changeNum: (string|number),
+ * method: string,
+ * patchNum: (string|number|undefined),
+ * endpoint: string,
+ * body: (string|number|Object|null|undefined),
+ * errFn: (function(?Response, string=)|null|undefined),
+ * contentType: (string|null|undefined),
+ * headers: (Object|undefined),
+ * parseResponse: (boolean|undefined),
+ * anonymizedEndpoint: (string|undefined),
+ * reportEndpointAsIs: (boolean|undefined),
+ * }}
+ */
+Gerrit.ChangeSendRequest;
+
+/**
+ * @typedef {{
+ * url: string,
+ * fetchOptions: (Object|null|undefined),
+ * anonymizedUrl: (string|undefined),
+ * }}
+ */
+Gerrit.FetchRequest;
+
+/**
+ * Object to describe a request for passing into fetchJSON or fetchRawJSON.
+ * - url is the URL for the request (excluding get params)
+ * - errFn is a function to invoke when the request fails.
+ * - cancelCondition is a function that, if provided and returns true, will
+ * cancel the response after it resolves.
+ * - params is a key-value hash to specify get params for the request URL.
+ *
+ * @typedef {{
+ * url: string,
+ * errFn: (function(?Response, string=)|null|undefined),
+ * cancelCondition: (function()|null|undefined),
+ * params: (Object|null|undefined),
+ * fetchOptions: (Object|null|undefined),
+ * anonymizedUrl: (string|undefined),
+ * reportUrlAsIs: (boolean|undefined),
+ * }}
+ */
+Gerrit.FetchJSONRequest;
+
+/**
+ * @typedef {{
+ * message: string,
+ * icon: string,
+ * class: string,
+ * }}
+ */
+Gerrit.PushCertificateValidation;
+
+/**
+ * Object containing layout values to be used in rendering size-bars.
+ * `max{Inserted,Deleted}` represent the largest values of the
+ * `lines_inserted` and `lines_deleted` fields of the files respectively. The
+ * `max{Addition,Deletion}Width` represent the width of the graphic allocated
+ * to the insertion or deletion side respectively. Finally, the
+ * `deletionOffset` value represents the x-position for the deletion bar.
+ *
+ * @typedef {{
+ * maxInserted: number,
+ * maxDeleted: number,
+ * maxAdditionWidth: number,
+ * maxDeletionWidth: number,
+ * deletionOffset: number,
+ * }}
+ */
+Gerrit.LayoutStats;
+
+/**
+ * @typedef {{
+ * changeNum: number,
+ * path: string,
+ * patchRange: !Gerrit.PatchRange,
+ * projectConfig: (Object|undefined),
+ * }}
+ */
+Gerrit.CommentMeta;
+
+/**
+ * @typedef {{
+ * meta: !Gerrit.CommentMeta,
+ * left: !Array,
+ * right: !Array,
+ * }}
+ */
+Gerrit.CommentsBySide;
+
+/**
+ * The DiffIntralineInfo entity contains information about intraline edits in a
+ * file.
+ *
+ * The information consists of a list of <skip length, mark length> pairs, where
+ * the skip length is the number of characters between the end of the previous
+ * edit and the start of this edit, and the mark length is the number of edited
+ * characters following the skip. The start of the edits is from the beginning
+ * of the related diff content lines.
+ *
+ * Note that the implied newline character at the end of each line is included
+ * in the length calculation, and thus it is possible for the edits to span
+ * newlines.
+ *
+ * @typedef {!Array<number>}
+ */
+Gerrit.IntralineInfo;
+
+/**
+ * A portion of the diff that is treated the same.
+ *
+ * Called `DiffContent` in the API, see
+ * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#diff-content
+ *
+ * @typedef {{
+ * ab: ?Array<!string>,
+ * a: ?Array<!string>,
+ * b: ?Array<!string>,
+ * skip: ?number,
+ * edit_a: ?Array<!Gerrit.IntralineInfo>,
+ * edit_b: ?Array<!Gerrit.IntralineInfo>,
+ * due_to_rebase: ?boolean,
+ * common: ?boolean
+ * }}
+ */
+Gerrit.DiffChunk;
+
+/**
+ * Special line number which should not be collapsed into a shared region.
+ *
+ * @typedef {{
+ * number: number,
+ * leftSide: boolean
+ * }}
+ */
+Gerrit.LineOfInterest;
+
+/**
+ * @typedef {{
+ * html: Node,
+ * position: number,
+ * length: number,
+ * }}
+ */
+Gerrit.CommentLinkItem;
+
+/**
+ * @typedef {{
+ * name: string,
+ * value: Object,
+ * }}
+ */
+Gerrit.GrSuggestionItem;
+
+/**
+ * @typedef {{
+ * getSuggestions: function(string): Promise<Array<Object>>,
+ * makeSuggestionItem: function(Object): Gerrit.GrSuggestionItem,
+ * }}
+ */
+Gerrit.GrSuggestionsProvider; \ No newline at end of file
diff --git a/polygerrit-ui/app/wct_test.sh b/polygerrit-ui/app/wct_test.sh
index dd16ba7e1d..f1b4666de9 100755
--- a/polygerrit-ui/app/wct_test.sh
+++ b/polygerrit-ui/app/wct_test.sh
@@ -14,8 +14,7 @@ cp $TEST_SRCDIR/gerrit/polygerrit-ui/app/test/index.html $t/test/
if [ "${WCT_HEADLESS_MODE:-0}" != "0" ]; then
CHROME_OPTIONS=[\'start-maximized\',\'headless\',\'disable-gpu\',\'no-sandbox\']
- # TODO(paladox): Fix Firefox support for headless mode
- FIREFOX_OPTIONS=[\'\']
+ FIREFOX_OPTIONS=[\'-headless\']
else
CHROME_OPTIONS=[\'start-maximized\']
FIREFOX_OPTIONS=[\'\']
diff --git a/polygerrit-ui/server.go b/polygerrit-ui/server.go
index 2f5df90075..1a2d299d58 100644
--- a/polygerrit-ui/server.go
+++ b/polygerrit-ui/server.go
@@ -38,11 +38,12 @@ import (
)
var (
- plugins = flag.String("plugins", "", "comma seperated plugin paths to serve")
- port = flag.String("port", ":8081", "Port to serve HTTP requests on")
- host = flag.String("host", "gerrit-review.googlesource.com", "Host to proxy requests to")
- scheme = flag.String("scheme", "https", "URL scheme")
- cdnPattern = regexp.MustCompile("https://cdn.googlesource.com/polygerrit_ui/[0-9.]*")
+ plugins = flag.String("plugins", "", "comma seperated plugin paths to serve")
+ port = flag.String("port", ":8081", "Port to serve HTTP requests on")
+ host = flag.String("host", "gerrit-review.googlesource.com", "Host to proxy requests to")
+ scheme = flag.String("scheme", "https", "URL scheme")
+ cdnPattern = regexp.MustCompile("https://cdn.googlesource.com/polygerrit_ui/[0-9.]*")
+ bundledPluginsPattern = regexp.MustCompile("https://cdn.googlesource.com/polygerrit_assets/[0-9.]*")
)
func main() {
@@ -74,6 +75,7 @@ func main() {
http.HandleFunc("/accounts/", handleProxy)
http.HandleFunc("/config/", handleProxy)
http.HandleFunc("/projects/", handleProxy)
+ http.HandleFunc("/static/", handleProxy)
http.HandleFunc("/accounts/self/detail", handleAccountDetail)
if len(*plugins) > 0 {
@@ -102,7 +104,8 @@ func resourceBasePath() (string, error) {
func handleIndex(writer http.ResponseWriter, originalRequest *http.Request) {
fakeRequest := &http.Request{
URL: &url.URL{
- Path: "/",
+ Path: "/",
+ RawQuery: originalRequest.URL.RawQuery,
},
}
handleProxy(writer, fakeRequest)
@@ -168,7 +171,7 @@ func setJsonPropByPath(json map[string]interface{}, path []string, value interfa
func patchResponse(req *http.Request, res *http.Response) io.Reader {
switch req.URL.EscapedPath() {
case "/":
- return replaceCdn(res.Body)
+ return rewriteHostPage(res.Body)
case "/config/server/info":
return injectLocalPlugins(res.Body)
default:
@@ -176,13 +179,42 @@ func patchResponse(req *http.Request, res *http.Response) io.Reader {
}
}
-func replaceCdn(reader io.Reader) io.Reader {
+func rewriteHostPage(reader io.Reader) io.Reader {
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
original := buf.String()
+ // Simply remove all CDN references, so files are loaded from the local file system or the proxy
+ // server instead.
replaced := cdnPattern.ReplaceAllString(original, "")
+ // Modify window.INITIAL_DATA so that it has the same effect as injectLocalPlugins. To achieve
+ // this let's add JavaScript lines at the end of the <script>...</script> snippet that also
+ // contains window.INITIAL_DATA=...
+ // Here we rely on the fact that the <script> snippet that we want to append to is the first one.
+ if len(*plugins) > 0 {
+ // If the host page contains a reference to a plugin bundle that would be preloaded, then remove it.
+ replaced = bundledPluginsPattern.ReplaceAllString(replaced, "")
+
+ insertionPoint := strings.Index(replaced, "</script>")
+ builder := new(strings.Builder)
+ builder.WriteString(
+ "window.INITIAL_DATA['/config/server/info'].plugin.html_resource_paths = []; ")
+ builder.WriteString(
+ "window.INITIAL_DATA['/config/server/info'].plugin.js_resource_paths = []; ")
+ for _, p := range strings.Split(*plugins, ",") {
+ if filepath.Ext(p) == ".html" {
+ builder.WriteString(
+ "window.INITIAL_DATA['/config/server/info'].plugin.html_resource_paths.push('" + p + "'); ")
+ }
+ if filepath.Ext(p) == ".js" {
+ builder.WriteString(
+ "window.INITIAL_DATA['/config/server/info'].plugin.js_resource_paths.push('" + p + "'); ")
+ }
+ }
+ replaced = replaced[:insertionPoint] + builder.String() + replaced[insertionPoint:]
+ }
+
return strings.NewReader(replaced)
}
@@ -207,11 +239,11 @@ func injectLocalPlugins(reader io.Reader) io.Reader {
jsResources := getJsonPropByPath(response, jsPluginsPath).([]interface{})
for _, p := range strings.Split(*plugins, ",") {
- if strings.HasSuffix(p, ".html") {
+ if filepath.Ext(p) == ".html" {
htmlResources = append(htmlResources, p)
}
- if strings.HasSuffix(p, ".js") {
+ if filepath.Ext(p) == ".js" {
jsResources = append(jsResources, p)
}
}
@@ -261,7 +293,7 @@ type server struct{}
// Any path prefixes that should resolve to index.html.
var (
- fePaths = []string{"/q/", "/c/", "/p/", "/x/", "/dashboard/", "/admin/"}
+ fePaths = []string{"/q/", "/c/", "/p/", "/x/", "/dashboard/", "/admin/", "/settings/"}
issueNumRE = regexp.MustCompile(`^\/\d+\/?$`)
)
diff --git a/prologtests/examples/BUILD b/prologtests/examples/BUILD
new file mode 100644
index 0000000000..f4ebe901b8
--- /dev/null
+++ b/prologtests/examples/BUILD
@@ -0,0 +1,15 @@
+package(default_visibility = ["//visibility:public"])
+
+DUMMY = ["dummy.sh"]
+
+# Enable prologtests on newer Java versions again, when this Bazel bug is fixed:
+# https://github.com/bazelbuild/bazel/issues/9391
+sh_test(
+ name = "test_examples",
+ srcs = select({
+ "//:java11": DUMMY,
+ "//:java_next": DUMMY,
+ "//conditions:default": ["run.sh"],
+ }),
+ data = glob(["*.pl"]) + ["//:gerrit.war"],
+)
diff --git a/prologtests/examples/README.md b/prologtests/examples/README.md
new file mode 100644
index 0000000000..12eb256ec6
--- /dev/null
+++ b/prologtests/examples/README.md
@@ -0,0 +1,54 @@
+# Prolog Unit Test Examples
+
+## Run all examples
+
+Build a local gerrit.war and then run the script:
+
+ ./run.sh
+
+Note that a local Gerrit server is not needed because
+these unit test examples redefine wrappers of the `gerrit:change\*`
+rules to provide mocked change data.
+
+## Add a new unit test
+
+Please follow the pattern in `t1.pl`, `t2.pl`, or `t3.pl`.
+
+* Put code to be tested in a file, e.g. `rules.pl`.
+ For easy unit testing, split long clauses into short ones
+ and test every positive and negative path.
+
+* Create a new unit test file, e.g. `t1.pl`,
+ which should _load_ the test source file and `utils.pl`.
+
+ % First load all source files and the utils.pl.
+ :- load([aosp_rules,utils]).
+
+ :- begin_tests(t1). % give this test any name
+
+ % Use test0/1 or test1/1 to verify failed/passed goals.
+
+ :- end_tests(_,0). % check total pass/fail counts
+
+* Optionally replace calls to gerrit functions that depend on repository.
+ For example, define the following wrappers and in source code, use
+ `change_branch/1` instead of `gerrti:change_branch/1`.
+
+ change_branch(X) :- gerrit:change_branch(X).
+ commit_label(L,U) :- gerrit:commit_label(L,U).
+
+* In unit test file, redefine the gerrit function wrappers and test.
+ For example, in `t3.pl`, we have:
+
+ :- redefine(uploader,1,uploader(user(42))). % mocked uploader
+ :- test1(uploader(user(42))).
+ :- test0(is_exempt_uploader).
+
+ % is_exempt_uploader/0 is expected to fail because it is
+ % is_exempt_uploader :- uploader(user(Id)), memberchk(Id, [104, 106]).
+
+ % Note that gerrit:remove_label does not depend on Gerrit repository,
+ % so its caller remove_label/1 is tested without any redefinition.
+
+ :- test1(remove_label('MyReview',[],[])).
+ :- test1(remove_label('MyReview',submit(),submit())).
diff --git a/prologtests/examples/aosp_rules.pl b/prologtests/examples/aosp_rules.pl
new file mode 100644
index 0000000000..18e8a73fd6
--- /dev/null
+++ b/prologtests/examples/aosp_rules.pl
@@ -0,0 +1,148 @@
+% A simplified and mocked AOSP rules.pl
+
+%%%%% wrapper functions for unit tests
+
+change_branch(X) :- gerrit:change_branch(X).
+change_project(X) :- gerrit:change_project(X).
+commit_author(U,N,M) :- gerrit:commit_author(U,N,M).
+commit_delta(X) :- gerrit:commit_delta(X).
+commit_label(L,U) :- gerrit:commit_label(L,U).
+uploader(X) :- gerrit:uploader(X).
+
+%%%%% true/false conditions
+
+% Special auto-merger accounts.
+is_exempt_uploader :-
+ uploader(user(Id)),
+ memberchk(Id, [104, 106]).
+
+% Build cop overrides everything.
+has_build_cop_override :-
+ commit_label(label('Build-Cop-Override', 1), _).
+
+is_exempt_from_reviews :-
+ or(is_exempt_uploader, has_build_cop_override).
+
+% Some files in selected projects need API review.
+needs_api_review :-
+ commit_delta('^(.*/)?api/|^(system-api/)'),
+ change_project(Project),
+ memberchk(Project, [
+ 'platform/external/apache-http',
+ 'platform/frameworks/base',
+ 'platform/frameworks/support',
+ 'platform/packages/services/Car',
+ 'platform/prebuilts/sdk'
+ ]).
+
+% Some branches need DrNo review.
+needs_drno_review :-
+ change_branch(Branch),
+ memberchk(Branch, [
+ 'refs/heads/my-alpha-dev',
+ 'refs/heads/my-beta-dev'
+ ]).
+
+% Some author email addresses need Qualcomm-Review.
+needs_qualcomm_review :-
+ commit_author(_, _, M),
+ regex_matches(
+'.*@(qti.qualcomm.com|qca.qualcomm.com|quicinc.com|qualcomm.com)', M).
+
+% Special projects, branches, user accounts
+% can opt out owners review.
+opt_out_find_owners :-
+ change_branch(Branch),
+ memberchk(Branch, [
+ 'refs/heads/my-beta-testing',
+ 'refs/heads/my-testing'
+ ]).
+
+% Special projects, branches, user accounts
+% can opt in owners review.
+% Note that opt_out overrides opt_in.
+opt_in_find_owners :- true.
+
+
+%%%%% Simple list filters.
+
+remove_label(X, In, Out) :-
+ gerrit:remove_label(In, label(X, _), Out).
+
+% Slow but simple for short input list.
+remove_review_categories(In, Out) :-
+ remove_label('API-Review', In, L1),
+ remove_label('Code-Review', L1, L2),
+ remove_label('DrNo-Review', L2, L3),
+ remove_label('Owner-Review-Vote', L3, L4),
+ remove_label('Qualcomm-Review', L4, L5),
+ remove_label('Verified', L5, Out).
+
+
+%%%%% Missing rules in Gerrit Prolog Cafe.
+
+or(InA, InB) :- once((A;B)).
+
+not(Goal) :- Goal -> false ; true.
+
+% memberchk(+Element, +List)
+memberchk(X, [H|T]) :-
+ (X = H -> true ; memberchk(X, T)).
+
+maplist(Functor, In, Out) :-
+ (In = []
+ -> Out = []
+ ; (In = [X1|T1],
+ Out = [X2|T2],
+ Goal =.. [Functor, X1, X2],
+ once(Goal),
+ maplist(Functor, T1, T2)
+ )
+ ).
+
+
+%%%%% Conditional rules and filters.
+
+submit_filter(In, Out) :-
+ (is_exempt_from_reviews
+ -> remove_review_categories(In, Out)
+ ; (check_review(needs_api_review,
+ 'API_Review', In, L1),
+ check_review(needs_drno_review,
+ 'DrNo-Review', L1, L2),
+ check_review(needs_qualcomm_review,
+ 'Qualcomm-Review', L2, L3),
+ check_find_owners(L3, Out)
+ )
+ ).
+
+check_review(NeedReview, Label, In, Out) :-
+ (NeedReview
+ -> Out = In
+ ; remove_label(Label, In, Out)
+ ).
+
+% If opt_out_find_owners is true,
+% remove all 'Owner-Review-Vote' label;
+% else if opt_in_find_owners is true,
+% call find_owners:submit_filter;
+% else default to no find_owners filter.
+check_find_owners(In, Out) :-
+ (opt_out_find_owners
+ -> remove_label('Owner-Review-Vote', In, Temp)
+ ; (opt_in_find_owners
+ -> find_owners:submit_filter(In, Temp)
+ ; In = Temp
+ )
+ ),
+ Temp =.. [submit | L1],
+ remove_label('Owner-Approved', L1, L2),
+ maplist(owner_may_to_need, L2, L3),
+ Out =.. [submit | L3].
+
+% change may(_) to need(_) to block submit.
+owner_may_to_need(In, Out) :-
+ (In = label('Owner-Review-Vote', may(_))
+ -> Out = label('Owner-Review-Vote', need(_))
+ ; Out = In
+ ).
diff --git a/prologtests/examples/dummy.sh b/prologtests/examples/dummy.sh
new file mode 100755
index 0000000000..2e0cca350f
--- /dev/null
+++ b/prologtests/examples/dummy.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Skip all prolog tests for newer Java versions.
+# See https://github.com/bazelbuild/bazel/issues/9391
+# for more details why we cannot support running tests
+# on newer Java versions for now.
diff --git a/prologtests/examples/load.pl b/prologtests/examples/load.pl
new file mode 100644
index 0000000000..f5b49e8aa0
--- /dev/null
+++ b/prologtests/examples/load.pl
@@ -0,0 +1,26 @@
+% If you have 1.4.3 or older Prolog-Cafe, you need to
+% use (consult(load), load(load)) to get definition of load.
+% Then use load([f1,f2,...]) to load multiple source files.
+
+% Input is a list of file names or a single file name.
+% Use a conditional expression style without cut operator.
+load(X) :-
+ ( (X = [])
+ -> true
+ ; ( (X = [H|T])
+ -> (load_file(H), load(T))
+ ; load_file(X)
+ )
+ ).
+
+% load_file is '$consult' without the bug of unbound 'File' variable.
+% For repeated unit tests, skip statistics and print_message.
+load_file(F) :- atom(F), !,
+ '$prolog_file_name'(F, PF),
+ open(PF, read, In),
+ % print_message(info, [loading,PF,'...']),
+ % statistics(runtime, _),
+ consult_stream(PF, In),
+ % statistics(runtime, [_,T]),
+ % print_message(info, [PF,'loaded in',T,msec]),
+ close(In).
diff --git a/prologtests/examples/rules.pl b/prologtests/examples/rules.pl
new file mode 100644
index 0000000000..1a7b17cecf
--- /dev/null
+++ b/prologtests/examples/rules.pl
@@ -0,0 +1,29 @@
+% An example source file to be tested.
+
+% Add common rules missing in Prolog Cafe.
+memberchk(X, [H|T]) :-
+ (X = H) -> true ; memberchk(X, T).
+
+% A rule that can succeed/backtrack multiple times.
+super_users(1001).
+super_users(1002).
+
+% Deterministic rule that pass/fail only once.
+is_super_user(X) :- memberchk(X, [1001, 1002]).
+
+% Another rule that can pass 5 times.
+multi_users(101).
+multi_users(102).
+multi_users(103).
+multi_users(104).
+multi_users(105).
+
+% Okay, single deterministic fact.
+single_user(abc).
+
+% Wrap calls to gerrit repository, to be redefined in tests.
+change_owner(X) :- gerrit:change_owner(X).
+
+% To test is_owner without gerrit:change_owner,
+% we should redefine change_owner.
+is_owner(X) :- change_owner(X).
diff --git a/prologtests/examples/run.sh b/prologtests/examples/run.sh
new file mode 100755
index 0000000000..947c1532f6
--- /dev/null
+++ b/prologtests/examples/run.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+TESTS="t1 t2 t3"
+
+# Note that both t1.pl and t2.pl test code in rules.pl.
+# Unit tests are usually longer than the tested code.
+# So it is common to test one source file with multiple
+# unit test files.
+
+LF=$'\n'
+PASS=""
+FAIL=""
+
+echo "#### TEST_SRCDIR = ${TEST_SRCDIR}"
+
+if [ "${TEST_SRCDIR}" == "" ]; then
+ # Assume running alone
+ GERRIT_WAR="../../bazel-bin/gerrit.war"
+ SRCDIR="."
+else
+ # Assume running from bazel
+ GERRIT_WAR=`pwd`/gerrit.war
+ SRCDIR="prologtests/examples"
+fi
+
+# Default GERRIT_TMP is ~/.gerritcodereview/tmp,
+# which won't be writable in a bazel test sandbox.
+/bin/mkdir -p /tmp/gerrit
+export GERRIT_TMP=/tmp/gerrit
+
+for T in $TESTS
+do
+
+ pushd $SRCDIR
+
+ # Unit tests do not need to define clauses in packages.
+ # Use one prolog-shell per unit test, to avoid name collision.
+ echo "### Running test ${T}.pl"
+ echo "[$T]." | java -jar ${GERRIT_WAR} prolog-shell -q -s load.pl
+
+ if [ "x$?" != "x0" ]; then
+ echo "### Test ${T}.pl failed."
+ FAIL="${FAIL}${LF}FAIL: Test ${T}.pl"
+ else
+ PASS="${PASS}${LF}PASS: Test ${T}.pl"
+ fi
+
+ popd
+
+ # java -jar ../../bazel-bin/gerrit.war prolog-shell -s $T < /dev/null
+ # Calling prolog-shell with -s flag works for small files,
+ # but got run-time exception with t3.pl.
+ # com.googlecode.prolog_cafe.exceptions.ReductionLimitException:
+ # exceeded reduction limit of 1048576
+done
+
+echo "$PASS"
+
+if [ "$FAIL" != "" ]; then
+ echo "$FAIL"
+ exit 1
+fi
diff --git a/prologtests/examples/t1.pl b/prologtests/examples/t1.pl
new file mode 100644
index 0000000000..caf906192c
--- /dev/null
+++ b/prologtests/examples/t1.pl
@@ -0,0 +1,20 @@
+:- load([rules,utils]).
+:- begin_tests(t1).
+
+:- test1(true). % expect true to pass
+:- test0(false). % expect false to fail
+
+:- test1(X = 3). % unification should pass
+:- test1(_ = 3). % unification should pass
+:- test0(X \= 3). % not-unified should fail
+
+% (7-4) should have expected result
+:- test1((X is (7-4), X =:= 3)).
+:- test1((X is (7-4), X =\= 4)).
+
+% memberchk should pass/fail exactly once
+:- test1(memberchk(3,[1,3,5,3])).
+:- test0(memberchk(2,[1,3,5,3])).
+:- test0(memberchk(2,[])).
+
+:- end_tests_or_halt(0). % expect no failure
diff --git a/prologtests/examples/t2.pl b/prologtests/examples/t2.pl
new file mode 100644
index 0000000000..9424b5330d
--- /dev/null
+++ b/prologtests/examples/t2.pl
@@ -0,0 +1,25 @@
+:- load([rules,utils]).
+:- begin_tests(t2).
+
+% expected to pass or fail once.
+:- test0(super_users(1000)).
+:- test1(super_users(1001)).
+
+:- test1(is_super_user(1001)).
+:- test1(is_super_user(1002)).
+:- test0(is_super_user(1003)).
+
+:- test1(super_users(X)). % expected fail (pass twice)
+:- test1(multi_users(X)). % expected fail (pass many times)
+
+:- test1(single_user(X)). % expected pass once
+
+% Redefine change_owner, skip gerrit:change_owner,
+% then test is_owner without a gerrit repository.
+
+:- redefine(change_owner,1,(change_owner(42))).
+:- test1(is_owner(42)).
+:- test1(is_owner(X)).
+:- test0(is_owner(24)).
+
+:- end_tests_or_halt(2). % expect 2 failures
diff --git a/prologtests/examples/t3.pl b/prologtests/examples/t3.pl
new file mode 100644
index 0000000000..02badc040d
--- /dev/null
+++ b/prologtests/examples/t3.pl
@@ -0,0 +1,69 @@
+:- load([aosp_rules,utils]).
+
+:- begin_tests(t3_basic_conditions).
+
+%% A negative test of is_exempt_uploader.
+:- redefine(uploader,1,uploader(user(42))). % mocked uploader
+:- test1(uploader(user(42))).
+:- test0(is_exempt_uploader).
+
+%% Helper functions for positive test of is_exempt_uploader.
+test_is_exempt_uploader(List) :- maplist(test1_uploader, List, _).
+test1_uploader(X,_) :-
+ redefine(uploader,1,uploader(user(X))),
+ test1(uploader(user(X))),
+ test1(is_exempt_uploader).
+:- test_is_exempt_uploader([104, 106]).
+
+%% Test has_build_cop_override.
+:- redefine(commit_label,2,commit_label(label('Code-Review',1),user(102))).
+:- test0(has_build_cop_override).
+commit_label(label('Build-Cop-Override',1),user(101)). % mocked 2nd label
+:- test1(has_build_cop_override).
+:- test1(commit_label(label(_,_),_)). % expect fail, two matches
+:- test1(commit_label(label('Build-Cop-Override',_),_)). % good, one pass
+
+%% TODO: more test for is_exempt_from_reviews.
+
+%% Test needs_api_review, which checks commit_delta and project.
+% Helper functions:
+test_needs_api_review(File, Project, Tester) :-
+ redefine(commit_delta,1,(commit_delta(R) :- regex_matches(R, File))),
+ redefine(change_project,1,change_project(Project)),
+ Goal =.. [Tester, needs_api_review],
+ msg('# check CL with changed file ', File, ' in ', Project),
+ once((Goal ; true)). % do not backtrack
+
+:- test_needs_api_review('apio/test.cc', 'platform/art', test0).
+:- test_needs_api_review('api/test.cc', 'platform/art', test0).
+:- test_needs_api_review('api/test.cc', 'platform/prebuilts/sdk', test1).
+:- test_needs_api_review('d1/d2/api/test.cc', 'platform/prebuilts/sdk', test1).
+:- test_needs_api_review('system-api/d/t.c', 'platform/external/apache-http', test1).
+
+%% TODO: Test needs_drno_review, needs_qualcomm_review
+
+%% TODO: Test opt_out_find_owners.
+
+:- test1(opt_in_find_owners). % default, unless opt_out_find_owners
+
+:- end_tests_or_halt(1). % expect 1 failure of multiple commit_label
+
+%% Test remove_label
+:- begin_tests(t3_remove_label).
+
+:- test1(remove_label('MyReview',[],[])).
+:- test1(remove_label('MyReview',submit(),submit())).
+:- test1(remove_label(myR,[label(a,X)],[label(a,X)])).
+:- test1(remove_label(myR,[label(myR,_)],[])).
+:- test1(remove_label(myR,[label(a,X),label(myR,_)],[label(a,X)])).
+:- test1(remove_label(myR,submit(label(a,X)),submit(label(a,X)))).
+:- test1(remove_label(myR,submit(label(myR,_)),submit())).
+
+%% Test maplist
+double(X,Y) :- Y is X * X.
+:- test1(maplist(double, [2,4,6], [4,16,36])).
+:- test1(maplist(double, [], [])).
+
+:- end_tests_or_halt(0). % expect no failure
+
+%% TODO: Add more tests.
diff --git a/prologtests/examples/utils.pl b/prologtests/examples/utils.pl
new file mode 100644
index 0000000000..8d15067789
--- /dev/null
+++ b/prologtests/examples/utils.pl
@@ -0,0 +1,78 @@
+%% Unit test helpers
+
+% Write one line message.
+msg(A) :- write(A), nl.
+msg(A,B) :- write(A), msg(B).
+msg(A,B,C) :- write(A), msg(B,C).
+msg(A,B,C,D) :- write(A), msg(B,C,D).
+msg(A,B,C,D,E) :- write(A), msg(B,C,D,E).
+msg(A,B,C,D,E,F) :- write(A), msg(B,C,D,E,F).
+
+% Redefine a caluse.
+redefine(Atom,Arity,Clause) :- abolish(Atom/Arity), assertz(Clause).
+
+% Increment/decrement of pass/fail counters.
+set_counters(N,X,Y) :- redefine(test_count,3,test_count(N,X,Y)).
+get_counters(N,X,Y) :- clause(test_count(N,X,Y), _) -> true ; (X=0, Y=0).
+inc_pass_count :- get_counters(N,P,F), P1 is P + 1, set_counters(N,P1,F).
+inc_fail_count :- get_counters(N,P,F), F1 is F + 1, set_counters(N,P,F1).
+
+% Report pass or fail of G.
+pass_1(G) :- msg('PASS: ', G), inc_pass_count.
+fail_1(G) :- msg('FAIL: ', G), inc_fail_count.
+
+% Report pass or fail of not(G).
+pass_0(G) :- msg('PASS: not(', G, ')'), inc_pass_count.
+fail_0(G) :- msg('FAIL: not(', G, ')'), inc_fail_count.
+
+% Report a test as failed if it passed 2 or more times
+pass_twice(G) :-
+ msg('FAIL: (pass twice): ', G),
+ inc_fail_count.
+pass_many(G) :-
+ G = [A,B|_],
+ length(G, N),
+ msg('FAIL: (pass ', N, ' times): ', [A,B,'...']),
+ inc_fail_count.
+
+% Test if G fails.
+test0(G) :- once(G) -> fail_0(G) ; pass_0(G).
+
+% Test if G passes exactly once.
+test1(G) :-
+ findall(G, G, S), length(S, N),
+ (N == 0
+ -> fail_1(G)
+ ; (N == 1
+ -> pass_1(S)
+ ; (N == 2 -> pass_twice(S) ; pass_many(S))
+ )
+ ).
+
+% Report the begin of test N.
+begin_tests(N) :-
+ nl,
+ msg('BEGIN test ',N),
+ set_counters(N,0,0).
+
+% Repot the end of test N and total pass/fail counts,
+% and check if the numbers are as exected OutP/OutF.
+end_tests(OutP,OutF) :-
+ get_counters(N,P,F),
+ (OutP = P
+ -> msg('Expected #PASS: ', OutP)
+ ; (msg('ERROR: expected #PASS is ',OutP), !, fail)
+ ),
+ (OutF = F
+ -> msg('Expected #FAIL: ', OutF)
+ ; (msg('ERROR: expected #FAIL is ',OutF), !, fail)
+ ),
+ msg('END test ', N),
+ nl.
+
+% Repot the end of test N and total pass/fail counts.
+end_tests(N) :- end_tests(N,_,_).
+
+% Call end_tests/2 and halt if the fail count is unexpected.
+end_tests_or_halt(ExpectedFails) :-
+ end_tests(_,ExpectedFails); (flush_output, halt(1)).
diff --git a/proto/cache.proto b/proto/cache.proto
index b34dbf3b86..10e0216cf1 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -75,7 +75,7 @@ message ChangeNotesKeyProto {
// Instead, we just take the tedious yet simple approach of having a "has_foo"
// field for each nullable field "foo", indicating whether or not foo is null.
//
-// Next ID: 19
+// Next ID: 23
message ChangeNotesStateProto {
// Effectively required, even though the corresponding ChangeNotesState field
// is optional, since the field is only absent when NoteDb is disabled, in
@@ -110,8 +110,8 @@ message ChangeNotesStateProto {
string submission_id = 13;
bool has_submission_id = 14;
- int32 assignee = 15;
- bool has_assignee = 16;
+ reserved 15; // assignee
+ reserved 16; // has_assignee
string status = 17;
bool has_status = 18;
@@ -130,7 +130,7 @@ message ChangeNotesStateProto {
// which case attempting to use the ChangeNotesCache is programmer error.
ChangeColumnsProto columns = 3;
- repeated int32 past_assignee = 4;
+ reserved 4; // past_assignee
repeated string hashtag = 5;
@@ -178,11 +178,25 @@ message ChangeNotesStateProto {
// Raw ChangeMessage proto as produced by ChangeMessageProtoConverter.
repeated bytes change_message = 15;
- // JSON produced from com.google.gerrit.reviewdb.client.Comment.
+ // JSON produced from com.google.gerrit.entities.Comment.
repeated string published_comment = 16;
reserved 17; // read_only_until
reserved 18; // has_read_only_until
+
+ // Number of updates to the change's meta ref.
+ int32 update_count = 19;
+
+ string server_id = 20;
+ bool has_server_id = 21;
+
+ message AssigneeStatusUpdateProto {
+ int64 date = 1;
+ int32 updated_by = 2;
+ int32 current_assignee = 3;
+ bool has_current_assignee = 4;
+ }
+ repeated AssigneeStatusUpdateProto assignee_update = 22;
}
diff --git a/proto/entities.proto b/proto/entities.proto
index d2851d382f..374b47c243 100644
--- a/proto/entities.proto
+++ b/proto/entities.proto
@@ -18,19 +18,19 @@ package devtools.gerritcodereview;
option java_package = "com.google.gerrit.proto";
-// Serialized form of com.google.gerrit.reviewdb.client.Change.Id.
+// Serialized form of com.google.gerrit.entities.Change.Id.
// Next ID: 2
message Change_Id {
required int32 id = 1;
}
-// Serialized form of com.google.gerrit.reviewdb.client.Change.Key.
+// Serialized form of com.google.gerrit.entities.Change.Key.
// Next ID: 2
message Change_Key {
optional string id = 1;
}
-// Serialized form of com.google.gerrit.reviewdb.client.Change.
+// Serialized form of com.google.gerrit.entities.Change.
// Next ID: 24
message Change {
required Change_Id change_id = 1;
@@ -61,14 +61,14 @@ message Change {
reserved 101; // note_db_state
}
-// Serialized form of com.google.gerrit.reviewdb.client.ChangeMessage.
+// Serialized form of com.google.gerrit.enities.ChangeMessage.
// Next ID: 3
message ChangeMessage_Key {
required Change_Id change_id = 1;
required string uuid = 2;
}
-// Serialized form of com.google.gerrit.reviewdb.client.ChangeMessage.
+// Serialized form of com.google.gerrit.entities.ChangeMessage.
// Next ID: 8
message ChangeMessage {
required ChangeMessage_Key key = 1;
@@ -80,18 +80,18 @@ message ChangeMessage {
optional Account_Id real_author = 7;
}
-// Serialized form of com.google.gerrit.reviewdb.client.PatchSet.Id.
+// Serialized form of com.google.gerrit.entities.PatchSet.Id.
// Next ID: 3
message PatchSet_Id {
required Change_Id change_id = 1;
- required int32 patch_set_id = 2;
+ required int32 id = 2;
}
-// Serialized form of com.google.gerrit.reviewdb.client.PatchSet.
+// Serialized form of com.google.gerrit.entities.PatchSet.
// Next ID: 10
message PatchSet {
required PatchSet_Id id = 1;
- optional RevId revision = 2;
+ optional ObjectId commitId = 2;
optional Account_Id uploader_account_id = 3;
optional fixed64 created_on = 4;
optional string groups = 6;
@@ -103,27 +103,27 @@ message PatchSet {
reserved 7; // pushCertficate
}
-// Serialized form of com.google.gerrit.reviewdb.client.Account.Id.
+// Serialized form of com.google.gerrit.entities.Account.Id.
// Next ID: 2
message Account_Id {
required int32 id = 1;
}
-// Serialized form of com.google.gerrit.reviewdb.client.LabelId.
+// Serialized form of com.google.gerrit.entities.LabelId.
// Next ID: 2
message LabelId {
required string id = 1;
}
-// Serialized form of com.google.gerrit.reviewdb.client.PatchSetApproval.Key.
+// Serialized form of com.google.gerrit.entities.PatchSetApproval.Key.
// Next ID: 4
message PatchSetApproval_Key {
required PatchSet_Id patch_set_id = 1;
required Account_Id account_id = 2;
- required LabelId category_id = 3;
+ required LabelId label_id = 3;
}
-// Serialized form of com.google.gerrit.reviewdb.client.PatchSetApproval.
+// Serialized form of com.google.gerrit.entities.PatchSetApproval.
// Next ID: 9
message PatchSetApproval {
required PatchSetApproval_Key key = 1;
@@ -138,21 +138,22 @@ message PatchSetApproval {
reserved 5; // changeSortKey
}
-// Serialized form of com.google.gerrit.reviewdb.client.Project.NameKey.
+// Serialized form of com.google.gerrit.entities.Project.NameKey.
// Next ID: 2
message Project_NameKey {
optional string name = 1;
}
-// Serialized form of com.google.gerrit.reviewdb.client.Branch.NameKey.
+// Serialized form of com.google.gerrit.entities.Branch.NameKey.
// Next ID: 3
message Branch_NameKey {
- optional Project_NameKey project_name = 1;
- optional string branch_name = 2;
+ optional Project_NameKey project = 1;
+ optional string branch = 2;
}
-// Serialized form of com.google.gerrit.reviewdb.client.RevId.
+// Serialized form of org.eclipse.jgit.lib.ObjectId.
// Next ID: 2
-message RevId {
- optional string id = 1;
+message ObjectId {
+ // Hex string representation of the ID.
+ optional string name = 1;
}
diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index 8f151a8c8c..3feb1b452e 100644
--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -19,11 +19,14 @@
{template .Index}
{@param canonicalPath: ?}
{@param staticResourcePath: ?}
+ {@param gerritInitialData: /** {string} map of REST endpoint to response for startup. */ ?}
{@param? assetsPath: ?} /** {string} URL to static assets root, if served from CDN. */
{@param? assetsBundle: ?} /** {string} Assets bundle .html file, served from $assetsPath. */
{@param? faviconPath: ?}
{@param? versionInfo: ?}
- {@param? polymer2: ?}
+ {@param? polyfillCE: ?}
+ {@param? polyfillSD: ?}
+ {@param? polyfillSC: ?}
<!DOCTYPE html>{\n}
<html lang="en">{\n}
<meta charset="utf-8">{\n}
@@ -36,12 +39,27 @@
</noscript>
<script>
+ // Disable extra font load from paper-styles
+ window.polymerSkipLoadingFontRoboto = true;
window.CLOSURE_NO_DEPS = true;
{if $canonicalPath != ''}window.CANONICAL_PATH = '{$canonicalPath}';{/if}
{if $versionInfo}window.VERSION_INFO = '{$versionInfo}';{/if}
{if $staticResourcePath != ''}window.STATIC_RESOURCE_PATH = '{$staticResourcePath}';{/if}
{if $assetsPath}window.ASSETS_PATH = '{$assetsPath}';{/if}
- {if $polymer2}window.POLYMER2 = true;{/if}
+ {if $polyfillCE}if (window.customElements) window.customElements.forcePolyfill = true;{/if}
+ {if $polyfillSD}{literal}ShadyDOM = { force: true };{/literal}{/if}
+ {if $polyfillSC}{literal}ShadyCSS = { shimcssproperties: true};{/literal}{/if}
+ {if $gerritInitialData}
+ // INITIAL_DATA is a string that represents a JSON map. It's inlined here so that we can
+ // spare calls to the API when starting up the app.
+ // The map maps from endpoint to returned value. This matches Gerrit's REST API 1:1, so the
+ // values here can be used as a drop-in replacement for calls to the API.
+ //
+ // Example:
+ // '/config/server/version' => '3.0.0-468-g0757b52a7d'
+ // '/accounts/self/detail' => { 'username' : 'gerrit-user' }
+ window.INITIAL_DATA = JSON.parse({$gerritInitialData});
+ {/if}
</script>{\n}
{if $faviconPath}
@@ -60,7 +78,9 @@
<link rel="preload" href="{$staticResourcePath}/fonts/Roboto-Medium.woff" as="font" type="font/woff" crossorigin="anonymous">{\n}
<link rel="stylesheet" href="{$staticResourcePath}/styles/fonts.css">{\n}
<link rel="stylesheet" href="{$staticResourcePath}/styles/main.css">{\n}
+
<script src="{$staticResourcePath}/bower_components/webcomponentsjs/webcomponents-lite.js"></script>{\n}
+
// Content between webcomponents-lite and the load of the main app element
// run before polymer-resin is installed so may have security consequences.
// Contact your local security engineer if you have any questions, and
diff --git a/resources/com/google/gerrit/pgm/init/gerrit.sh b/resources/com/google/gerrit/pgm/init/gerrit.sh
index d92ec51849..ce858d5b80 100755
--- a/resources/com/google/gerrit/pgm/init/gerrit.sh
+++ b/resources/com/google/gerrit/pgm/init/gerrit.sh
@@ -345,8 +345,9 @@ fi
test -z "$GERRIT_USER" && GERRIT_USER=`whoami`
RUN_ARGS="-jar $GERRIT_WAR daemon -d $GERRIT_SITE"
-if test "`get_config --bool container.slave`" = "true" ; then
- RUN_ARGS="$RUN_ARGS --slave --enable-httpd --headless"
+if test "`get_config --bool container.slave`" = "true" || \
+ test "`get_config --bool container.replica`" = "true"; then
+ RUN_ARGS="$RUN_ARGS --replica --enable-httpd --headless"
fi
DAEMON_OPTS=`get_config --get-all container.daemonOpt`
if test -n "$DAEMON_OPTS" ; then
diff --git a/resources/com/google/gerrit/server/mail/Abandoned.soy b/resources/com/google/gerrit/server/mail/Abandoned.soy
index 2785ffc8e7..d5aac0edc3 100644
--- a/resources/com/google/gerrit/server/mail/Abandoned.soy
+++ b/resources/com/google/gerrit/server/mail/Abandoned.soy
@@ -17,7 +17,7 @@
{namespace com.google.gerrit.server.mail.template}
/**
- * .Abandoned template will determine the contents of the email related to a
+ * The .Abandoned template will determine the contents of the email related to a
* change being abandoned.
*/
{template .Abandoned kind="text"}
diff --git a/resources/com/google/gerrit/server/mail/InboundEmailRejection.soy b/resources/com/google/gerrit/server/mail/InboundEmailRejection.soy
index e99777694a..e88c424ac8 100644
--- a/resources/com/google/gerrit/server/mail/InboundEmailRejection.soy
+++ b/resources/com/google/gerrit/server/mail/InboundEmailRejection.soy
@@ -62,3 +62,8 @@
This might be caused by an ongoing maintenance or a data corruption.
{call .InboundEmailRejectionFooter /}
{/template}
+
+{template .InboundEmailRejection_COMMENT_REJECTED kind="text"}
+ Gerrit Code Review rejected one or more comments because they did not pass validation.
+ {call .InboundEmailRejectionFooter /}
+{/template}
diff --git a/resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy b/resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy
index f879270ed2..e17508de37 100644
--- a/resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy
+++ b/resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy
@@ -78,3 +78,10 @@
<p>
{call .InboundEmailRejectionFooterHtml /}
{/template}
+
+{template .InboundEmailRejectionHtml_COMMENT_REJECTED}
+ <p>
+ Gerrit Code Review rejected one or more comments because they did not pass validation.
+ </p>
+ {call .InboundEmailRejectionFooterHtml /}
+{/template}
diff --git a/tools/BUILD b/tools/BUILD
index 9ccc0651f4..f5da450f06 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -21,72 +21,75 @@ default_java_toolchain(
# enabled. This warnings list is originally based on:
# https://github.com/bazelbuild/BUILD_file_generator/blob/master/tools/bazel_defs/java.bzl
# However, feel free to add any additional errors. Thus far they have all been pretty useful.
+# TODO(davido): Enable ImmutableAnnotationChecker again when these issues are fixed:
+# https://github.com/google/error-prone/issues/1348
+# https://github.com/bazelbuild/bazel/issues/9378
java_package_configuration(
name = "error_prone",
javacopts = [
"-XepDisableWarningsInGeneratedCode",
- "-Xep:AmbiguousMethodReference:WARN",
+ "-Xep:AmbiguousMethodReference:ERROR",
"-Xep:AutoValueFinalMethods:ERROR",
- "-Xep:BadAnnotationImplementation:WARN",
- "-Xep:BadComparable:WARN",
+ "-Xep:BadAnnotationImplementation:ERROR",
+ "-Xep:BadComparable:ERROR",
"-Xep:BoxedPrimitiveConstructor:ERROR",
- "-Xep:CannotMockFinalClass:WARN",
+ "-Xep:CannotMockFinalClass:ERROR",
"-Xep:ClassCanBeStatic:ERROR",
- "-Xep:ClassNewInstance:WARN",
+ "-Xep:ClassNewInstance:ERROR",
"-Xep:DateFormatConstant:ERROR",
"-Xep:DefaultCharset:ERROR",
- "-Xep:DoubleCheckedLocking:WARN",
- "-Xep:ElementsCountedInLoop:WARN",
- "-Xep:EqualsHashCode:WARN",
- "-Xep:EqualsIncompatibleType:WARN",
- "-Xep:ExpectedExceptionChecker:OFF",
- "-Xep:Finally:WARN",
- "-Xep:FloatingPointLiteralPrecision:WARN",
- "-Xep:FragmentInjection:WARN",
- "-Xep:FragmentNotInstantiable:WARN",
+ "-Xep:DoubleCheckedLocking:ERROR",
+ "-Xep:ElementsCountedInLoop:ERROR",
+ "-Xep:EqualsHashCode:ERROR",
+ "-Xep:EqualsIncompatibleType:ERROR",
+ "-Xep:ExpectedExceptionChecker:ERROR",
+ "-Xep:Finally:ERROR",
+ "-Xep:FloatingPointLiteralPrecision:ERROR",
+ "-Xep:FragmentInjection:ERROR",
+ "-Xep:FragmentNotInstantiable:ERROR",
"-Xep:FunctionalInterfaceClash:ERROR",
- "-Xep:FutureReturnValueIgnored:WARN",
- "-Xep:GetClassOnEnum:WARN",
- "-Xep:ImmutableAnnotationChecker:WARN",
- "-Xep:ImmutableEnumChecker:WARN",
- "-Xep:IncompatibleModifiers:WARN",
- "-Xep:InjectOnConstructorOfAbstractClass:WARN",
- "-Xep:InputStreamSlowMultibyteRead:WARN",
- "-Xep:IterableAndIterator:WARN",
- "-Xep:JUnit3FloatingPointComparisonWithoutDelta:WARN",
- "-Xep:JUnitAmbiguousTestClass:WARN",
- "-Xep:LiteralClassName:WARN",
+ "-Xep:FutureReturnValueIgnored:ERROR",
+ "-Xep:GetClassOnEnum:ERROR",
+ "-Xep:ImmutableAnnotationChecker:OFF",
+ "-Xep:ImmutableEnumChecker:ERROR",
+ "-Xep:IncompatibleModifiers:ERROR",
+ "-Xep:InjectOnConstructorOfAbstractClass:ERROR",
+ "-Xep:InputStreamSlowMultibyteRead:ERROR",
+ "-Xep:IterableAndIterator:ERROR",
+ "-Xep:JUnit3FloatingPointComparisonWithoutDelta:ERROR",
+ "-Xep:JUnitAmbiguousTestClass:ERROR",
+ "-Xep:LiteralClassName:ERROR",
"-Xep:MissingCasesInEnumSwitch:ERROR",
"-Xep:MissingFail:ERROR",
- "-Xep:MissingOverride:WARN",
+ "-Xep:MissingOverride:ERROR",
"-Xep:MutableConstantField:ERROR",
- "-Xep:NarrowingCompoundAssignment:WARN",
- "-Xep:NonAtomicVolatileUpdate:WARN",
- "-Xep:NonOverridingEquals:WARN",
- "-Xep:NullableConstructor:WARN",
- "-Xep:NullablePrimitive:WARN",
- "-Xep:NullableVoid:WARN",
+ "-Xep:NarrowingCompoundAssignment:ERROR",
+ "-Xep:NonAtomicVolatileUpdate:ERROR",
+ "-Xep:NonOverridingEquals:ERROR",
+ "-Xep:NullableConstructor:ERROR",
+ "-Xep:NullablePrimitive:ERROR",
+ "-Xep:NullableVoid:ERROR",
"-Xep:ObjectToString:ERROR",
"-Xep:OperatorPrecedence:ERROR",
- "-Xep:OverridesGuiceInjectableMethod:WARN",
- "-Xep:PreconditionsInvalidPlaceholder:WARN",
- "-Xep:ProtoFieldPreconditionsCheckNotNull:WARN",
- "-Xep:ProtocolBufferOrdinal:WARN",
- "-Xep:ReferenceEquality:WARN",
- "-Xep:RequiredModifiers:WARN",
- "-Xep:ShortCircuitBoolean:WARN",
- "-Xep:SimpleDateFormatConstant:WARN",
- "-Xep:StaticGuardedByInstance:WARN",
- "-Xep:StringEquality:WARN",
- "-Xep:SynchronizeOnNonFinalField:WARN",
- "-Xep:TruthConstantAsserts:WARN",
- "-Xep:TypeParameterShadowing:WARN",
- "-Xep:TypeParameterUnusedInFormals:WARN",
- "-Xep:URLEqualsHashCode:WARN",
- "-Xep:UnsynchronizedOverridesSynchronized:WARN",
+ "-Xep:OverridesGuiceInjectableMethod:ERROR",
+ "-Xep:PreconditionsInvalidPlaceholder:ERROR",
+ "-Xep:ProtoFieldPreconditionsCheckNotNull:ERROR",
+ "-Xep:ProtocolBufferOrdinal:ERROR",
+ "-Xep:ReferenceEquality:ERROR",
+ "-Xep:RequiredModifiers:ERROR",
+ "-Xep:ShortCircuitBoolean:ERROR",
+ "-Xep:SimpleDateFormatConstant:ERROR",
+ "-Xep:StaticGuardedByInstance:ERROR",
+ "-Xep:StringEquality:ERROR",
+ "-Xep:SynchronizeOnNonFinalField:ERROR",
+ "-Xep:TruthConstantAsserts:ERROR",
+ "-Xep:TypeParameterShadowing:ERROR",
+ "-Xep:TypeParameterUnusedInFormals:ERROR",
+ "-Xep:URLEqualsHashCode:ERROR",
+ "-Xep:UnsynchronizedOverridesSynchronized:ERROR",
"-Xep:UnusedException:ERROR",
- "-Xep:WaitNotInLoop:WARN",
- "-Xep:WildcardImport:WARN",
+ "-Xep:WaitNotInLoop:ERROR",
+ "-Xep:WildcardImport:ERROR",
],
packages = ["error_prone_packages"],
)
@@ -98,10 +101,14 @@ package_group(
"//javatests/...",
"//plugins/codemirror-editor/...",
"//plugins/commit-message-length-validator/...",
+ "//plugins/delete-project/...",
"//plugins/download-commands/...",
+ "//plugins/gitiles/...",
"//plugins/hooks/...",
+ "//plugins/plugin-manager/...",
"//plugins/replication/...",
"//plugins/reviewnotes/...",
"//plugins/singleusergroup/...",
+ "//plugins/webhooks/...",
],
)
diff --git a/tools/bzl/javadoc.bzl b/tools/bzl/javadoc.bzl
index 3add0258ce..62b40109e3 100644
--- a/tools/bzl/javadoc.bzl
+++ b/tools/bzl/javadoc.bzl
@@ -61,14 +61,14 @@ def _impl(ctx):
command = " && ".join(cmd),
)
-java_doc = rule(
+_java_doc = rule(
attrs = {
"external_docs": attr.string_list(),
"libs": attr.label_list(allow_files = False),
"pkgs": attr.string_list(),
"title": attr.string(),
"_jdk": attr.label(
- default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
+ default = Label("@bazel_tools//tools/jdk:current_host_java_runtime"),
allow_files = True,
providers = [java_common.JavaRuntimeInfo],
),
@@ -76,3 +76,16 @@ java_doc = rule(
outputs = {"zip": "%{name}.zip"},
implementation = _impl,
)
+
+def java_doc(**kwargs):
+ libs = kwargs.get("libs", [])
+ libs = libs + select({
+ "//:java11": [],
+ "//:java_next": [],
+ # TODO(davido): Remove this dependency, when Java 8 support is removed.
+ # auto-value generates @javax.annotation.Generated annotation on generated
+ # classes when Java 8 source compatibility level is used, but Java 11 and
+ # later don't have this class any more.
+ "//conditions:default": ["//lib:javax-annotation"],
+ })
+ _java_doc(**dict(kwargs, libs = libs))
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index 9160f1dee0..b428a2d903 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -310,6 +310,13 @@ def _bundle_impl(ctx):
destdir = ctx.outputs.html.path + ".dir"
zips = [z for d in ctx.attr.deps for z in d[ComponentInfo].transitive_zipfiles.to_list()]
+ # We are splitting off the package dir from the app.path such that
+ # we can set the package dir as the root for the bundler, which means
+ # that absolute imports are interpreted relative to that root.
+ pkg_dir = ctx.attr.pkg.lstrip("/")
+ app_path = ctx.file.app.path
+ app_path = app_path[app_path.index(pkg_dir) + len(pkg_dir):]
+
hermetic_npm_binary = " ".join([
"python",
"$p/" + ctx.file._run_npm.path,
@@ -320,10 +327,11 @@ def _bundle_impl(ctx):
"--strip-comments",
"--out-file",
"$p/" + bundled.path,
- ctx.file.app.path,
+ "--root",
+ pkg_dir,
+ app_path,
])
- pkg_dir = ctx.attr.pkg.lstrip("/")
cmd = " && ".join([
# unpack dependencies.
"export PATH",
@@ -480,8 +488,8 @@ def polygerrit_plugin(name, app, srcs = [], deps = [], externs = [], assets = No
name = name + "_bin",
compilation_level = "WHITESPACE_ONLY",
defs = [
- "--polymer_version=1",
- "--language_out=ECMASCRIPT6",
+ "--polymer_version=2",
+ "--language_out=ECMASCRIPT_2017",
"--rewrite_polyfills=false",
],
deps = [
diff --git a/tools/bzl/junit.bzl b/tools/bzl/junit.bzl
index 1cf82ea975..66d72303c1 100644
--- a/tools/bzl/junit.bzl
+++ b/tools/bzl/junit.bzl
@@ -70,7 +70,6 @@ POST_JDK8_OPTS = [
# Enforce JDK 8 compatibility on Java 9, see
# https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm#JSINT-GUID-AF5AECA7-07C1-4E7D-BC10-BC7E73DC6C7F
"-Djava.locale.providers=COMPAT,CLDR,SPI",
- "--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED",
]
def junit_tests(name, srcs, **kwargs):
@@ -82,7 +81,7 @@ def junit_tests(name, srcs, **kwargs):
)
jvm_flags = kwargs.get("jvm_flags", [])
jvm_flags = jvm_flags + select({
- "//:java9": POST_JDK8_OPTS,
+ "//:java11": POST_JDK8_OPTS,
"//:java_next": POST_JDK8_OPTS,
"//conditions:default": [],
})
diff --git a/tools/bzl/maven_jar.bzl b/tools/bzl/maven_jar.bzl
index 177e80b0b3..5bc181b24a 100644
--- a/tools/bzl/maven_jar.bzl
+++ b/tools/bzl/maven_jar.bzl
@@ -8,6 +8,10 @@ MAVEN_LOCAL = "MAVEN_LOCAL:"
ECLIPSE = "ECLIPSE:"
+MAVEN_SNAPSHOT = "https://oss.sonatype.org/content/repositories/snapshots"
+
+SNAPSHOT = "-SNAPSHOT-"
+
def _maven_release(ctx, parts):
"""induce jar and url name from maven coordinates."""
if len(parts) not in [3, 4]:
@@ -20,9 +24,25 @@ def _maven_release(ctx, parts):
group, artifact, version = parts
file_version = version
+ repository = ctx.attr.repository
+
+ if "-SNAPSHOT-" in version:
+ start = version.index(SNAPSHOT)
+ end = start + len(SNAPSHOT) - 1
+
+ # file version without snapshot constant, but with post snapshot suffix
+ file_version = version[:start] + version[end:]
+
+ # version without post snapshot suffix
+ version = version[:end]
+
+ # overwrite the repository with Maven snapshot repository
+ repository = MAVEN_SNAPSHOT
+
jar = artifact.lower() + "-" + file_version
+
url = "/".join([
- ctx.attr.repository,
+ repository,
group.replace(".", "/"),
artifact,
version,
diff --git a/tools/bzl/plugin.bzl b/tools/bzl/plugin.bzl
index 0387510b85..7534501dcc 100644
--- a/tools/bzl/plugin.bzl
+++ b/tools/bzl/plugin.bzl
@@ -22,6 +22,7 @@ def gerrit_plugin(
manifest_entries = [],
dir_name = None,
target_suffix = "",
+ deploy_env = [],
**kwargs):
java_library(
name = name + "__plugin",
@@ -47,6 +48,7 @@ def gerrit_plugin(
runtime_deps = [
":%s__plugin" % name,
] + static_jars,
+ deploy_env = deploy_env,
visibility = ["//visibility:public"],
**kwargs
)
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 649f7da781..9915a6e213 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -172,13 +172,23 @@ def gen_classpath(ext):
impl = xml.dom.minidom.getDOMImplementation()
return impl.createDocument(None, 'classpath', None)
- def classpathentry(kind, path, src=None, out=None, exported=None):
+ def import_jgit_sources():
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit/src')
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit/resources')
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit.archive/src',
+ excluding='org/eclipse/jgit/archive/FormatActivator.java')
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit.archive/resources')
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit.http.server/src')
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit.http.server/resources')
+ classpathentry('src', 'modules/jgit/org.eclipse.jgit.junit/src')
+
+ def classpathentry(kind, path, src=None, out=None, exported=None, excluding=None):
e = doc.createElement('classpathentry')
e.setAttribute('kind', kind)
# Excluding the BUILD file, to avoid the Eclipse warnings:
# "The resource is a duplicate of ..."
if kind == 'src':
- e.setAttribute('excluding', '**/BUILD')
+ e.setAttribute('excluding', '**/BUILD' if not excluding else excluding)
e.setAttribute('path', path)
if src:
e.setAttribute('sourcepath', src)
@@ -193,7 +203,7 @@ def gen_classpath(ext):
testAtt.setAttribute('name', 'test')
testAtt.setAttribute('value', 'true')
atts.appendChild(testAtt)
- if "apt_generated" in path:
+ if "apt_generated" in path or "modules/jgit" in path:
if not atts:
atts = doc.createElement('attributes')
ignoreOptionalProblems = doc.createElement('attribute')
@@ -216,6 +226,7 @@ def gen_classpath(ext):
# Classpath entries are absolute for cross-cell support
java_library = re.compile('bazel-out/.*?-fastbuild/bin/(.*)/[^/]+[.]jar$')
+ proto_library = re.compile('bazel-out/.*?-fastbuild/bin/(.*)proto/(.*)_proto-speed[.]jar$')
srcs = re.compile('(.*/external/[^/]+)/jar/(.*)[.]jar')
for p in _query_classpath(MAIN):
if p.endswith('-src.jar'):
@@ -230,11 +241,7 @@ def gen_classpath(ext):
p.endswith('com_google_protobuf/libprotobuf_java.jar') or \
p.endswith('lucene-core-and-backward-codecs-merged_deploy.jar'):
lib.add(p)
- # JGit dependency from external repository
- if 'gerrit-' not in p and 'jgit' in p:
- lib.add(p)
- # Assume any jars in /proto/ are from java_proto_library rules
- if '/bin/proto/' in p:
+ if proto_library.match(p) :
proto.add(p)
else:
# Don't mess up with Bazel internal test runner dependencies.
@@ -249,6 +256,7 @@ def gen_classpath(ext):
classpathentry('src', 'java')
classpathentry('src', 'javatests', out='eclipse-out/test')
classpathentry('src', 'resources')
+ import_jgit_sources()
for s in sorted(src):
out = None
@@ -296,8 +304,8 @@ def gen_classpath(ext):
classpathentry('lib', j, s)
for p in sorted(proto):
- s = p.replace('-fastbuild/bin/proto/lib', '-fastbuild/genfiles/proto/')
- s = p.replace('-fastbuild/bin/proto/testing/lib', '-fastbuild/genfiles/proto/testing/')
+ s = p.replace('/proto/lib', '/proto/')
+ s = s.replace('/proto/testing/lib', '/proto/testing/')
s = s.replace('.jar', '-src.jar')
classpathentry('lib', p, s)
diff --git a/tools/maven/gerrit-acceptance-framework_pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
index 0ce6d4e5b0..a62ac3af1a 100644
--- a/tools/maven/gerrit-acceptance-framework_pom.xml
+++ b/tools/maven/gerrit-acceptance-framework_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-acceptance-framework</artifactId>
- <version>3.0.14-SNAPSHOT</version>
+ <version>3.1.9-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Acceptance Test Framework</name>
<description>Framework for Gerrit's acceptance tests</description>
diff --git a/tools/maven/gerrit-extension-api_pom.xml b/tools/maven/gerrit-extension-api_pom.xml
index 32c43696ef..8248ef2ca6 100644
--- a/tools/maven/gerrit-extension-api_pom.xml
+++ b/tools/maven/gerrit-extension-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-extension-api</artifactId>
- <version>3.0.14-SNAPSHOT</version>
+ <version>3.1.9-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Extension API</name>
<description>API for Gerrit Extensions</description>
diff --git a/tools/maven/gerrit-plugin-api_pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
index 0f57b921da..a9e26b0dd9 100644
--- a/tools/maven/gerrit-plugin-api_pom.xml
+++ b/tools/maven/gerrit-plugin-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-plugin-api</artifactId>
- <version>3.0.14-SNAPSHOT</version>
+ <version>3.1.9-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Plugin API</name>
<description>API for Gerrit Plugins</description>
diff --git a/tools/maven/gerrit-war_pom.xml b/tools/maven/gerrit-war_pom.xml
index 50619250f0..6c8f834603 100644
--- a/tools/maven/gerrit-war_pom.xml
+++ b/tools/maven/gerrit-war_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-war</artifactId>
- <version>3.0.14-SNAPSHOT</version>
+ <version>3.1.9-SNAPSHOT</version>
<packaging>war</packaging>
<name>Gerrit Code Review - WAR</name>
<description>Gerrit WAR</description>
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index e4faf18c21..eceead475e 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -17,38 +17,46 @@ def declare_nongoogle_deps():
# Transitive dependency of commons-compress
maven_jar(
name = "tukaani-xz",
- artifact = "org.tukaani:xz:1.6",
- sha1 = "05b6f921f1810bdf90e25471968f741f87168b64",
+ artifact = "org.tukaani:xz:1.8",
+ sha1 = "c4f7d054303948eb6a4066194253886c8af07128",
)
maven_jar(
name = "dropwizard-core",
- artifact = "io.dropwizard.metrics:metrics-core:4.0.5",
- sha1 = "b81ef162970cdb9f4512ee2da09715a856ff4c4c",
+ artifact = "io.dropwizard.metrics:metrics-core:4.0.7",
+ sha1 = "673899f605f52ca35836673ccfee97154a496a61",
)
+ SSHD_VERS = "2.3.0"
+
maven_jar(
name = "sshd",
- artifact = "org.apache.sshd:sshd-core:2.0.0",
- sha1 = "f4275079a2463cfd2bf1548a80e1683288a8e86b",
+ artifact = "org.apache.sshd:sshd-core:" + SSHD_VERS,
+ sha1 = "21aeea9deba96c9b81ea0935fa4fac61aa3cf646",
+ )
+
+ maven_jar(
+ name = "sshd-common",
+ artifact = "org.apache.sshd:sshd-common:" + SSHD_VERS,
+ sha1 = "8b6e3baaa0d35b547696965eef3e62477f5e74c9",
)
maven_jar(
name = "eddsa",
- artifact = "net.i2p.crypto:eddsa:0.2.0",
- sha1 = "0856a92559c4daf744cb27c93cd8b7eb1f8c4780",
+ artifact = "net.i2p.crypto:eddsa:0.3.0",
+ sha1 = "1901c8d4d8bffb7d79027686cfb91e704217c3e1",
)
maven_jar(
name = "mina-core",
- artifact = "org.apache.mina:mina-core:2.0.17",
- sha1 = "7e10ec974760436d931f3e58be507d1957bcc8db",
+ artifact = "org.apache.mina:mina-core:2.0.21",
+ sha1 = "e1a317689ecd438f54e863747e832f741ef8e092",
)
maven_jar(
name = "sshd-mina",
- artifact = "org.apache.sshd:sshd-mina:2.0.0",
- sha1 = "50f2669312494f6c1996d8bd0d266c1fca7be6f6",
+ artifact = "org.apache.sshd:sshd-mina:" + SSHD_VERS,
+ sha1 = "55dc0830dfcbceba01f9460812ee454978a15fe8",
)
# elasticsearch-rest-client explicitly depends on this version
@@ -141,50 +149,6 @@ def declare_nongoogle_deps():
sha1 = "dc13ae4faca6df981fc7aeb5a522d9db446d5d50",
)
- POWERM_VERS = "1.6.1"
-
- maven_jar(
- name = "powermock-module-junit4",
- artifact = "org.powermock:powermock-module-junit4:" + POWERM_VERS,
- sha1 = "ea8530b2848542624f110a393513af397b37b9cf",
- )
-
- maven_jar(
- name = "powermock-module-junit4-common",
- artifact = "org.powermock:powermock-module-junit4-common:" + POWERM_VERS,
- sha1 = "7222ced54dabc310895d02e45c5428ca05193cda",
- )
-
- maven_jar(
- name = "powermock-reflect",
- artifact = "org.powermock:powermock-reflect:" + POWERM_VERS,
- sha1 = "97d25eda8275c11161bcddda6ef8beabd534c878",
- )
-
- maven_jar(
- name = "powermock-api-easymock",
- artifact = "org.powermock:powermock-api-easymock:" + POWERM_VERS,
- sha1 = "aa740ecf89a2f64d410b3d93ef8cd6833009ef00",
- )
-
- maven_jar(
- name = "powermock-api-support",
- artifact = "org.powermock:powermock-api-support:" + POWERM_VERS,
- sha1 = "592ee6d929c324109d3469501222e0c76ccf0869",
- )
-
- maven_jar(
- name = "powermock-core",
- artifact = "org.powermock:powermock-core:" + POWERM_VERS,
- sha1 = "5afc1efce8d44ed76b30af939657bd598e45d962",
- )
-
- maven_jar(
- name = "javassist",
- artifact = "org.javassist:javassist:3.22.0-GA",
- sha1 = "3e83394258ae2089be7219b971ec21a8288528ad",
- )
-
TESTCONTAINERS_VERSION = "1.14.3"
maven_jar(
diff --git a/tools/workspace_status_release.py b/tools/workspace_status_release.py
new file mode 100644
index 0000000000..36535fb70f
--- /dev/null
+++ b/tools/workspace_status_release.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+
+# This is a variant of the `workspace_status.py` script that in addition to
+# plain `git describe` implements a few heuristics to arrive at more to the
+# point stamps for directories. But due to the implemented heuristics, it will
+# typically take longer to run (especially if you use lots of plugins that
+# come without tags) and might slow down your development cycle when used
+# as default.
+#
+# To use it, simply add
+#
+# --workspace_status_command="python ./tools/workspace_status_release.py"
+#
+# to your bazel command. So for example instead of
+#
+# bazel build release.war
+#
+# use
+#
+# bazel build --workspace_status_command="python ./tools/workspace_status_release.py" release.war
+#
+# Alternatively, you can add
+#
+# build --workspace_status_command="python ./tools/workspace_status_release.py"
+#
+# to `.bazelrc` in your home directory.
+#
+# If the script exits with non-zero code, it's considered as a failure
+# and the output will be discarded.
+
+from __future__ import print_function
+import os
+import subprocess
+import sys
+import re
+
+ROOT = os.path.abspath(__file__)
+while not os.path.exists(os.path.join(ROOT, 'WORKSPACE')):
+ ROOT = os.path.dirname(ROOT)
+REVISION_CMD = ['git', 'describe', '--always', '--dirty']
+
+
+def run(command):
+ try:
+ return subprocess.check_output(command).strip().decode("utf-8")
+ except OSError as err:
+ print('could not invoke %s: %s' % (command[0], err), file=sys.stderr)
+ sys.exit(1)
+ except subprocess.CalledProcessError:
+ # ignore "not a git repository error" to report unknown version
+ return None
+
+
+def revision_with_match(pattern=None, prefix=False, all_refs=False,
+ return_unmatched=False):
+ """Return a description of the current commit
+
+ Keyword arguments:
+ pattern -- (Default: None) Use only refs that match this pattern.
+ prefix -- (Default: False) If True, the pattern is considered a prefix
+ and does not require an exact match.
+ all_refs -- (Default: False) If True, consider all refs, not just tags.
+ return_unmatched -- (Default: False) If False and a pattern is given that
+ cannot be matched, return the empty string. If True, return
+ the unmatched description nonetheless.
+ """
+
+ command = REVISION_CMD[:]
+ if pattern:
+ command += ['--match', pattern + ('*' if prefix else '')]
+ if all_refs:
+ command += ['--all', '--long']
+
+ description = run(command)
+
+ if pattern and not return_unmatched and not description.startswith(pattern):
+ return ''
+ return description
+
+
+def branch_with_match(pattern):
+ for ref_kind in ['origin/', 'gerrit/', '']:
+ description = revision_with_match(ref_kind + pattern, all_refs=True,
+ return_unmatched=True)
+ for cutoff in ['heads/', 'remotes/', ref_kind]:
+ if description.startswith(cutoff):
+ description = description[len(cutoff):]
+ if description.startswith(pattern):
+ return description
+ return ''
+
+
+def revision(template=None):
+ if template:
+ # We use the version `v2.16.19-1-gec686a6352` as running example for the
+ # below comments. First, we split into ['v2', '16', '19']
+ parts = template.split('-')[0].split('.')
+
+ # Although we have releases with version tags containing 4 numbers, we
+ # treat only the first three numbers for simplicity. See discussion on
+ # Ib1681b2730cf2c443a3cb55fe6e282f6484e18de.
+
+ if len(parts) >= 3:
+ # Match for v2.16.19
+ version_part = '.'.join(parts[0:3])
+ description = revision_with_match(version_part)
+ if description:
+ return description
+
+ if len(parts) >= 2:
+ version_part = '.'.join(parts[0:2])
+
+ # Match for v2.16.*
+ description = revision_with_match(version_part + '.', prefix=True)
+ if description:
+ return description
+
+ # Match for v2.16
+ description = revision_with_match(version_part)
+ if description.startswith(version_part):
+ return description
+
+ if template.startswith('v'):
+ # Match for stable-2.16 branches
+ branch = 'stable-' + version_part[1:]
+ description = branch_with_match(branch)
+ if description:
+ return description
+
+ # None of the template based methods worked out, so we're falling back to
+ # generic matches.
+
+ # Match for master branch
+ description = branch_with_match('master')
+ if description:
+ return description
+
+ # Match for anything that looks like a version tag
+ description = revision_with_match('v[0-9].', return_unmatched=True)
+ if description.startswith('v'):
+ return description
+
+ # Still no good tag, so we re-try without any matching
+ return revision_with_match()
+
+
+# prints the stamps for the current working directory
+def print_stamps_for_cwd(name, template):
+ workspace_status_script = os.path.join(
+ 'tools', 'workspace_status_release.py')
+ if os.path.isfile(workspace_status_script):
+ # directory has own workspace_status_command, so we use stamps from that
+ for line in run(["python", workspace_status_script]).split('\n'):
+ if re.search("^STABLE_[a-zA-Z0-9().:@/_ -]*$", line):
+ print(line)
+ else:
+ # directory lacks own workspace_status_command, so we create a default
+ # stamp
+ v = revision(template)
+ print('STABLE_BUILD_%s_LABEL %s' % (name.upper(),
+ v if v else 'unknown'))
+
+
+# os.chdir is different from plain `cd` in shells in that it follows symlinks
+# and does not update the PWD environment. So when using os.chdir to change into
+# a symlinked directory from gerrit's `plugins` or `modules` directory, we
+# cannot recover gerrit's directory. This prevents the plugins'/modules'
+# `workspace_status_release.py` scripts to detect the name they were symlinked
+# as (E.g.: it-* plugins sometimes get linked in more than once under different
+# names) and to detect gerrit's root directory. To work around this problem, we
+# mimic the `cd` of ordinary shells. By using this function, symlink information
+# is preserved in the `PWD` environment variable (as it is for example also done
+# in bash) and plugin/module `workspace_status_release.py` scripts can pick up
+# the needed information from there.
+def cd(absolute_path):
+ os.environ['PWD'] = absolute_path
+ os.chdir(absolute_path)
+
+
+def print_stamps():
+ cd(ROOT)
+ gerrit_version = revision()
+ print("STABLE_BUILD_GERRIT_LABEL %s" % gerrit_version)
+ for kind in ['modules', 'plugins']:
+ kind_dir = os.path.join(ROOT, kind)
+ for d in os.listdir(kind_dir) if os.path.isdir(kind_dir) else []:
+ p = os.path.join(kind_dir, d)
+ if os.path.isdir(p):
+ cd(p)
+ name = os.path.basename(p)
+ print_stamps_for_cwd(name, gerrit_version)
+
+
+if __name__ == '__main__':
+ print_stamps()
diff --git a/version.bzl b/version.bzl
index 73fc14edc4..67dc8e56cc 100644
--- a/version.bzl
+++ b/version.bzl
@@ -2,4 +2,4 @@
# Used by :api_install and :api_deploy targets
# when talking to the destination repository.
#
-GERRIT_VERSION = "3.0.14-SNAPSHOT"
+GERRIT_VERSION = "3.1.9-SNAPSHOT"